diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c80b17fed0..495573148b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -314,11 +314,21 @@ jobs: -Source "${{ env.NUGET_FEED_URL }}" - name: script + id: buildstep env: VCPKG_BINARY_SOURCES: clear;nuget,${{ env.NUGET_FEED_URL }},readwrite run: | ./scripts/build_linux.sh + # Currently this runs if build_linux fails for any reason whatsoever (because detecting + # that the CTS tests have failed requires some changes to build_linux) +# - name: Upload generated CTS output files if CTS tests fail +# if: failure() && matrix.options.tools_cts == 'ON' && steps.buildstep.outcome == 'failure' +# uses: actions/upload-artifact@v7 +# with: +# name: cts_failure_artifacts_${{ github.sha }} +# path: tests/cts/clitests/output + - name: Sanitize release notes if: matrix.options.package == 'YES' && github.event_name == 'push' && github.ref_type == 'tag' run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index dc30bc4111..835292efbe 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -272,6 +272,7 @@ jobs: scripts/smudge_date.sh - name: script + id: buildstep env: VCPKG_BINARY_SOURCES: clear;nuget,${{ env.NUGET_FEED_URL }},readwrite run: | @@ -284,6 +285,15 @@ jobs: fi set +e + # Currently this runs if build_macos fails for any reason whatsoever (because detecting + # that the CTS tests have failed requires some changes to build_macos) +# - name: Upload generated CTS output files if CTS tests fail +# if: failure() && matrix.options.tools_cts == 'ON' && steps.buildstep.outcome == 'failure' +# uses: actions/upload-artifact@v7 +# with: +# name: cts_failure_artifacts_${{ github.sha }} +# path: tests/cts/clitests/output + # Re. packaging, note that the build script only creates packages for # the Release configuration. If this is changed work will be needed # here to prevent failures from uploading same-named artifacts from both diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index d54bf55ef4..c552ad8dab 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -84,7 +84,15 @@ jobs: - name: Build Mingw x64 Release run: cmake --build build --config Release -j $(nproc) - name: Test Mingw build + id: teststep run: ctest --output-on-failure --test-dir build -C Release -j $(nproc) +# - name: Upload generated CTS output files if CTS tests fail +# if: failure() && matrix.options.tools_cts == 'ON' && steps.teststep.outcome == 'failure' +# uses: actions/upload-artifact@v7 +# with: +# name: cts_failure_artifacts_${{ github.sha }} +# path: tests/cts/clitests/output + # - name: Upload test log # shell: pwsh # if: ${{ failure() }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index a813415bd2..c6586ba23b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -331,12 +331,20 @@ jobs: run: scripts/check_mkvk.ps1 - name: Test Windows build + id: teststep # Tests built for arm64 can't be run as the CI runners are all x64. if: matrix.options.tests == 'ON' run: | $njobs = [Environment]::ProcessorCount ctest --output-on-failure --test-dir $env:BUILD_DIR -C Release -j $njobs +# - name: Upload generated CTS output files if CTS tests fail +# if: failure() && matrix.options.tools_cts == 'ON' && steps.teststep.outcome == 'failure' +# uses: actions/upload-artifact@v7 +# with: +# name: cts_failure_artifacts_${{ github.sha }} +# path: tests/cts/clitests/output + # - name: Upload test log # if: ${{ failure() }} # run: scripts/on_failure.ps1 diff --git a/.gitignore b/.gitignore index 4ca96605bb..431373c031 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,6 @@ the_khronos_group_inc.p12 # Place for random developer test files. /testground/ + +# clangd compile commands (used by LSPs) +compile_commands.json diff --git a/.gitmodules b/.gitmodules index dd8e8e8c9e..58daa368ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,5 +4,5 @@ [submodule "tests/cts"] path = tests/cts - url = https://github.com/KhronosGroup/KTX-Software-CTS.git + url = https://github.com/walcht/KTX-Software-CTS.git branch = main diff --git a/REUSE.toml b/REUSE.toml index 50710a058d..728dc5dfda 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -17,6 +17,12 @@ precedence = "aggregate" SPDX-FileCopyrightText = "Copyright (c) 2004, Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd." SPDX-License-Identifier = "BSD-3-Clause" +[[annotations]] +path = "tests/resources/input/exr/rgb16_sfloat_bc6hu.exr" +precedence = "aggregate" +SPDX-FileCopyrightText = "2026 The Khronos Group Inc." +SPDX-License-Identifier = "Apache-2.0" + [[annotations]] path = "tests/resources/ktx2/Desk_uastc_hdr4x4_zstd_15.ktx2" precedence = "aggregate" diff --git a/external/bc7enc_rdo/.gitrepo b/external/bc7enc_rdo/.gitrepo new file mode 100644 index 0000000000..6ee8304228 --- /dev/null +++ b/external/bc7enc_rdo/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/walcht/bc7enc_rdo.git + branch = restructure-for-bcn-encoder-decoder + commit = 2268ddf4f1ee63496d28c614794fa97e076f0f3c + parent = d9b6e638a233c10e68c70cfe3515c785bdbff3f4 + method = merge + cmdver = 0.4.9 diff --git a/external/bc7enc_rdo/CMakeLists.txt b/external/bc7enc_rdo/CMakeLists.txt new file mode 100644 index 0000000000..bfb79e7323 --- /dev/null +++ b/external/bc7enc_rdo/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2019-2020 The Khronos Group Inc. +# SPDX-License-Identifier: Apache-2.0 + +add_library(bc7enc_rdo) +target_compile_features(bc7enc_rdo PUBLIC cxx_std_17) + +# original bc7enc_rdo disables strict aliasing (see https://github.com/richgel999/bc7enc_rdo/blob/master/CMakeLists.txt) +# basisu also emphasises on compiling with '-fno-strict-aliasing' +if (NOT MSVC) + target_compile_options(bc7enc_rdo PRIVATE -fno-strict-aliasing) +endif() + +target_sources(bc7enc_rdo + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/rgbcx.cpp # BC1, BC3, BC4 and BC5 encoders/decoders + ${CMAKE_CURRENT_LIST_DIR}/ert.cpp # RDO post processing step and some utils +) + +target_include_directories(bc7enc_rdo + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/ +) diff --git a/external/bc7enc_rdo/LICENSES/MIT.txt b/external/bc7enc_rdo/LICENSES/MIT.txt new file mode 100644 index 0000000000..204b93da48 --- /dev/null +++ b/external/bc7enc_rdo/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +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 (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/external/bc7enc_rdo/README.md b/external/bc7enc_rdo/README.md new file mode 100644 index 0000000000..585b4f3f1f --- /dev/null +++ b/external/bc7enc_rdo/README.md @@ -0,0 +1,88 @@ +# About + +This branch of bc7enc_rdo forked from [bc7enc_rdo][bc7enc_rdo] contains two +components used by KTX-Software with some changes: + + - rgbcx: BC1, BC3, BC4, and BC5 encoders/decoders (+ added BC2 decoder). + - ert: rate distortion optimization (RDO) (agnostic to encoders/decoders). + +Changes: + + - added [[maybe_unused]] to silence compiler warnings about unused variables + +Essentially: only what is absolutely necessary and not already provided by Basis +Universal is left. The following components have been removed: + + - bc7 encoder/decoder. See [Notes][#Notes] below. + - CLI components. + - utils.h/cpp because it relies on some heavy classes. + +Currently, the remaining two components are compiled together under a single +STATIC library bc7enc_rdo (if the need arises where only the RDO component is needed, +this will be separated into multiple libraries). + +## Building + +This is intended to be built as a static library dependency (see minimal +CMakeLists.txt). + +Somewhere in your project's CMakeLists: + +```bash +# Make sure bc7enc_rdo is built as a STATIC library +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(path-to-bc7enc_rdo) +set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_RESET}) + +# Then append it to your target's link dependencies +target_link_libraries(your_target PRIVATE bc7enc_rdo) +``` + +## Notes + +BC2 encoder is currently not implemented because practically it is very rarely +used (if at all) and can almost always be replaced with the more capable BC3 +format (which offers same color RGB block encoding as BC1 but with added alpha +support that is usually better than the one implemented by BC2). + +BC6HU/BC6HS HDR formats are already originally not supported by this repo. +Basis Universal provides bc6hu (emphasis on the **u**) encoder and bc6hu/bc6hs decoders. + +BC7 encoder/decoder is not included because Basis Universal already provides a +much more capable encoder (bc7f). + +Notes about choice with BC1 vs. BC3 vs. BC7 (from Basisu wiki): + +> bc7f encodes so rapidly (on average), that apart from lower VRAM consumption +> (4bpp vs. 8bpp) and better GPU texture cache efficiency, there's little need +> to use BC1 now. BC3 still has an advantage vs. BC7, because it very strongly +> separates how RGB is encoded from the alpha channel, in a predictable way. + +## Compilation Notes + +**Makes sure to compile with `-fno-strict-aliasing`** since the original +bc7enc_rdo repo was mainly developed and tested on MSVC (which doesn't do +excessive optimizations including strict aliasing). + +There are pointer conversions all over the place and if strict aliasing is +enabled, these conversions are most likely UB (see strict aliasing rule or type +punning). + +This can be re-written a way that complies with strict aliasing but a lot of +`memcpy`s have to be added which will result in the code being significantly +less readable (also questionable if there is any benefit to this whatsoever). + +## TODOs + +There are some features that I'd like to add (from most important to least): + +1. [] HDR RDO (this is possible but need to be extremely careful when changing +ert.cpp/h code to handle uint16_t instead of chars/uint8_t) +1. [] BC2 encoder (even though it is seldom used) +1. [] BC6HS encoder + +## License + +MIT License Rich Geldreich. + +[bc7enc_rdo]: https://github.com/richgel999/bc7enc_rdo.git) diff --git a/external/bc7enc_rdo/REUSE.toml b/external/bc7enc_rdo/REUSE.toml new file mode 100644 index 0000000000..c38894d12f --- /dev/null +++ b/external/bc7enc_rdo/REUSE.toml @@ -0,0 +1,9 @@ +version = 1 +SPDX-PackageName = "bc7enc_rdo" +SPDX-PackageDownloadLocation = "https://github.com/richgel999/bc7enc_rdo" + +[[annotations]] +path = "**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2020-2021 Richard Geldreich, Jr. " +SPDX-License-Identifier = "MIT" diff --git a/external/bc7enc_rdo/ert.cpp b/external/bc7enc_rdo/ert.cpp new file mode 100644 index 0000000000..552cea1062 --- /dev/null +++ b/external/bc7enc_rdo/ert.cpp @@ -0,0 +1,643 @@ +#include "ert.h" +#include +#include +#include +#include +#include + +#define ERT_FAVOR_CONT_AND_REP0_MATCHES (1) +#define ERT_FAVOR_REP0_MATCHES (0) + +namespace ert +{ + const uint32_t MAX_BLOCK_PIXELS = 12 * 12; + const uint32_t MAX_BLOCK_SIZE_IN_BYTES = 256; + const uint32_t MIN_MATCH_LEN = 3; + const float LITERAL_BITS = 13.0f; + const float MATCH_CONTINUE_BITS = 1.0f; + const float MATCH_REP0_BITS = 4.0f; + + static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; } + template inline F lerp(F a, F b, F s) { return a + (b - a) * s; } + + static const uint8_t g_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + + static const uint8_t g_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + + static inline uint32_t compute_match_cost_estimate(uint32_t dist, uint32_t match_len_in_bytes) + { + assert(match_len_in_bytes <= 258); + + uint32_t len_cost = 6; + if (match_len_in_bytes >= 12) + len_cost = 9; + else if (match_len_in_bytes >= 8) + len_cost = 8; + else if (match_len_in_bytes >= 6) + len_cost = 7; + + uint32_t dist_cost = 5; + if (dist < 512) + dist_cost += g_tdefl_small_dist_extra[dist & 511]; + else + { + dist_cost += g_tdefl_large_dist_extra[std::min(dist, 32767) >> 8]; + while (dist >= 32768) + { + dist_cost++; + dist >>= 1; + } + } + return len_cost + dist_cost; + } + + static inline float compute_block_mse(const color_rgba* pPixelsA, const color_rgba* pPixelsB, uint32_t block_width, uint32_t block_height, uint32_t total_block_pixels, uint32_t num_comps, const uint32_t weights[4], float one_over_total_color_weight) + { + uint64_t total_err = 0; + + if ((block_width == 4) && (block_height == 4) && (num_comps == 4)) + { + if ((weights[0] == 1) && (weights[1] == 1) && (weights[2] == 1) && (weights[3] == 1)) + { + for (uint32_t i = 0; i < 16; i++) + { + const color_rgba* pA = pPixelsA + i; + const color_rgba* pB = pPixelsB + i; + + const int dr = pA->m_comps[0] - pB->m_comps[0]; + const int dg = pA->m_comps[1] - pB->m_comps[1]; + const int db = pA->m_comps[2] - pB->m_comps[2]; + const int da = pA->m_comps[3] - pB->m_comps[3]; + + total_err += dr * dr + dg * dg + db * db + da * da; + } + } + else + { + for (uint32_t i = 0; i < 16; i++) + { + const color_rgba* pA = pPixelsA + i; + const color_rgba* pB = pPixelsB + i; + + const int dr = pA->m_comps[0] - pB->m_comps[0]; + const int dg = pA->m_comps[1] - pB->m_comps[1]; + const int db = pA->m_comps[2] - pB->m_comps[2]; + const int da = pA->m_comps[3] - pB->m_comps[3]; + + total_err += weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db + weights[3] * da * da; + } + } + } + else if ((block_width == 4) && (block_height == 4) && (num_comps == 3)) + { + for (uint32_t y = 0; y < 4; y++) + { + const uint32_t y_ofs = y * 4; + for (uint32_t x = 0; x < 4; x++) + { + const color_rgba* pA = pPixelsA + x + y_ofs; + const color_rgba* pB = pPixelsB + x + y_ofs; + + const int dr = pA->m_comps[0] - pB->m_comps[0]; + const int dg = pA->m_comps[1] - pB->m_comps[1]; + const int db = pA->m_comps[2] - pB->m_comps[2]; + + total_err += weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db; + } + } + } + else if ((block_width == 4) && (block_height == 4) && (num_comps == 2)) + { + for (uint32_t y = 0; y < 4; y++) + { + const uint32_t y_ofs = y * 4; + for (uint32_t x = 0; x < 4; x++) + { + const color_rgba* pA = pPixelsA + x + y_ofs; + const color_rgba* pB = pPixelsB + x + y_ofs; + + const int dr = pA->m_comps[0] - pB->m_comps[0]; + const int dg = pA->m_comps[1] - pB->m_comps[1]; + + total_err += weights[0] * dr * dr + weights[1] * dg * dg; + } + } + } + else if ((block_width == 4) && (block_height == 4) && (num_comps == 1)) + { + for (uint32_t y = 0; y < 4; y++) + { + const uint32_t y_ofs = y * 4; + for (uint32_t x = 0; x < 4; x++) + { + const color_rgba* pA = pPixelsA + x + y_ofs; + const color_rgba* pB = pPixelsB + x + y_ofs; + + const int dr = pA->m_comps[0] - pB->m_comps[0]; + + total_err += weights[0] * dr * dr; + } + } + } + else + { + for (uint32_t y = 0; y < block_height; y++) + { + const uint32_t y_ofs = y * block_width; + for (uint32_t x = 0; x < block_width; x++) + { + const color_rgba* pA = pPixelsA + x + y_ofs; + const color_rgba* pB = pPixelsB + x + y_ofs; + + for (uint32_t c = 0; c < num_comps; c++) + { + const int d = pA->m_comps[c] - pB->m_comps[c]; + total_err += weights[c] * d * d; + } + } + } + } + + return total_err * (one_over_total_color_weight / total_block_pixels); + } + + uint32_t hash_hsieh(const uint8_t* pBuf, size_t len, uint32_t salt) + { + if (!pBuf || !len) + return 0; + + uint32_t h = static_cast(len + (salt << 16)); + + const uint32_t bytes_left = len & 3; + len >>= 2; + + while (len--) + { + const uint16_t* pWords = reinterpret_cast(pBuf); + + h += pWords[0]; + + const uint32_t t = (pWords[1] << 11) ^ h; + h = (h << 16) ^ t; + + pBuf += sizeof(uint32_t); + + h += h >> 11; + } + + switch (bytes_left) + { + case 1: + h += *reinterpret_cast(pBuf); + h ^= h << 10; + h += h >> 1; + break; + case 2: + h += *reinterpret_cast(pBuf); + h ^= h << 11; + h += h >> 17; + break; + case 3: + h += *reinterpret_cast(pBuf); + h ^= h << 16; + h ^= (static_cast(pBuf[sizeof(uint16_t)])) << 18; + h += h >> 11; + break; + default: + break; + } + + h ^= h << 3; + h += h >> 5; + h ^= h << 4; + h += h >> 17; + h ^= h << 25; + h += h >> 6; + + return h; + } + + // BC7 entropy reduction transform with Deflate/LZMA/LZHAM optimizations + bool reduce_entropy(void* pBlocks, uint32_t num_blocks, + uint32_t total_block_stride_in_bytes, uint32_t block_size_to_optimize_in_bytes, uint32_t block_width, uint32_t block_height, uint32_t num_comps, + const color_rgba* pBlock_pixels, const reduce_entropy_params& params, uint32_t& total_modified, + pUnpack_block_func pUnpack_block_func, void* pUnpack_block_func_user_data, ert::reduce_entropy_stats& stats, + const float* pBlock_mse_scales) + { + assert(total_block_stride_in_bytes && block_size_to_optimize_in_bytes); + assert(total_block_stride_in_bytes >= block_size_to_optimize_in_bytes); + + assert(num_comps >= 1 && num_comps <= 4); + for (uint32_t i = num_comps; i < 4; i++) + { + assert(!params.m_color_weights[i]); + if (params.m_color_weights[i]) + return false; + } + + const uint32_t total_color_weight = params.m_color_weights[0] + params.m_color_weights[1] + params.m_color_weights[2] + params.m_color_weights[3]; + assert(total_color_weight); + const float one_over_total_color_weight = 1.0f / total_color_weight; + + assert((block_size_to_optimize_in_bytes >= MIN_MATCH_LEN) && (block_size_to_optimize_in_bytes <= MAX_BLOCK_SIZE_IN_BYTES)); + if ((block_size_to_optimize_in_bytes < MIN_MATCH_LEN) || (block_size_to_optimize_in_bytes > MAX_BLOCK_SIZE_IN_BYTES)) + return false; + + uint8_t* pBlock_bytes = (uint8_t*)pBlocks; + + const uint32_t total_block_pixels = block_width * block_height; + if (total_block_pixels > MAX_BLOCK_PIXELS) + return false; + + const int total_blocks_to_check = std::max(1U, params.m_lookback_window_size / total_block_stride_in_bytes); + + std::vector len_hist(MAX_BLOCK_SIZE_IN_BYTES + 1); + std::vector second_len_hist(MAX_BLOCK_SIZE_IN_BYTES + 1); + + stats.total_second_matches = 0; + stats.total_smooth_blocks = 0; + + int prev_match_window_ofs_to_favor_cont = -1, prev_match_dist_to_favor = -1; + + + const uint32_t HASH_SIZE = 8192; + uint32_t hash[HASH_SIZE]; + + for (uint32_t block_index = 0; block_index < num_blocks; block_index++) + { + if ((block_index & 0xFF) == 0) + memset(hash, 0, sizeof(hash)); + + uint8_t* pOrig_block = &pBlock_bytes[block_index * total_block_stride_in_bytes]; + const color_rgba* pPixels = &pBlock_pixels[block_index * total_block_pixels]; + + color_rgba decoded_block[MAX_BLOCK_PIXELS]; + if (!(*pUnpack_block_func)(pOrig_block, decoded_block, block_index, pUnpack_block_func_user_data)) + return false; + + float cur_mse = compute_block_mse(pPixels, decoded_block, block_width, block_height, total_block_pixels, num_comps, params.m_color_weights, one_over_total_color_weight); + + if ((params.m_skip_zero_mse_blocks) && (cur_mse == 0.0f)) + continue; + + const float max_std_dev = compute_block_max_std_dev(pPixels, block_width, block_height, num_comps); + + float yl = clampf(max_std_dev / params.m_max_smooth_block_std_dev, 0.0f, 1.0f); + yl = yl * yl; + float smooth_block_mse_scale = lerp(params.m_smooth_block_max_mse_scale, 1.0f, yl); + + if (pBlock_mse_scales) + { + if (pBlock_mse_scales[block_index] > 0.0f) + { + smooth_block_mse_scale = pBlock_mse_scales[block_index]; + } + } + + if (smooth_block_mse_scale > 1.0f) + stats.total_smooth_blocks++; + + float cur_bits = (LITERAL_BITS * block_size_to_optimize_in_bytes); + float cur_t = cur_mse * smooth_block_mse_scale + cur_bits * params.m_lambda; + + int first_block_to_check = std::max(0, block_index - total_blocks_to_check); + int last_block_to_check = block_index - 1; + + uint8_t best_block[MAX_BLOCK_SIZE_IN_BYTES]; + memcpy(best_block, pOrig_block, block_size_to_optimize_in_bytes); + + float best_t = cur_t; + uint32_t best_match_len = 0, best_match_src_window_ofs = 0, best_match_dst_window_ofs = 0, best_match_dst_block_ofs = 0; + [[maybe_unused]] uint32_t best_match_src_block_ofs = 0; + float best_match_bits = 0; + + // Don't let thresh_ms_err be 0 to let zero error blocks have slightly increased distortion + const float thresh_ms_err = params.m_max_allowed_rms_increase_ratio * params.m_max_allowed_rms_increase_ratio * std::max(cur_mse, 1.0f); + + for (int prev_block_index = last_block_to_check; prev_block_index >= first_block_to_check; --prev_block_index) + { + const uint8_t* pPrev_blk = &pBlock_bytes[prev_block_index * total_block_stride_in_bytes]; + + for (uint32_t len = block_size_to_optimize_in_bytes; len >= MIN_MATCH_LEN; len--) + { + const uint32_t match_dist = (block_index - prev_block_index) * total_block_stride_in_bytes; + + // Assume the block has 1 match and block_size_to_optimize_in_bytes-match_len literals. + const float trial_match_bits = (float)compute_match_cost_estimate(match_dist, len); + const float trial_total_bits = (block_size_to_optimize_in_bytes - len) * LITERAL_BITS + trial_match_bits; + const float trial_total_bits_times_lambda = trial_total_bits * params.m_lambda; + + for (uint32_t ofs = 0; ofs <= (block_size_to_optimize_in_bytes - len); ofs++) + { + assert(len + ofs <= block_size_to_optimize_in_bytes); + + const uint32_t dst_match_window_ofs = block_index * total_block_stride_in_bytes + ofs; + const uint32_t src_match_window_ofs = prev_block_index * total_block_stride_in_bytes + ofs; + + float trial_match_bits_to_use = trial_match_bits; + float trial_total_bits_times_lambda_to_use = trial_total_bits_times_lambda; + + uint32_t hs = hash_hsieh(pPrev_blk + ofs, len, ofs); + +#if ERT_FAVOR_CONT_AND_REP0_MATCHES + // Continue a previous match (which would cross block boundaries) + if (((int)src_match_window_ofs == prev_match_window_ofs_to_favor_cont) && (ofs == 0)) + { + float continue_match_trial_bits = (block_size_to_optimize_in_bytes - len) * LITERAL_BITS + MATCH_CONTINUE_BITS; + trial_match_bits_to_use = MATCH_CONTINUE_BITS; + trial_total_bits_times_lambda_to_use = continue_match_trial_bits * params.m_lambda; + } + // Exploit REP0 matches + else if ((prev_match_dist_to_favor != -1) && (src_match_window_ofs == (dst_match_window_ofs - prev_match_dist_to_favor))) + { + float continue_match_trial_bits = (block_size_to_optimize_in_bytes - len) * LITERAL_BITS + MATCH_REP0_BITS; + trial_match_bits_to_use = MATCH_REP0_BITS; + trial_total_bits_times_lambda_to_use = continue_match_trial_bits * params.m_lambda; + } + else + { + uint32_t hash_check = hash[hs & (HASH_SIZE - 1)]; + if ((hash_check & 0xFF) == (block_index & 0xFF)) + { + if ((hash_check >> 8) == (hs >> 8)) + continue; + } + } +#else + uint32_t hash_check = hash[hs & (HASH_SIZE - 1)]; + if ((hash_check & 0xFF) == (block_index & 0xFF)) + { + if ((hash_check >> 8) == (hs >> 8)) + continue; + } +#endif + + hash[hs & (HASH_SIZE - 1)] = (hs & 0xFFFFFF00) | (block_index & 0xFF); + + uint8_t trial_block[MAX_BLOCK_SIZE_IN_BYTES]; + memcpy(trial_block, pOrig_block, block_size_to_optimize_in_bytes); + memcpy(trial_block + ofs, pPrev_blk + ofs, len); + + color_rgba decoded_trial_block[MAX_BLOCK_PIXELS]; + if (!(*pUnpack_block_func)(trial_block, decoded_trial_block, block_index, pUnpack_block_func_user_data)) + continue; + + float trial_mse = compute_block_mse(pPixels, decoded_trial_block, block_width, block_height, total_block_pixels, num_comps, params.m_color_weights, one_over_total_color_weight); + + if (trial_mse < thresh_ms_err) + { + float t = trial_mse * smooth_block_mse_scale + trial_total_bits_times_lambda_to_use; + + if (t < best_t) + { + best_t = t; + memcpy(best_block, trial_block, block_size_to_optimize_in_bytes); + best_match_len = len; + best_match_src_window_ofs = src_match_window_ofs; + best_match_dst_window_ofs = dst_match_window_ofs; + best_match_src_block_ofs = ofs; + best_match_dst_block_ofs = ofs; + best_match_bits = trial_match_bits_to_use; + } + } + } // ofs + } // len + } // prev_block_index + + if (best_t < cur_t) + { + uint32_t best_second_match_len = 0, best_second_match_src_window_ofs = 0, best_second_match_dst_window_ofs = 0, best_second_match_dst_block_ofs = 0; + [[maybe_unused]] uint32_t best_second_match_src_block_ofs = 0; + + // Try injecting a second match, being sure it does't overlap with the first. + if ((params.m_try_two_matches) && (best_match_len <= (block_size_to_optimize_in_bytes - 3))) + { + uint8_t matched_flags[MAX_BLOCK_SIZE_IN_BYTES]; + memset(matched_flags, 0, sizeof(matched_flags)); + memset(matched_flags + best_match_dst_block_ofs, 1, best_match_len); + + uint8_t orig_best_block[MAX_BLOCK_SIZE_IN_BYTES]; + memcpy(orig_best_block, best_block, block_size_to_optimize_in_bytes); + + for (int prev_block_index = last_block_to_check; prev_block_index >= first_block_to_check; --prev_block_index) + { + const uint8_t* pPrev_blk = &pBlock_bytes[prev_block_index * total_block_stride_in_bytes]; + + const uint32_t match_dist = (block_index - prev_block_index) * total_block_stride_in_bytes; + + for (uint32_t len = 3; len <= (block_size_to_optimize_in_bytes - best_match_len); len++) + { + const float trial_total_bits = (block_size_to_optimize_in_bytes - len - best_match_len) * LITERAL_BITS + compute_match_cost_estimate(match_dist, len) + best_match_bits; + + const float trial_total_bits_times_lambda = trial_total_bits * params.m_lambda; + + for (uint32_t ofs = 0; ofs <= (block_size_to_optimize_in_bytes - len); ofs++) + { + int i; + for (i = 0; i < (int)len; i++) + if (matched_flags[ofs + i]) + break; + if (i != (int)len) + continue; + + assert(len + ofs <= block_size_to_optimize_in_bytes); + + const uint32_t dst_match_window_ofs = block_index * total_block_stride_in_bytes + ofs; + const uint32_t src_match_window_ofs = prev_block_index * total_block_stride_in_bytes + ofs; + + uint8_t trial_block[MAX_BLOCK_SIZE_IN_BYTES]; + memcpy(trial_block, orig_best_block, block_size_to_optimize_in_bytes); + memcpy(trial_block + ofs, pPrev_blk + ofs, len); + + color_rgba decoded_trial_block[MAX_BLOCK_PIXELS]; + if (!(*pUnpack_block_func)(trial_block, decoded_trial_block, block_index, pUnpack_block_func_user_data)) + continue; + + float trial_mse = compute_block_mse(pPixels, decoded_trial_block, block_width, block_height, total_block_pixels, num_comps, params.m_color_weights, one_over_total_color_weight); + + if (trial_mse < thresh_ms_err) + { + float t = trial_mse * smooth_block_mse_scale + trial_total_bits_times_lambda; + + if (t < best_t) + { + best_t = t; + memcpy(best_block, trial_block, block_size_to_optimize_in_bytes); + best_second_match_len = len; + best_second_match_src_window_ofs = src_match_window_ofs; + best_second_match_dst_window_ofs = dst_match_window_ofs; + best_second_match_src_block_ofs = ofs; + best_second_match_dst_block_ofs = ofs; + } + } + } + } + } + } + + memcpy(pOrig_block, best_block, block_size_to_optimize_in_bytes); + total_modified++; + + if ((best_second_match_len == 0) || (best_match_dst_window_ofs > best_second_match_dst_window_ofs)) + { + int best_match_dist = best_match_dst_window_ofs - best_match_src_window_ofs; + assert(best_match_dist >= 1); + (void)best_match_dist; + + if (block_size_to_optimize_in_bytes == total_block_stride_in_bytes) + { + // If the match goes all the way to the end of a block, we can try to continue it on the next encoded block. + if ((best_match_dst_block_ofs + best_match_len) == total_block_stride_in_bytes) + prev_match_window_ofs_to_favor_cont = best_match_src_window_ofs + best_match_len; + else + prev_match_window_ofs_to_favor_cont = -1; + } + +#if ERT_FAVOR_REP0_MATCHES + // Compute the window offset where a cheaper REP0 match would be available + prev_match_dist_to_favor = best_match_dist; +#endif + } + else + { + int best_match_dist = best_second_match_dst_window_ofs - best_second_match_src_window_ofs; + assert(best_match_dist >= 1); + (void)best_match_dist; + + if (block_size_to_optimize_in_bytes == total_block_stride_in_bytes) + { + // If the match goes all the way to the end of a block, we can try to continue it on the next encoded block. + if ((best_second_match_dst_block_ofs + best_second_match_len) == total_block_stride_in_bytes) + prev_match_window_ofs_to_favor_cont = best_second_match_src_window_ofs + best_second_match_len; + else + prev_match_window_ofs_to_favor_cont = -1; + } + +#if ERT_FAVOR_REP0_MATCHES + // Compute the window offset where a cheaper REP0 match would be available + prev_match_dist_to_favor = best_match_dist; +#endif + } + + len_hist[best_match_len]++; + + if (best_second_match_len) + { + second_len_hist[best_second_match_len]++; + stats.total_second_matches++; + } + } + else + { + prev_match_window_ofs_to_favor_cont = -1; + } + + } // block_index + + return true; + } + + + inline bool flood_fill_is_inside(std::vector& image, int x, int y, int w, int h, const color_rgba& b) + { + if (x < 0 || x >= w || y < 0 || y >= h) + return false; + return image[x + y * w] == b; + } + + struct fill_segment + { + int16_t m_y, m_xl, m_xr, m_dy; + + fill_segment(int y, int xl, int xr, int dy) : + m_y((int16_t)y), m_xl((int16_t)xl), m_xr((int16_t)xr), m_dy((int16_t)dy) + { + } + }; + + // See http://www.realtimerendering.com/resources/GraphicsGems/gems/SeedFill.c + uint32_t flood_fill(std::vector& image, int x, int y, + uint32_t width, uint32_t height, const color_rgba& c, + const color_rgba& b, std::vector* pSet_pixels) + { + uint32_t total_set = 0; + + if (!flood_fill_is_inside(image, x, y, width, height, b)) + return 0; + + std::vector stack; + stack.reserve(64); + + #define FLOOD_PUSH(y, xl, xr, dy) if (((y + (dy)) >= 0) && ((y + (dy)) < (int)height)) { stack.push_back(fill_segment(y, xl, xr, dy)); } + + FLOOD_PUSH(y, x, x, 1); + FLOOD_PUSH(y + 1, x, x, -1); + + while (stack.size()) + { + fill_segment s = stack.back(); + stack.pop_back(); + + int x1 = s.m_xl, x2 = s.m_xr, dy = s.m_dy; + y = s.m_y + s.m_dy; + + for (x = x1; (x >= 0) && flood_fill_is_inside(image, x, y, width, height, b); x--) + { + image[x + y * width] = c; + total_set++; + if (pSet_pixels) + pSet_pixels->push_back(pixel_coord(x, y)); + } + + int l; + + if (x >= x1) + goto skip; + + l = x + 1; + if (l < x1) + FLOOD_PUSH(y, l, x1 - 1, -dy); + + x = x1 + 1; + + do + { + for (; x <= ((int)width - 1) && flood_fill_is_inside(image, x, y, width, height, b); x++) + { + image[x + y * width] = c; + total_set++; + if (pSet_pixels) + pSet_pixels->push_back(pixel_coord(x, y)); + } + FLOOD_PUSH(y, l, x - 1, dy); + + if (x > (x2 + 1)) + FLOOD_PUSH(y, x2 + 1, x - 1, -dy); + + skip: + for (x++; x <= x2 && !flood_fill_is_inside(image, x, y, width, height, b); x++) + ; + + l = x; + } while (x <= x2); + } + + return total_set; + } + +} // namespace ert diff --git a/external/bc7enc_rdo/ert.h b/external/bc7enc_rdo/ert.h new file mode 100644 index 0000000000..a21c0b9cf8 --- /dev/null +++ b/external/bc7enc_rdo/ert.h @@ -0,0 +1,247 @@ +#pragma once + +#ifdef _MSC_VER +#pragma warning (disable:4201) // nameless struct/union +#endif + +#include +#include +#include +#include +#include + +namespace ert +{ + template inline S clamp(S value, S low, S high) { + return (value < low) ? low : ((value > high) ? high : value); + } + + // Copied from Basis Universal's basist::color_rgba. Even though this is the + // same as basist::color_rgba (minus some functions), using them + // interchangeably is probably undefined behavior (UB). + // + // Q. Why not use basist::color_rgba directly? This would require adding + // Basis Universal as an external dependency to an otherwise + // zero-dependency project. In case that is acceptable, color32 in rgbcx + // can also be replaced (to some degree) by basist::color_rgba. + struct color_rgba + { + union + { + uint8_t m_comps[4]; + + struct + { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + }; + }; + + inline color_rgba() + { + static_assert(sizeof(*this) == 4, "sizeof(*this) != 4"); + } + + + inline color_rgba(int y) + { + set(y); + } + + inline color_rgba(int y, int na) + { + set(y, na); + } + + inline color_rgba(int sr, int sg, int sb, int sa) + { + set(sr, sg, sb, sa); + } + + inline color_rgba &set(int y) + { + m_comps[0] = static_cast(clamp(y, 0, 255)); + m_comps[1] = m_comps[0]; + m_comps[2] = m_comps[0]; + m_comps[3] = 255; + return *this; + } + + inline color_rgba &set(int y, int na) + { + m_comps[0] = static_cast(clamp(y, 0, 255)); + m_comps[1] = m_comps[0]; + m_comps[2] = m_comps[0]; + m_comps[3] = static_cast(clamp(na, 0, 255)); + return *this; + } + + inline color_rgba &set(int sr, int sg, int sb, int sa) + { + m_comps[0] = static_cast(clamp(sr, 0, 255)); + m_comps[1] = static_cast(clamp(sg, 0, 255)); + m_comps[2] = static_cast(clamp(sb, 0, 255)); + m_comps[3] = static_cast(clamp(sa, 0, 255)); + return *this; + } + + inline bool operator== (const color_rgba &rhs) const + { + if (m_comps[0] != rhs.m_comps[0]) return false; + if (m_comps[1] != rhs.m_comps[1]) return false; + if (m_comps[2] != rhs.m_comps[2]) return false; + if (m_comps[3] != rhs.m_comps[3]) return false; + return true; + } + + inline int get_601_luma() const { return (19595U * m_comps[0] + 38470U * m_comps[1] + 7471U * m_comps[2] + 32768U) >> 16U; } + inline int get_709_luma() const { return (13938U * m_comps[0] + 46869U * m_comps[1] + 4729U * m_comps[2] + 32768U) >> 16U; } + inline int get_luma(bool luma_601) const { return luma_601 ? get_601_luma() : get_709_luma(); } + }; + + + struct pixel_coord + { + uint16_t m_x, m_y; + pixel_coord() { } + pixel_coord(uint32_t x, uint32_t y) : m_x((uint16_t)x), m_y((uint16_t)y) { } + }; + + + // + // https://www.johndcook.com/blog/standard_deviation/ + // This class is for small numbers of integers, so precision shouldn't be an issue. + // This is also available at: basis_universal/example_transcoding/utils.h + // + class tracked_stat + { + public: + tracked_stat() { clear(); } + + void clear() { m_num = 0; m_total = 0; m_total2 = 0; } + + void update(uint32_t val) { m_num++; m_total += val; m_total2 += val * val; } + + tracked_stat& operator += (uint32_t val) { update(val); return *this; } + + uint32_t get_number_of_values() const { return m_num; } + uint64_t get_total() const { return m_total; } + uint64_t get_total2() const { return m_total2; } + + float get_mean() const { return m_num ? (float)m_total / m_num : 0.0f; }; + + float get_variance() const { return m_num ? ((float)(m_num * m_total2 - m_total * m_total)) / (m_num * m_num) : 0.0f; } + float get_std_dev() const { return m_num ? sqrtf((float)(m_num * m_total2 - m_total * m_total)) / m_num : 0.0f; } + + float get_sample_variance() const { return (m_num > 1) ? ((float)(m_num * m_total2 - m_total * m_total)) / (m_num * (m_num - 1)) : 0.0f; } + float get_sample_std_dev() const { return (m_num > 1) ? sqrtf(get_sample_variance()) : 0.0f; } + + private: + uint32_t m_num; + uint64_t m_total; + uint64_t m_total2; + }; + + + struct reduce_entropy_params + { + // m_lambda: The post-processor tries to reduce distortion*smooth_block_scale + rate*lambda (rate is approximate LZ bits and distortion is scaled MS error multiplied against the smooth block MSE weighting factor). + // Larger values push the postprocessor towards optimizing more for lower rate, and smaller values more for distortion. 0=minimal distortion. + float m_lambda; + + // m_lookback_window_size: The number of bytes the encoder can look back from each block to find matches. The larger this value, the slower the encoder but the higher the quality per LZ compressed bit. + uint32_t m_lookback_window_size; + + // m_max_allowed_rms_increase_ratio: How much the RMS error of a block is allowed to increase before a trial is rejected. 1.0=no increase allowed, 1.05=5% increase allowed, etc. + float m_max_allowed_rms_increase_ratio; + + float m_max_smooth_block_std_dev; + float m_smooth_block_max_mse_scale; + + uint32_t m_color_weights[4]; + + bool m_try_two_matches; + bool m_skip_zero_mse_blocks; + + reduce_entropy_params() { clear(); } + + void clear() + { + m_lookback_window_size = 128; + m_lambda = 0.5f; + m_max_allowed_rms_increase_ratio = 10.0f; + m_max_smooth_block_std_dev = 18.0f; + m_smooth_block_max_mse_scale = 10.0f; + m_color_weights[0] = 1; + m_color_weights[1] = 1; + m_color_weights[2] = 1; + m_color_weights[3] = 1; + m_try_two_matches = true; + m_skip_zero_mse_blocks = false; + } + }; + + struct reduce_entropy_stats { + uint32_t total_smooth_blocks; + uint32_t total_second_matches; + }; + + typedef bool (*pUnpack_block_func)(const void* pBlock, color_rgba* pPixels, uint32_t block_index, void* pUser_data); + + // BC7 entropy reduction transform with Deflate/LZMA/LZHAM optimizations + bool reduce_entropy(void* pBlocks, uint32_t num_blocks, + uint32_t total_block_stride_in_bytes, uint32_t block_size_to_optimize_in_bytes, uint32_t block_width, uint32_t block_height, uint32_t num_comps, + const color_rgba* pBlock_pixels, const reduce_entropy_params& params, uint32_t& total_modified, + pUnpack_block_func pUnpack_block_func, void* pUnpack_block_func_user_data, reduce_entropy_stats& stats, + const float* pBlock_mse_scales = nullptr); + + inline uint32_t get_clamped_index(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + return clamp(x, 0, width - 1) + clamp(y, 0, height- 1) * width; + } + + inline float compute_block_max_std_dev(const color_rgba* pPixels, + uint32_t block_width, uint32_t block_height, uint32_t num_comps) { + tracked_stat comp_stats[4]; + + for (uint32_t y = 0; y < block_height; y++) + { + for (uint32_t x = 0; x < block_width; x++) + { + const color_rgba* pPixel = pPixels + x + y * block_width; + + for (uint32_t c = 0; c < num_comps; c++) + comp_stats[c].update(pPixel->m_comps[c]); + } + } + + float max_std_dev = 0.0f; + for (uint32_t i = 0; i < num_comps; i++) + max_std_dev = std::max(max_std_dev, comp_stats[i].get_std_dev()); + return max_std_dev; + } + + // See http://www.realtimerendering.com/resources/GraphicsGems/gems/SeedFill.c + uint32_t flood_fill(std::vector& image, int x, int y, + uint32_t width, uint32_t height, const color_rgba& c, + const color_rgba& b, std::vector* pSet_pixels); + + /* + * Number of blocks to encode for each thread SHOULD be superior or equal to + * (rdo_window_size / block_size) otherwise multi-threaded RDO will not be + * deterministic and will depend on the set window size. + */ + inline uint32_t + adjust_num_threads_for_deterministic_rdo(uint32_t requested_num_threads, uint32_t rdo_window_size, + uint32_t block_size, uint32_t num_blocks_total) { + const auto min_num_blocks_per_thread = std::max(1U, rdo_window_size / block_size); + // num_blocks_total / x >= min_num_blocks + // => x <= num_blocks_total / min_num_blocks + // => x <= floor(num_blocks_total / min_num_blocks) + // and handle the edge case where min_num_blocks_per_thread > num_blocks_total + // => x = max(1, floor(num_blocks_total / min_num_blocks)) + return std::max(1U, std::min(requested_num_threads, num_blocks_total / min_num_blocks_per_thread)); + } + +} // namespace ert diff --git a/external/bc7enc_rdo/rgbcx.cpp b/external/bc7enc_rdo/rgbcx.cpp new file mode 100644 index 0000000000..2fc16a4e2c --- /dev/null +++ b/external/bc7enc_rdo/rgbcx.cpp @@ -0,0 +1,3100 @@ +// rgbcx.cpp - see license at end of rgbcx.h +#include "rgbcx.h" +#include +#include +#include + +namespace rgbcx +{ + [[maybe_unused]] const uint8_t g_bc1_to_linear[4] = { 0, 3, 1, 2 }; + + const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS4 = 969; + +#ifdef _MSC_VER +#pragma region +#endif + // All total orderings for 16 pixels 2-bit selectors. + // BC1 selector order 0, 2, 3, 1 (i.e. the selectors are reordered into linear order). + static uint8_t g_unique_total_orders4[NUM_UNIQUE_TOTAL_ORDERINGS4][4] = + { + {0,8,2,6},{4,3,9,0},{4,8,1,3},{12,0,3,1},{11,3,2,0},{6,4,6,0},{7,5,0,4},{6,0,8,2},{1,0,0,15},{3,0,8,5},{1,1,13,1},{13,1,2,0},{0,14,1,1},{0,15,1,0},{0,13,0,3},{16,0,0,0},{4,3,4,5},{8,6,0,2},{0,10,0,6},{10,0,4,2},{7,2,1,6},{4,7,5,0},{1,4,7,4},{0,14,2,0},{2,7,2,5},{9,0,5,2},{9,2,2,3},{10,0,5,1},{2,3,7,4},{4,9,0,3},{1,5,0,10},{1,1,6,8}, + {6,6,4,0},{11,5,0,0},{11,2,0,3},{4,0,10,2},{2,3,10,1},{1,13,1,1},{0,14,0,2},{2,3,3,8},{12,3,1,0},{14,0,0,2},{9,1,3,3},{6,4,0,6},{1,1,5,9},{5,9,0,2},{2,10,1,3},{12,0,0,4},{4,6,6,0},{0,6,4,6},{3,7,4,2},{0,13,3,0},{3,10,0,3},{10,2,1,3},{1,12,1,2},{2,0,13,1},{11,0,5,0},{12,1,3,0},{6,4,5,1},{10,4,2,0},{3,6,1,6},{7,3,6,0},{10,4,0,2},{10,0,2,4}, + {0,5,9,2},{0,9,3,4},{6,4,2,4},{3,4,7,2},{3,3,5,5},{4,2,9,1},{6,2,8,0},{3,5,3,5},{4,10,1,1},{10,1,3,2},{5,7,0,4},{5,3,7,1},{6,8,1,1},{8,8,0,0},{11,1,0,4},{14,1,0,1},{9,3,2,2},{8,2,1,5},{0,0,2,14},{3,3,9,1},{10,1,5,0},{8,3,1,4},{1,5,8,2},{6,1,9,0},{3,2,1,10},{3,11,1,1},{7,6,3,0},{9,0,3,4},{5,2,5,4},{0,2,3,11},{15,0,0,1},{0,6,6,4}, + {3,4,9,0},{4,7,0,5},{0,4,4,8},{0,13,2,1},{2,4,1,9},{3,2,5,6},{10,6,0,0},{3,5,6,2},{8,0,4,4},{1,3,6,6},{7,7,0,2},{6,1,4,5},{0,11,1,4},{2,2,8,4},{0,1,2,13},{15,0,1,0},{7,2,6,1},{8,1,7,0},{1,8,4,3},{2,13,1,0},{1,0,7,8},{14,2,0,0},{1,8,1,6},{9,3,3,1},{0,0,7,9},{4,4,1,7},{9,0,6,1},{10,2,4,0},{1,7,3,5},{0,3,8,5},{5,2,4,5},{1,2,5,8}, + {0,8,7,1},{10,3,2,1},{12,0,4,0},{2,1,4,9},{5,2,2,7},{1,9,3,3},{15,1,0,0},{6,3,4,3},{9,5,0,2},{1,6,9,0},{6,6,0,4},{13,2,1,0},{5,1,8,2},{0,5,11,0},{7,1,0,8},{1,2,12,1},{0,3,3,10},{7,4,2,3},{5,1,4,6},{7,0,3,6},{3,12,0,1},{3,4,5,4},{1,10,0,5},{7,4,3,2},{10,5,0,1},{13,3,0,0},{2,5,4,5},{3,10,1,2},{5,1,2,8},{14,0,1,1},{1,5,4,6},{1,4,5,6}, + {2,3,11,0},{11,0,4,1},{11,2,2,1},{5,3,8,0},{1,3,10,2},{0,1,13,2},{3,1,4,8},{4,2,4,6},{1,5,6,4},{2,1,11,2},{1,2,9,4},{4,7,3,2},{6,2,5,3},{7,2,2,5},{8,1,4,3},{3,2,8,3},{12,1,0,3},{7,8,1,0},{7,0,2,7},{5,10,0,1},{0,2,14,0},{2,9,3,2},{7,0,0,9},{11,1,4,0},{10,4,1,1},{2,2,9,3},{5,7,2,2},{1,3,1,11},{13,2,0,1},{4,2,8,2},{2,3,1,10},{4,2,5,5}, + {7,0,7,2},{10,0,0,6},{0,8,5,3},{4,4,0,8},{12,4,0,0},{0,1,14,1},{8,0,1,7},{5,1,5,5},{11,0,3,2},{0,4,1,11},{0,8,8,0},{0,2,5,9},{7,3,2,4},{7,8,0,1},{1,0,3,12},{7,4,5,0},{1,6,7,2},{7,6,1,2},{9,6,1,0},{12,2,0,2},{4,1,6,5},{4,0,1,11},{8,4,4,0},{13,0,1,2},{8,6,2,0},{4,12,0,0},{2,7,5,2},{2,0,5,9},{5,4,5,2},{3,8,5,0},{7,3,3,3},{4,4,8,0}, + {2,1,3,10},{5,0,1,10},{6,4,3,3},{4,9,1,2},{1,4,0,11},{11,3,1,1},{4,0,12,0},{13,0,0,3},{6,1,6,3},{9,0,4,3},{8,0,0,8},{8,4,0,4},{0,12,1,3},{0,4,10,2},{3,4,8,1},{1,3,8,4},{9,2,5,0},{5,7,4,0},{1,0,11,4},{4,10,0,2},{1,3,12,0},{6,9,0,1},{5,0,9,2},{5,9,2,0},{13,1,0,2},{9,3,4,0},{9,4,0,3},{3,1,12,0},{2,4,3,7},{1,2,13,0},{2,2,4,8},{6,8,0,2}, + {9,2,1,4},{9,5,1,1},{2,0,4,10},{5,4,0,7},{0,0,6,10},{1,2,0,13},{4,7,2,3},{6,5,5,0},{3,3,1,9},{1,6,1,8},{12,2,1,1},{4,4,5,3},{1,0,6,9},{0,6,10,0},{4,8,3,1},{4,3,2,7},{2,1,7,6},{1,9,1,5},{3,1,3,9},{8,7,1,0},{1,2,3,10},{14,1,1,0},{5,4,4,3},{3,7,0,6},{7,4,1,4},{3,7,5,1},{1,1,0,14},{0,10,3,3},{0,4,3,9},{1,7,7,1},{2,0,10,4},{5,8,0,3}, + {6,7,3,0},{0,8,4,4},{5,7,3,1},{7,9,0,0},{7,6,2,1},{0,4,5,7},{6,3,5,2},{1,2,1,12},{5,2,0,9},{8,5,0,3},{4,6,1,5},{1,1,7,7},{10,5,1,0},{1,2,8,5},{1,8,2,5},{5,1,0,10},{6,9,1,0},{13,0,2,1},{8,3,5,0},{6,3,6,1},{2,11,3,0},{3,7,3,3},{1,5,2,8},{7,5,2,2},{0,6,7,3},{13,1,1,1},{5,3,4,4},{7,2,7,0},{5,8,3,0},{3,13,0,0},{0,7,9,0},{8,0,3,5}, + {1,3,7,5},{4,0,2,10},{12,0,1,3},{1,7,6,2},{3,9,0,4},{7,2,0,7},{0,1,7,8},{2,1,8,5},{0,13,1,2},{0,8,1,7},{5,0,11,0},{5,6,2,3},{0,3,0,13},{2,3,4,7},{5,6,3,2},{4,2,10,0},{3,3,7,3},{7,2,5,2},{1,1,11,3},{12,3,0,1},{5,1,1,9},{1,15,0,0},{9,7,0,0},{9,1,2,4},{0,7,3,6},{3,0,13,0},{3,0,11,2},{0,6,5,5},{8,2,2,4},{6,10,0,0},{4,8,4,0},{0,0,3,13}, + {0,4,12,0},{7,1,6,2},{3,5,0,8},{8,0,6,2},{6,2,3,5},{2,10,0,4},{4,11,0,1},{6,1,5,4},{5,1,3,7},{0,11,3,2},{4,6,0,6},{2,6,0,8},{3,1,7,5},{2,14,0,0},{2,9,2,3},{0,3,4,9},{11,0,1,4},{13,0,3,0},{8,3,0,5},{0,5,3,8},{5,11,0,0},{0,1,4,11},{2,1,9,4},{3,4,4,5},{7,1,2,6},{12,2,2,0},{9,4,1,2},{6,0,2,8},{4,6,2,4},{11,2,3,0},{3,2,2,9},{10,3,1,2}, + {1,1,2,12},{0,5,2,9},{0,1,11,4},{6,2,4,4},{2,8,2,4},{0,9,4,3},{11,0,2,3},{0,2,11,3},{6,0,7,3},{0,3,6,7},{4,5,5,2},{1,2,6,7},{7,5,1,3},{9,0,2,5},{2,6,4,4},{4,1,9,2},{4,8,2,2},{1,12,3,0},{0,9,6,1},{0,10,6,0},{3,1,5,7},{2,13,0,1},{2,2,1,11},{3,6,0,7},{5,6,5,0},{5,5,4,2},{4,0,3,9},{3,4,1,8},{0,11,2,3},{2,12,1,1},{7,1,3,5},{7,0,9,0}, + {8,0,8,0},{1,0,2,13},{3,3,10,0},{2,4,4,6},{2,3,8,3},{1,10,5,0},{7,3,0,6},{2,9,0,5},{1,4,6,5},{6,6,3,1},{5,6,0,5},{6,3,0,7},{3,10,2,1},{2,5,5,4},{3,8,4,1},{1,14,0,1},{10,3,3,0},{3,5,7,1},{1,1,3,11},{2,4,0,10},{9,3,1,3},{5,10,1,0},{3,0,6,7},{3,1,9,3},{11,2,1,2},{5,3,3,5},{0,5,1,10},{4,1,11,0},{10,2,0,4},{7,6,0,3},{2,7,0,7},{4,2,2,8}, + {6,1,7,2},{4,9,2,1},{0,0,8,8},{3,7,2,4},{9,6,0,1},{0,12,4,0},{6,7,1,2},{0,7,2,7},{1,0,10,5},{0,0,14,2},{2,7,3,4},{5,0,0,11},{7,7,1,1},{6,2,7,1},{4,5,3,4},{3,5,1,7},{5,9,1,1},{6,2,1,7},{3,2,0,11},{0,11,0,5},{3,11,2,0},{10,1,4,1},{7,0,4,5},{11,4,0,1},{10,3,0,3},{0,2,4,10},{0,15,0,1},{0,11,5,0},{6,7,2,1},{1,12,2,1},{4,1,3,8},{1,0,13,2}, + {1,8,5,2},{7,0,1,8},{3,12,1,0},{9,2,4,1},{1,7,4,4},{11,4,1,0},{4,3,8,1},{2,8,4,2},{1,11,3,1},{1,1,4,10},{4,10,2,0},{8,2,5,1},{1,0,9,6},{5,3,2,6},{0,9,7,0},{10,2,2,2},{5,8,1,2},{8,7,0,1},{0,3,12,1},{1,0,1,14},{4,8,0,4},{3,8,0,5},{4,6,5,1},{0,9,5,2},{10,2,3,1},{2,3,9,2},{1,0,12,3},{11,3,0,2},{4,5,2,5},{0,2,12,2},{9,1,0,6},{9,2,0,5}, + {1,2,7,6},{4,7,4,1},{0,12,2,2},{0,0,0,16},{2,8,3,3},{3,6,2,5},{0,6,3,7},{7,5,4,0},{3,3,3,7},{3,3,0,10},{5,0,6,5},{0,0,10,6},{8,5,3,0},{8,1,5,2},{6,0,9,1},{11,1,2,2},{2,11,2,1},{9,5,2,0},{3,0,4,9},{2,2,12,0},{2,6,6,2},{2,1,13,0},{6,0,5,5},{2,0,14,0},{2,11,1,2},{4,4,7,1},{2,0,11,3},{3,1,1,11},{2,9,4,1},{3,7,6,0},{14,0,2,0},{1,10,4,1}, + {8,0,7,1},{3,6,5,2},{0,3,11,2},{2,5,6,3},{11,1,3,1},{6,5,3,2},{3,8,1,4},{0,2,7,7},{2,10,2,2},{1,6,2,7},{11,0,0,5},{12,1,1,2},{12,1,2,1},{0,7,1,8},{0,3,9,4},{0,2,1,13},{7,1,4,4},{10,1,0,5},{4,0,8,4},{5,2,7,2},{0,2,0,14},{4,3,7,2},{2,7,1,6},{1,2,2,11},{6,3,3,4},{1,14,1,0},{2,4,6,4},{5,3,6,2},{5,3,5,3},{8,4,1,3},{1,3,0,12},{3,5,2,6}, + {1,8,7,0},{0,7,4,5},{2,1,6,7},{4,11,1,0},{7,2,4,3},{6,1,3,6},{4,5,4,3},{2,11,0,3},{1,5,7,3},{12,0,2,2},{5,0,4,7},{1,13,0,2},{7,7,2,0},{4,1,7,4},{4,5,0,7},{5,0,5,6},{6,5,4,1},{2,4,2,8},{1,10,1,4},{6,3,1,6},{3,3,8,2},{0,7,7,2},{4,4,2,6},{1,1,8,6},{1,12,0,3},{2,1,12,1},{1,9,2,4},{1,11,0,4},{2,5,2,7},{10,0,3,3},{4,6,3,3},{3,7,1,5}, + {1,9,0,6},{7,1,7,1},{1,6,5,4},{9,2,3,2},{6,2,2,6},{2,2,2,10},{8,3,3,2},{0,1,8,7},{2,0,8,6},{0,3,1,12},{9,4,2,1},{9,4,3,0},{6,2,6,2},{1,8,0,7},{5,1,10,0},{0,5,5,6},{8,2,4,2},{2,3,2,9},{6,0,3,7},{2,2,6,6},{2,6,2,6},{1,13,2,0},{9,3,0,4},{7,3,5,1},{6,5,2,3},{5,2,6,3},{2,0,12,2},{5,7,1,3},{8,1,3,4},{3,1,10,2},{1,0,15,0},{0,8,0,8}, + {5,0,7,4},{4,4,6,2},{0,1,0,15},{10,0,1,5},{7,3,4,2},{4,9,3,0},{2,5,7,2},{3,4,2,7},{8,3,2,3},{5,1,6,4},{0,10,2,4},{6,6,1,3},{6,0,0,10},{4,4,3,5},{1,3,9,3},{7,5,3,1},{3,0,7,6},{1,8,6,1},{4,3,0,9},{3,11,0,2},{6,0,6,4},{0,1,3,12},{0,4,2,10},{5,5,6,0},{4,1,4,7},{8,1,6,1},{5,6,4,1},{8,4,2,2},{4,3,1,8},{3,0,2,11},{1,11,4,0},{0,8,3,5}, + {5,1,7,3},{7,0,8,1},{4,3,5,4},{4,6,4,2},{3,2,4,7},{1,6,3,6},{0,7,8,1},{3,0,1,12},{9,1,4,2},{7,4,0,5},{1,7,0,8},{5,4,1,6},{9,1,5,1},{1,1,9,5},{4,1,1,10},{5,3,0,8},{2,2,5,7},{4,0,0,12},{9,0,7,0},{3,4,0,9},{0,2,6,8},{8,2,0,6},{3,2,6,5},{4,2,6,4},{3,6,4,3},{2,8,6,0},{5,0,3,8},{0,4,0,12},{0,16,0,0},{0,9,2,5},{4,0,11,1},{1,6,4,5}, + {0,1,6,9},{3,4,6,3},{3,0,10,3},{7,0,6,3},{1,4,9,2},{1,5,3,7},{8,5,2,1},{0,12,0,4},{7,2,3,4},{0,5,6,5},{11,1,1,3},{6,5,0,5},{2,1,5,8},{1,4,11,0},{9,1,1,5},{0,0,13,3},{5,8,2,1},{2,12,0,2},{3,3,6,4},{4,1,10,1},{4,0,5,7},{8,1,0,7},{5,1,9,1},{4,3,3,6},{0,2,2,12},{6,3,2,5},{0,0,12,4},{1,5,1,9},{2,6,5,3},{3,6,3,4},{2,12,2,0},{1,6,8,1}, + {10,1,1,4},{1,3,4,8},{7,4,4,1},{1,11,1,3},{1,2,10,3},{3,9,3,1},{8,5,1,2},{2,10,4,0},{4,2,0,10},{2,7,6,1},{8,2,3,3},{1,5,5,5},{3,1,0,12},{3,10,3,0},{8,0,5,3},{0,6,8,2},{0,3,13,0},{0,0,16,0},{1,9,4,2},{4,1,8,3},{1,6,6,3},{0,10,5,1},{0,1,12,3},{4,0,6,6},{3,8,3,2},{0,5,4,7},{1,0,14,1},{0,4,6,6},{3,9,1,3},{3,5,8,0},{3,6,6,1},{5,4,7,0}, + {3,0,12,1},{8,6,1,1},{2,9,5,0},{6,1,1,8},{4,1,2,9},{3,9,4,0},{5,2,9,0},{0,12,3,1},{1,4,10,1},{4,0,7,5},{3,1,2,10},{5,4,2,5},{5,5,5,1},{4,2,3,7},{1,7,5,3},{2,8,0,6},{8,1,2,5},{3,8,2,3},{6,1,2,7},{3,9,2,2},{9,0,0,7},{0,8,6,2},{8,4,3,1},{0,2,8,6},{6,5,1,4},{2,3,5,6},{2,10,3,1},{0,7,0,9},{4,2,7,3},{2,4,8,2},{7,1,1,7},{2,4,7,3}, + {2,4,10,0},{0,1,10,5},{4,7,1,4},{0,10,4,2},{9,0,1,6},{1,9,6,0},{3,3,4,6},{4,5,7,0},{5,5,2,4},{2,8,1,5},{2,3,6,5},{0,1,1,14},{3,2,3,8},{10,1,2,3},{9,1,6,0},{3,4,3,6},{2,2,0,12},{0,0,9,7},{4,0,9,3},{7,0,5,4},{4,5,6,1},{2,5,1,8},{2,5,9,0},{3,5,4,4},{1,3,11,1},{7,1,5,3},{3,2,7,4},{1,4,2,9},{1,11,2,2},{2,2,3,9},{5,0,10,1},{3,2,11,0}, + {1,10,3,2},{8,3,4,1},{3,6,7,0},{0,7,5,4},{1,3,3,9},{2,2,10,2},{1,9,5,1},{0,5,0,11},{3,0,3,10},{0,4,8,4},{2,7,7,0},{2,0,2,12},{1,2,11,2},{6,3,7,0},{0,6,2,8},{0,10,1,5},{0,9,0,7},{6,4,4,2},{6,0,1,9},{1,5,10,0},{5,4,6,1},{5,5,3,3},{0,0,4,12},{0,3,2,11},{1,4,1,10},{3,0,9,4},{5,5,0,6},{1,7,8,0},{2,0,3,11},{6,4,1,5},{10,0,6,0},{0,6,0,10}, + {0,4,11,1},{3,1,6,6},{2,5,8,1},{0,2,10,4},{3,1,11,1},{6,6,2,2},{1,1,10,4},{2,1,2,11},{6,1,8,1},{0,2,13,1},{0,7,6,3},{6,8,2,0},{3,0,0,13},{4,4,4,4},{6,2,0,8},{7,3,1,5},{0,11,4,1},{6,7,0,3},{2,6,3,5},{5,2,1,8},{7,1,8,0},{5,5,1,5},{1,8,3,4},{8,2,6,0},{6,0,10,0},{5,6,1,4},{1,4,4,7},{2,7,4,3},{1,4,8,3},{5,4,3,4},{1,10,2,3},{2,9,1,4}, + {2,2,11,1},{2,5,0,9},{0,0,1,15},{0,0,11,5},{0,4,7,5},{0,1,15,0},{2,1,0,13},{0,3,10,3},{8,0,2,6},{3,3,2,8},{3,5,5,3},{1,7,1,7},{1,3,2,10},{4,0,4,8},{2,0,9,5},{1,1,1,13},{2,2,7,5},{2,1,10,3},{4,2,1,9},{4,3,6,3},{1,3,5,7},{2,5,3,6},{1,0,8,7},{5,0,2,9},{2,8,5,1},{1,6,0,9},{0,0,5,11},{0,4,9,3},{2,0,7,7},{1,7,2,6},{2,1,1,12},{2,4,9,1}, + {0,5,7,4},{6,0,4,6},{3,2,10,1},{0,6,1,9},{2,6,1,7},{0,5,8,3},{4,1,0,11},{1,2,4,9},{4,1,5,6},{6,1,0,9},{1,4,3,8},{4,5,1,6},{1,0,5,10},{5,3,1,7},{0,9,1,6},{2,0,1,13},{2,0,6,8},{8,1,1,6},{1,5,9,1},{0,6,9,1},{0,3,5,8},{0,2,9,5},{5,2,8,1},{1,1,14,0},{3,2,9,2},{5,0,8,3},{0,5,10,1},{5,2,3,6},{2,6,7,1},{2,3,0,11},{0,1,9,6},{1,0,4,11}, + {3,0,5,8},{0,0,15,1},{2,4,5,5},{0,3,7,6},{2,0,0,14},{1,1,12,2},{2,6,8,0},{3,1,8,4},{0,1,5,10} + }; + + // All total orderings for 16 pixels [0,2] 2-bit selectors. + // BC1 selector order: 0, 1, 2 + // Note this is different from g_unique_total_orders4[], which reorders the selectors into linear order. + const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS3 = 153; + static uint8_t g_unique_total_orders3[NUM_UNIQUE_TOTAL_ORDERINGS3][3] = + { + {6,0,10},{3,6,7},{3,0,13},{13,3,0},{12,4,0},{9,1,6},{2,13,1},{4,7,5},{7,5,4},{9,6,1},{7,4,5},{8,6,2},{16,0,0},{10,6,0},{2,7,7}, + {0,0,16},{0,3,13},{1,15,0},{0,2,14},{1,4,11},{15,1,0},{1,12,3},{9,2,5},{14,1,1},{8,2,6},{3,3,10},{4,2,10},{14,0,2},{0,14,2},{1,7,8},{6,6,4}, + {11,5,0},{6,4,6},{11,3,2},{4,3,9},{7,1,8},{10,4,2},{12,1,3},{11,0,5},{9,3,4},{1,0,15},{9,0,7},{2,6,8},{12,2,2},{6,2,8},{6,8,2},{15,0,1}, + {4,8,4},{0,4,12},{8,5,3},{5,9,2},{11,2,3},{12,3,1},{6,3,7},{1,1,14},{2,9,5},{1,8,7},{4,10,2},{7,7,2},{13,1,2},{0,15,1},{3,2,11},{7,0,9}, + {4,4,8},{3,8,5},{0,5,11},{13,2,1},{1,10,5},{4,11,1},{3,10,3},{5,10,1},{10,2,4},{0,6,10},{14,2,0},{11,4,1},{3,12,1},{1,13,2},{1,5,10},{5,11,0}, + {12,0,4},{8,1,7},{6,10,0},{3,13,0},{7,2,7},{0,7,9},{5,8,3},{0,12,4},{11,1,4},{13,0,3},{0,16,0},{5,7,4},{10,3,3},{10,0,6},{0,13,3},{4,6,6}, + {2,8,6},{2,5,9},{7,8,1},{2,1,13},{2,0,14},{7,3,6},{5,1,10},{3,11,2},{5,4,7},{8,3,5},{10,5,1},{6,9,1},{1,3,12},{4,5,7},{2,2,12},{4,1,11}, + {0,8,8},{4,12,0},{6,5,5},{8,7,1},{5,5,6},{3,7,6},{7,9,0},{4,9,3},{0,10,6},{8,0,8},{5,3,8},{10,1,5},{6,1,9},{7,6,3},{9,5,2},{0,1,15}, + {9,7,0},{2,14,0},{3,4,9},{8,4,4},{9,4,3},{0,9,7},{1,9,6},{3,9,4},{5,2,9},{2,3,11},{5,6,5},{1,14,1},{6,7,3},{2,4,10},{2,12,2},{8,8,0}, + {2,10,4},{4,0,12},{0,11,5},{2,11,3},{1,11,4},{3,5,8},{5,0,11},{3,1,12},{1,2,13},{1,6,9} + }; + + // For each total ordering, this table indicates which other total orderings are likely to improve quality using a least squares pass. Each array is sorted by usefulness. + static uint16_t g_best_total_orderings4[NUM_UNIQUE_TOTAL_ORDERINGS4][MAX_TOTAL_ORDERINGS4] = + { +#if RGBCX_USE_SMALLER_TABLES + #include "rgbcx_table4_small.h" +#else + #include "rgbcx_table4.h" +#endif + }; + + static uint8_t g_best_total_orderings3[NUM_UNIQUE_TOTAL_ORDERINGS3][32] = + { + { 12,1,3,5,27,2,4,38,8,7,16,18,6,10,41,79,40,23,46,9,20,88,22,37,14,19,24,126,99,119,35,11 }, + { 7,64,116,14,94,30,8,42,1,108,47,55,137,10,134,95,96,115,69,32,63,29,90,113,11,148,16,103,19,9,34,25 }, + { 12,1,0,5,3,7,4,27,8,6,38,40,41,16,18,46,9,10,20,23,79,62,14,22,88,99,37,126,92,19,120,11 }, + { 16,88,27,18,46,48,126,107,79,19,59,38,37,65,23,66,0,2,3,43,12,151,28,25,5,87,72,40,1,20,52,92 }, + { 79,48,88,16,27,65,18,38,46,19,37,4,72,33,126,41,52,0,12,92,5,1,2,107,3,77,23,91,43,51,22,74 }, + { 1,8,41,122,10,22,2,0,87,24,37,120,38,7,39,4,5,3,9,92,62,59,23,16,104,11,27,79,19,26,25,32 }, + { 2,76,99,28,40,86,93,21,138,60,6,0,17,128,145,119,98,144,141,82,147,54,67,75,5,12,27,132,146,1,38,14 }, + { 47,7,64,90,1,118,116,85,57,14,30,94,50,45,137,134,8,42,69,139,55,68,58,108,95,29,10,115,0,32,2,11 }, + { 49,8,10,30,124,11,32,113,130,58,125,9,100,53,104,115,131,103,24,7,1,39,45,36,139,0,137,22,90,44,114,105 }, + { 9,38,72,125,49,41,84,11,13,5,27,0,16,92,8,2,65,105,10,18,48,29,127,131,36,14,1,46,111,79,130,12 }, + { 130,8,10,100,104,131,49,32,53,39,30,36,113,24,11,22,124,44,83,58,7,103,1,4,9,125,5,0,91,33,115,74 }, + { 114,11,58,8,120,49,9,124,142,111,41,30,10,0,97,130,62,84,38,5,72,125,92,127,100,27,139,113,13,132,32,1 }, + { 60,46,28,27,40,20,0,17,18,2,126,16,6,38,86,23,79,54,1,93,5,88,41,14,21,111,7,48,3,84,72,62 }, + { 72,92,38,65,84,48,41,79,27,16,29,111,88,5,18,46,1,0,152,14,37,19,77,42,132,7,22,13,119,56,12,2 }, + { 7,55,1,95,29,56,64,116,143,8,14,30,47,94,152,90,65,67,10,133,42,72,146,84,16,48,6,0,25,108,77,21 }, + { 27,23,20,5,0,79,38,2,3,1,59,46,4,41,33,86,37,87,88,92,7,126,43,8,22,152,151,150,149,148,147,146 }, + { 12,0,1,2,7,6,3,5,28,4,8,14,60,40,17,19,21,86,126,93,10,18,9,29,48,99,65,25,84,119,72,41 }, + { 60,40,99,2,54,12,0,1,19,28,98,93,6,138,21,5,27,17,151,14,76,46,16,18,38,29,86,144,107,7,25,41 }, + { 12,0,1,2,3,5,6,7,4,28,8,60,14,40,16,17,21,10,19,9,86,38,126,41,93,27,29,48,62,84,79,99 }, + { 0,1,2,10,5,8,3,25,4,29,32,34,63,7,77,26,16,48,65,56,14,22,129,103,72,24,18,152,140,53,96,42 }, + { 46,126,18,54,12,16,1,0,5,2,27,98,20,23,6,3,88,48,28,7,19,8,4,60,151,38,37,21,79,14,65,40 }, + { 76,6,141,86,119,2,138,67,28,145,0,93,17,1,40,60,146,99,147,14,21,144,132,7,5,29,55,27,16,75,19,12 }, + { 71,5,51,39,22,80,0,43,10,122,8,62,41,24,104,87,35,37,2,91,33,120,36,38,1,131,9,100,130,66,3,4 }, + { 126,18,46,27,20,16,88,23,12,79,54,59,48,0,73,1,37,151,5,19,28,38,2,66,60,3,65,98,14,26,6,43 }, + { 22,10,8,5,0,71,35,80,104,39,24,51,100,1,62,32,2,130,11,41,7,9,53,43,49,83,122,120,30,44,37,38 }, + { 1,34,14,129,53,63,42,26,121,148,7,44,96,10,0,24,100,32,64,116,140,22,5,19,29,103,135,108,8,61,39,83 }, + { 1,7,34,63,44,25,135,14,24,108,22,0,83,94,5,129,35,101,47,121,2,19,42,53,6,110,103,8,148,10,16,123 }, + { 12,28,16,60,18,1,6,21,14,0,86,19,2,48,93,17,38,29,7,5,65,126,46,72,41,79,84,119,40,56,54,88 }, + { 0,2,12,27,5,46,38,40,41,79,88,99,3,23,1,62,20,4,22,37,92,35,18,8,16,24,10,60,7,120,98,54 }, + { 1,7,14,56,8,0,84,67,10,2,133,72,42,111,5,30,21,4,9,3,25,94,16,116,47,11,65,18,132,90,55,64 }, + { 30,8,124,139,45,11,58,90,113,137,7,115,10,32,1,49,94,85,9,47,108,103,0,97,63,14,50,114,53,106,100,25 }, + { 65,38,48,27,16,79,72,18,88,19,46,77,84,92,37,41,0,29,1,14,12,111,2,5,31,36,87,74,105,40,28,51 }, + { 10,8,30,113,130,100,53,32,115,103,104,7,1,121,39,49,131,44,24,36,63,137,34,45,22,90,108,83,26,11,94,139 }, + { 51,52,43,33,5,74,16,37,71,91,38,3,36,87,48,22,4,0,122,41,39,18,66,27,79,24,65,88,59,23,62,92 }, + { 1,7,63,53,108,121,94,44,103,100,14,10,129,47,32,26,24,25,148,42,135,22,0,61,83,8,39,104,5,64,115,34 }, + { 1,8,10,7,5,0,80,32,62,2,24,44,53,83,9,41,30,22,100,11,14,25,120,4,26,6,3,16,122,34,19,35 }, + { 74,4,36,48,33,91,39,79,22,16,65,5,131,38,24,71,27,52,0,105,51,18,88,104,3,31,10,37,72,19,41,130 }, + { 59,43,38,79,23,27,92,51,0,16,46,5,18,88,41,37,66,3,87,20,48,2,122,4,22,12,1,126,19,65,33,24 }, + { 12,28,1,27,0,16,2,46,65,60,21,3,5,18,6,19,48,14,4,7,79,88,86,29,22,72,93,40,23,8,17,41 }, + { 22,91,39,33,24,71,5,131,36,10,51,0,130,8,104,2,35,125,9,43,52,49,83,80,100,41,122,3,37,38,4,16 }, + { 12,0,1,2,5,3,4,8,7,27,18,38,10,6,16,46,9,20,41,23,126,79,22,14,19,99,88,54,37,48,62,35 }, + { 12,27,1,2,3,0,46,4,38,16,8,28,7,79,18,5,84,6,88,10,14,21,23,20,40,22,60,19,9,29,72,65 }, + { 1,14,7,55,95,29,8,94,30,56,10,108,77,116,152,64,32,48,63,42,143,148,16,25,137,65,11,0,115,9,19,72 }, + { 37,79,66,38,16,52,48,59,43,27,87,33,41,4,23,51,3,5,88,18,92,46,73,122,22,71,20,0,65,19,2,120 }, + { 24,32,83,22,53,1,8,10,7,30,35,5,103,0,100,101,121,113,34,123,63,2,44,25,71,115,80,14,26,108,51,39 }, + { 97,45,111,58,85,139,0,90,47,7,120,106,142,30,50,132,41,62,84,1,119,114,14,56,117,8,38,29,2,64,116,5 }, + { 12,28,16,18,1,60,6,14,2,21,0,86,126,19,48,93,7,27,17,29,5,65,54,38,72,79,84,88,119,145,8,111 }, + { 118,47,64,116,57,85,7,14,50,1,42,0,45,68,86,69,2,111,134,28,90,55,16,29,56,48,84,144,60,30,112,41 }, + { 12,1,2,0,7,6,28,5,3,4,8,14,60,21,18,40,17,86,10,9,16,29,19,93,126,79,38,84,72,27,111,119 }, + { 11,8,49,130,10,125,9,124,100,114,131,30,58,104,32,39,24,113,36,105,0,41,22,120,5,53,111,38,142,44,83,35 }, + { 50,70,47,118,85,57,106,0,45,7,64,90,81,14,2,134,28,62,86,55,69,1,78,119,68,56,18,67,16,60,29,21 }, + { 43,37,33,87,51,41,66,5,122,38,22,59,92,0,23,91,27,16,71,79,18,52,120,4,3,24,46,20,73,39,62,36 }, + { 79,48,4,16,27,88,43,33,18,38,65,37,46,3,19,51,52,22,66,87,74,5,41,91,23,59,0,71,122,72,20,92 }, + { 32,100,10,8,30,104,24,44,39,113,83,103,1,7,22,53,115,63,135,121,26,35,34,5,0,108,137,90,91,45,2,130 }, + { 0,1,2,5,16,12,6,7,14,3,19,18,29,20,4,21,40,8,17,35,23,48,126,22,25,56,26,10,98,27,38,65 }, + { 143,67,56,146,1,7,133,55,64,141,134,69,6,47,14,29,84,21,111,147,57,16,95,72,118,132,50,0,2,18,119,42 }, + { 1,7,67,14,133,111,8,84,0,21,2,47,64,132,55,10,95,147,119,42,16,5,72,56,4,3,6,29,9,25,18,30 }, + { 68,57,69,112,144,86,102,2,134,55,0,70,118,64,75,47,14,28,93,143,67,7,50,149,1,21,29,56,119,95,60,78 }, + { 58,97,114,30,124,45,11,139,8,90,0,142,7,10,41,113,84,62,49,111,85,1,9,5,137,120,32,14,2,117,47,38 }, + { 23,66,18,79,38,20,43,27,16,88,46,59,126,37,87,12,73,92,3,5,48,0,19,54,2,51,28,1,41,65,122,22 }, + { 0,12,2,27,5,40,46,38,1,41,3,79,88,23,99,4,20,62,22,54,92,18,8,37,16,35,10,7,19,120,144,24 }, + { 1,14,25,26,0,7,44,34,129,42,24,5,135,22,19,148,6,96,83,2,29,16,63,35,101,64,140,136,116,110,3,10 }, + { 12,1,2,27,3,4,38,5,7,8,18,16,46,6,0,40,41,10,79,23,88,9,20,22,14,19,37,92,48,126,28,21 }, + { 7,1,10,32,108,103,94,47,8,53,25,14,34,115,100,129,121,130,148,42,64,116,63,26,44,0,24,30,113,4,104,22 }, + { 47,134,7,14,55,69,64,95,1,29,85,118,56,116,45,57,102,143,50,90,42,30,16,94,0,8,67,75,133,2,18,48 }, + { 12,1,2,0,7,6,28,8,14,5,3,4,40,21,17,18,60,86,16,93,126,10,9,29,99,38,119,25,19,54,27,84 }, + { 59,16,27,18,23,88,79,37,46,66,38,20,73,126,3,43,48,87,92,51,41,12,19,5,52,107,65,0,151,122,54,2 }, + { 1,21,147,7,119,14,76,132,55,0,86,145,2,6,69,67,16,143,111,138,17,28,29,60,18,93,8,19,40,56,84,5 }, + { 144,86,112,2,68,102,69,0,149,93,75,28,57,55,145,60,21,67,99,134,143,40,146,119,82,110,62,6,29,26,78,14 }, + { 102,57,55,69,143,75,146,67,56,68,134,2,29,141,0,21,6,14,133,118,64,1,7,95,47,84,111,28,147,82,72,119 }, + { 0,70,57,119,50,145,2,86,28,118,69,78,149,47,60,68,67,55,93,81,134,21,14,62,64,7,5,1,132,85,41,16 }, + { 51,5,43,71,122,87,41,37,91,39,0,22,33,36,38,24,66,120,62,2,80,16,92,10,59,4,27,23,35,79,8,3 }, + { 12,1,2,0,7,6,28,5,8,14,3,21,40,4,60,17,86,18,16,93,10,9,126,119,99,29,19,41,38,27,25,92 }, + { 27,18,46,126,23,16,88,79,20,151,59,73,48,38,0,54,12,2,37,1,19,5,28,60,66,41,3,109,86,65,40,6 }, + { 48,79,4,33,16,74,65,38,88,27,91,52,18,36,22,19,46,0,37,3,51,5,71,39,72,43,24,41,92,87,2,10 }, + { 86,2,144,93,28,112,141,6,102,21,99,60,75,0,68,82,69,146,67,149,55,40,145,76,111,147,56,119,110,143,26,132 }, + { 6,138,2,99,86,17,40,93,28,21,145,141,0,60,119,147,128,76,67,54,1,12,5,27,144,14,38,98,146,41,29,19 }, + { 1,8,0,10,2,29,7,5,3,56,4,25,14,152,63,32,65,72,96,42,34,108,48,9,26,16,84,103,67,148,22,129 }, + { 149,145,0,86,2,28,93,144,62,60,119,101,21,41,5,35,78,99,26,40,12,68,57,67,110,120,69,18,55,76,132,70 }, + { 12,28,16,1,48,19,6,60,2,14,18,21,0,27,46,65,86,29,5,7,72,93,40,3,17,84,56,88,126,4,38,8 }, + { 1,8,5,10,7,24,2,62,0,41,22,122,120,9,4,3,32,87,11,37,38,83,100,44,25,104,16,26,39,80,14,6 }, + { 0,119,62,86,145,149,28,132,93,2,120,67,60,41,35,5,144,21,123,38,111,81,84,56,12,44,24,50,92,55,40,22 }, + { 2,93,99,28,40,144,60,0,86,150,76,21,149,98,6,25,1,61,82,26,12,5,54,141,7,18,145,16,27,138,110,38 }, + { 24,8,10,22,32,35,100,5,1,53,0,7,71,80,30,123,83,104,51,11,2,39,44,113,9,62,25,103,34,101,43,41 }, + { 12,1,2,0,7,6,28,5,40,60,8,16,3,18,14,4,86,21,17,93,41,10,9,99,27,119,38,19,126,22,48,145 }, + { 45,47,50,7,85,90,97,1,64,139,116,118,30,58,14,106,70,111,0,57,94,42,137,142,29,120,8,56,18,134,84,41 }, + { 12,0,2,5,27,38,1,46,41,40,79,144,3,22,88,23,28,60,99,62,6,24,26,7,4,16,10,35,37,18,14,20 }, + { 37,38,59,92,0,5,23,51,79,41,27,22,2,3,87,16,46,4,1,43,20,33,18,88,24,71,8,10,48,19,126,122 }, + { 12,28,16,60,1,18,6,21,19,14,48,0,2,86,93,5,46,29,17,27,65,7,3,72,38,126,119,40,84,37,56,4 }, + { 0,2,5,1,16,6,27,28,18,38,60,7,14,21,46,40,86,41,19,48,93,8,3,79,22,4,10,37,62,23,24,111 }, + { 85,7,90,30,47,139,45,50,94,58,137,1,8,64,14,116,118,115,113,11,124,108,0,10,97,57,32,70,42,106,29,114 }, + { 33,36,22,71,51,5,91,39,0,52,43,24,131,74,16,37,38,122,41,3,87,48,4,104,35,80,10,2,105,62,27,18 }, + { 12,1,27,2,0,16,3,28,46,18,4,6,5,72,21,79,38,7,14,60,88,8,65,19,48,29,23,40,22,20,86,126 }, + { 0,12,2,27,5,38,46,41,1,40,79,3,88,23,22,99,20,37,62,4,18,6,16,35,60,28,24,7,92,8,14,10 }, + { 7,47,1,30,137,8,116,94,90,64,14,115,108,118,57,10,148,113,42,85,32,11,63,50,103,45,124,134,55,9,69,34 }, + { 55,7,1,29,56,143,64,47,67,133,14,146,95,72,84,8,116,111,6,134,141,21,65,0,69,30,16,45,85,42,50,10 }, + { 14,1,42,8,10,29,108,63,55,148,95,32,7,19,25,115,103,34,56,129,77,0,16,152,94,30,113,26,2,5,48,4 }, + { 111,120,142,97,58,0,41,45,62,132,114,84,139,30,5,8,38,2,7,85,119,90,117,1,124,11,56,47,28,27,35,72 }, + { 1,0,14,2,6,5,16,19,7,29,42,18,3,25,12,35,21,8,26,17,40,4,20,48,109,99,22,96,55,101,10,61 }, + { 12,0,1,5,3,2,4,7,27,8,38,6,40,18,16,10,20,46,9,41,23,22,79,14,62,19,37,126,88,11,92,48 }, + { 10,8,104,39,24,32,22,83,44,100,30,130,53,91,113,5,11,1,35,33,7,49,0,2,103,71,36,124,9,80,131,34 }, + { 1,7,0,14,8,34,5,25,35,26,6,63,10,123,2,16,103,19,44,32,135,121,108,80,62,30,115,94,149,144,53,18 }, + { 75,68,146,141,102,67,2,21,6,57,69,143,0,55,82,86,28,144,147,29,93,112,56,119,133,14,76,60,84,134,111,145 }, + { 10,32,115,7,8,53,1,108,30,113,94,137,100,63,90,34,130,103,121,47,44,25,104,39,24,26,85,14,49,36,22,131 }, + { 39,24,10,22,8,130,91,104,83,49,5,33,100,11,0,35,32,131,71,36,9,44,53,2,80,51,30,1,41,7,43,62 }, + { 38,36,65,105,27,72,31,79,41,131,5,48,125,39,0,16,92,46,22,13,18,84,24,37,88,2,33,74,91,71,130,49 }, + { 0,106,62,50,45,119,85,81,132,28,2,86,41,47,38,60,35,117,5,29,7,30,145,90,55,70,14,111,18,67,93,56 }, + { 0,2,5,1,3,25,19,26,4,34,29,10,22,16,8,7,24,14,48,65,53,18,6,77,44,56,72,61,121,21,136,40 }, + { 7,1,94,8,47,115,10,32,113,103,30,108,137,63,14,64,116,148,129,42,90,25,34,118,53,57,11,49,85,9,96,50 }, + { 14,0,1,26,19,5,42,2,25,24,29,22,6,44,61,16,7,96,136,3,140,34,35,55,135,18,48,77,83,4,8,10 }, + { 1,7,14,0,25,6,34,5,26,16,63,2,19,8,35,101,108,29,94,10,18,42,123,144,129,47,61,21,3,62,149,4 }, + { 12,0,2,1,28,5,6,120,7,60,40,16,18,86,27,14,21,93,8,62,41,38,3,17,4,119,99,48,19,126,10,9 }, + { 86,144,93,2,28,149,0,60,99,112,110,145,40,21,102,26,75,62,69,1,12,101,119,25,76,67,7,68,55,5,6,14 }, + { 8,30,10,32,113,49,115,137,124,103,45,90,7,139,11,1,58,53,130,94,108,100,9,63,85,125,34,47,0,24,44,104 }, + { 120,142,111,41,58,114,97,0,11,62,84,124,5,30,8,38,132,127,27,139,92,10,72,45,49,9,28,2,29,56,16,1 }, + { 8,113,30,137,7,32,10,90,94,115,1,103,108,63,47,85,49,53,11,45,34,50,14,25,9,124,100,130,139,121,42,26 }, + { 64,7,14,47,134,55,1,42,95,69,116,90,94,30,8,29,56,137,45,108,85,10,57,16,102,143,118,19,63,32,11,50 }, + { 62,132,0,119,120,41,111,86,35,28,5,84,56,38,2,93,145,60,67,12,92,27,29,72,55,117,21,24,133,149,22,45 }, + { 57,68,69,118,134,64,50,47,55,14,7,2,102,144,0,112,70,86,85,1,95,29,116,143,42,75,16,56,28,45,21,48 }, + { 0,12,2,1,5,28,6,40,60,27,7,38,16,14,86,18,93,41,62,46,99,35,8,23,3,17,22,21,10,19,79,20 }, + { 12,1,2,27,16,3,38,111,4,0,18,5,7,46,40,8,79,6,14,28,88,10,48,41,19,84,21,9,22,23,20,72 }, + { 53,103,32,7,1,100,22,63,71,44,10,115,108,24,92,104,26,30,122,94,8,39,83,34,137,135,90,91,121,5,87,47 }, + { 87,37,41,0,22,38,2,92,1,24,4,8,3,59,10,5,39,23,71,79,122,27,16,46,33,7,91,20,18,51,9,120 }, + { 1,7,8,10,0,5,35,32,53,44,14,30,2,80,25,34,6,62,26,103,16,19,63,9,149,24,121,41,22,11,113,83 }, + { 11,58,8,30,124,49,10,113,9,114,139,45,97,32,7,137,90,1,0,130,115,125,100,24,5,94,53,41,14,13,35,38 }, + { 125,105,9,36,131,49,8,130,39,11,10,5,22,38,41,104,0,31,13,24,27,16,2,72,65,91,48,32,84,18,100,74 }, + { 12,1,0,2,6,3,7,5,4,8,14,28,16,60,18,10,21,17,19,9,40,27,86,93,29,38,54,11,25,48,46,41 }, + { 84,41,38,72,92,29,111,5,65,120,79,0,27,56,48,14,132,16,119,22,86,88,46,28,62,12,1,2,93,18,24,127 }, + { 99,28,40,60,2,93,138,0,98,17,86,54,76,12,27,1,21,144,128,38,5,14,46,18,25,16,109,6,41,145,7,29 }, + { 1,63,10,32,148,14,103,34,42,7,8,108,116,53,64,96,25,121,26,94,140,0,29,19,55,24,100,136,5,4,44,115 }, + { 131,100,130,49,10,8,36,104,39,0,48,41,11,38,4,24,27,22,16,44,79,5,33,2,53,9,125,74,91,120,32,83 }, + { 36,39,131,74,4,91,22,33,125,104,130,48,10,24,16,5,49,8,100,105,79,0,9,65,71,2,18,83,31,11,19,44 }, + { 0,12,2,1,6,5,7,28,40,60,16,14,18,62,86,27,93,8,17,38,21,41,35,99,3,19,10,23,22,4,9,48 }, + { 1,7,67,14,21,147,111,55,132,119,0,8,2,76,64,16,47,84,6,18,86,95,145,10,42,29,133,5,56,134,17,72 }, + { 69,55,47,134,102,143,7,57,118,95,14,64,29,56,1,50,75,67,146,2,0,133,68,16,21,6,141,85,116,18,72,65 }, + { 1,44,7,24,83,63,34,103,22,121,53,32,25,35,0,115,108,5,14,8,10,101,94,30,2,123,110,26,137,47,90,19 }, + { 14,1,25,42,34,0,26,96,19,29,140,5,53,10,2,121,3,24,44,22,55,77,129,7,63,16,8,4,6,61,100,48 }, + { 30,90,7,8,137,94,85,1,47,113,115,108,45,139,124,11,10,32,50,58,103,14,63,64,9,116,49,42,25,148,0,53 }, + { 40,99,2,60,28,17,0,54,93,98,86,138,6,12,21,76,1,5,27,144,128,38,19,46,14,41,145,7,16,67,3,109 }, + { 45,58,30,139,90,7,85,137,97,8,124,47,1,11,106,114,50,94,0,113,10,115,14,32,9,64,108,41,49,29,62,116 }, + { 14,42,10,1,63,96,32,25,34,8,129,29,0,103,55,19,26,53,77,5,95,2,4,7,3,16,148,56,18,24,121,108 }, + { 21,2,75,86,6,76,144,28,119,99,93,147,141,67,102,145,60,132,146,128,0,82,40,138,55,111,143,17,133,112,69,14 }, + { 111,120,41,62,84,132,0,5,38,119,56,92,72,142,27,28,29,35,58,80,2,86,65,79,12,14,1,24,145,16,21,48 }, + { 146,67,141,69,133,21,6,143,57,55,111,147,56,1,14,132,7,2,134,102,0,119,29,84,76,64,86,72,28,68,47,75 }, + { 12,1,0,5,27,3,7,4,38,8,6,41,16,40,46,10,18,79,2,9,23,86,20,22,62,14,37,88,92,19,24,11 }, + { 0,12,2,1,27,5,38,28,60,6,40,7,16,46,18,14,41,99,93,62,3,79,86,23,149,8,22,35,88,17,19,10 }, + { 141,6,21,67,147,102,146,2,76,119,132,69,55,111,86,75,28,133,143,0,1,145,14,128,56,99,17,60,29,93,84,68 }, + { 21,76,1,119,86,145,2,0,14,7,6,138,146,55,17,28,132,93,67,40,60,143,29,147,111,16,69,141,5,56,19,133 }, + { 1,8,108,14,7,116,64,42,10,63,94,32,115,103,113,96,30,34,55,47,95,148,29,140,129,25,134,53,69,26,19,11 }, + { 12,1,3,5,4,2,0,7,8,38,27,16,18,6,10,20,41,40,79,46,9,23,22,88,92,37,14,24,62,19,48,99 }, + { 1,14,7,0,6,25,5,16,19,2,42,26,29,35,61,8,18,129,101,21,3,110,34,148,96,10,17,4,22,40,12,20 }, + { 0,2,5,1,3,19,22,26,16,24,29,7,14,6,4,25,18,44,8,48,12,61,20,21,10,35,65,56,23,40,17,107 }, + { 1,7,8,29,56,0,10,14,2,42,72,5,4,65,3,30,84,94,67,9,25,133,111,11,32,108,16,63,21,96,26,48 } + }; +#ifdef _MSC_VER +#pragma endregion +#endif + + static inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast(-i) : static_cast(i); } + //static inline uint64_t iabs(int64_t i) { return (i < 0) ? static_cast(-i) : static_cast(i); } + + static inline uint8_t to_5(uint32_t v) { v = v * 31 + 128; return (uint8_t)((v + (v >> 8)) >> 8); } + static inline uint8_t to_6(uint32_t v) { v = v * 63 + 128; return (uint8_t)((v + (v >> 8)) >> 8); } + + template inline T square(T a) { return a * a; } + + static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; } + + template inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); } + static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; } + + static inline int squarei(int a) { return a * a; } + //static inline int absi(int a) { return (a < 0) ? -a : a; } + + template inline F lerp(F a, F b, F s) { return a + (b - a) * s; } + + static const uint32_t TOTAL_ORDER_4_0_16 = 15; + static const uint32_t TOTAL_ORDER_4_1_16 = 700; + static const uint32_t TOTAL_ORDER_4_2_16 = 753; + static const uint32_t TOTAL_ORDER_4_3_16 = 515; + static uint16_t g_total_ordering4_hash[4096]; + static float g_selector_factors4[NUM_UNIQUE_TOTAL_ORDERINGS4][3]; + + static const uint32_t TOTAL_ORDER_3_0_16 = 12; + static const uint32_t TOTAL_ORDER_3_1_16 = 15; + static const uint32_t TOTAL_ORDER_3_2_16 = 89; + static uint16_t g_total_ordering3_hash[256]; + static float g_selector_factors3[NUM_UNIQUE_TOTAL_ORDERINGS3][3]; + + struct hist4 + { + uint8_t m_hist[4]; + + hist4() + { + memset(m_hist, 0, sizeof(m_hist)); + } + + hist4(uint32_t i, uint32_t j, uint32_t k, uint32_t l) + { + m_hist[0] = (uint8_t)i; + m_hist[1] = (uint8_t)j; + m_hist[2] = (uint8_t)k; + m_hist[3] = (uint8_t)l; + } + + inline bool operator== (const hist4& h) const + { + if (m_hist[0] != h.m_hist[0]) return false; + if (m_hist[1] != h.m_hist[1]) return false; + if (m_hist[2] != h.m_hist[2]) return false; + if (m_hist[3] != h.m_hist[3]) return false; + return true; + } + + inline bool any_16() const + { + return (m_hist[0] == 16) || (m_hist[1] == 16) || (m_hist[2] == 16) || (m_hist[3] == 16); + } + + inline uint32_t lookup_total_ordering_index() const + { + if (m_hist[0] == 16) + return TOTAL_ORDER_4_0_16; + else if (m_hist[1] == 16) + return TOTAL_ORDER_4_1_16; + else if (m_hist[2] == 16) + return TOTAL_ORDER_4_2_16; + else if (m_hist[3] == 16) + return TOTAL_ORDER_4_3_16; + + // Must sum to 16, so m_hist[3] isn't needed. + return g_total_ordering4_hash[m_hist[0] | (m_hist[1] << 4) | (m_hist[2] << 8)]; + } + }; + + struct hist3 + { + uint8_t m_hist[3]; + + hist3() + { + memset(m_hist, 0, sizeof(m_hist)); + } + + hist3(uint32_t i, uint32_t j, uint32_t k) + { + m_hist[0] = (uint8_t)i; + m_hist[1] = (uint8_t)j; + m_hist[2] = (uint8_t)k; + } + + inline bool operator== (const hist3& h) const + { + if (m_hist[0] != h.m_hist[0]) return false; + if (m_hist[1] != h.m_hist[1]) return false; + if (m_hist[2] != h.m_hist[2]) return false; + return true; + } + + inline bool any_16() const + { + return (m_hist[0] == 16) || (m_hist[1] == 16) || (m_hist[2] == 16); + } + + inline uint32_t lookup_total_ordering_index() const + { + if (m_hist[0] == 16) + return TOTAL_ORDER_3_0_16; + else if (m_hist[1] == 16) + return TOTAL_ORDER_3_1_16; + else if (m_hist[2] == 16) + return TOTAL_ORDER_3_2_16; + + // Must sum to 16, so m_hist[2] isn't needed. + return g_total_ordering3_hash[m_hist[0] | (m_hist[1] << 4)]; + } + }; + + struct bc1_match_entry + { + uint8_t m_hi; + uint8_t m_lo; + uint8_t m_e; + }; + + static bc1_approx_mode g_bc1_approx_mode; + static bc1_match_entry g_bc1_match5_equals_1[256], g_bc1_match6_equals_1[256]; + static bc1_match_entry g_bc1_match5_half[256], g_bc1_match6_half[256]; + + [[maybe_unused]] static inline int scale_5_to_8(int v) { return (v << 3) | (v >> 2); } + [[maybe_unused]] static inline int scale_6_to_8(int v) { return (v << 2) | (v >> 4); } + + // v0, v1 = unexpanded DXT1 endpoint values (5/6-bits) + // c0, c1 = expanded DXT1 endpoint values (8-bits) + static inline int interp_5_6_ideal(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 2 + c1) / 3; } + static inline int interp_5_6_ideal_round(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 2 + c1 + 1) / 3; } + static inline int interp_half_5_6_ideal(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 + c1) / 2; } + + static inline int interp_5_nv(int v0, int v1) { assert(v0 < 32 && v1 < 32); return ((2 * v0 + v1) * 22) / 8; } + static inline int interp_6_nv(int c0, int c1) { assert(c0 < 256 && c1 < 256); const int gdiff = c1 - c0; return (256 * c0 + (gdiff / 4) + 128 + gdiff * 80) / 256; } + + static inline int interp_half_5_nv(int v0, int v1) { assert(v0 < 32 && v1 < 32); return ((v0 + v1) * 33) / 8; } + static inline int interp_half_6_nv(int c0, int c1) { assert(c0 < 256 && c1 < 256); const int gdiff = c1 - c0; return (256 * c0 + gdiff / 4 + 128 + gdiff * 128) / 256; } + + static inline int interp_5_6_amd(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 43 + c1 * 21 + 32) >> 6; } + static inline int interp_half_5_6_amd(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 + c1 + 1) >> 1; } + + static inline int interp_5(int v0, int v1, int c0, int c1, bc1_approx_mode mode) + { + assert(scale_5_to_8(v0) == c0 && scale_5_to_8(v1) == c1); + switch (mode) + { + case bc1_approx_mode::cBC1NVidia: return interp_5_nv(v0, v1); + case bc1_approx_mode::cBC1AMD: return interp_5_6_amd(c0, c1); + default: + case bc1_approx_mode::cBC1Ideal: return interp_5_6_ideal(c0, c1); + case bc1_approx_mode::cBC1IdealRound4: return interp_5_6_ideal_round(c0, c1); + } + } + + static inline int interp_6(int v0, int v1, int c0, int c1, bc1_approx_mode mode) + { + (void)v0; (void)v1; + assert(scale_6_to_8(v0) == c0 && scale_6_to_8(v1) == c1); + switch (mode) + { + case bc1_approx_mode::cBC1NVidia: return interp_6_nv(c0, c1); + case bc1_approx_mode::cBC1AMD: return interp_5_6_amd(c0, c1); + default: + case bc1_approx_mode::cBC1Ideal: return interp_5_6_ideal(c0, c1); + case bc1_approx_mode::cBC1IdealRound4: return interp_5_6_ideal_round(c0, c1); + } + } + + static inline int interp_half_5(int v0, int v1, int c0, int c1, bc1_approx_mode mode) + { + assert(scale_5_to_8(v0) == c0 && scale_5_to_8(v1) == c1); + switch (mode) + { + case bc1_approx_mode::cBC1NVidia: return interp_half_5_nv(v0, v1); + case bc1_approx_mode::cBC1AMD: return interp_half_5_6_amd(c0, c1); + case bc1_approx_mode::cBC1Ideal: + case bc1_approx_mode::cBC1IdealRound4: + default: + return interp_half_5_6_ideal(c0, c1); + } + } + + static inline int interp_half_6(int v0, int v1, int c0, int c1, bc1_approx_mode mode) + { + (void)v0; (void)v1; + assert(scale_6_to_8(v0) == c0 && scale_6_to_8(v1) == c1); + switch (mode) + { + case bc1_approx_mode::cBC1NVidia: return interp_half_6_nv(c0, c1); + case bc1_approx_mode::cBC1AMD: return interp_half_5_6_amd(c0, c1); + case bc1_approx_mode::cBC1Ideal: + case bc1_approx_mode::cBC1IdealRound4: + default: + return interp_half_5_6_ideal(c0, c1); + } + } + + static void prepare_bc1_single_color_table_half(bc1_match_entry* pTable, const uint8_t* pExpand, int size, bc1_approx_mode mode) + { + for (int i = 0; i < 256; i++) + { + int lowest_e = 256; + for (int lo = 0; lo < size; lo++) + { + const int lo_e = pExpand[lo]; + + for (int hi = 0; hi < size; hi++) + { + const int hi_e = pExpand[hi]; + + const int v = (size == 32) ? interp_half_5(hi, lo, hi_e, lo_e, mode) : interp_half_6(hi, lo, hi_e, lo_e, mode); + + int e = iabs(v - i); + + // We only need to factor in 3% error in BC1 ideal mode. + if ((mode == bc1_approx_mode::cBC1Ideal) || (mode == bc1_approx_mode::cBC1IdealRound4)) + e += (iabs(hi_e - lo_e) * 3) / 100; + + // Favor equal endpoints, for lower error on actual GPU's which approximate the interpolation. + if ((e < lowest_e) || ((e == lowest_e) && (lo == hi))) + { + pTable[i].m_hi = static_cast(hi); + pTable[i].m_lo = static_cast(lo); + + assert(e <= UINT8_MAX); + pTable[i].m_e = static_cast(e); + + lowest_e = e; + } + + } // hi + } // lo + } + } + + static void prepare_bc1_single_color_table(bc1_match_entry* pTable, const uint8_t* pExpand, int size, bc1_approx_mode mode) + { + for (int i = 0; i < 256; i++) + { + int lowest_e = 256; + for (int lo = 0; lo < size; lo++) + { + const int lo_e = pExpand[lo]; + + for (int hi = 0; hi < size; hi++) + { + const int hi_e = pExpand[hi]; + + const int v = (size == 32) ? interp_5(hi, lo, hi_e, lo_e, mode) : interp_6(hi, lo, hi_e, lo_e, mode); + + int e = iabs(v - i); + + if ((mode == bc1_approx_mode::cBC1Ideal) || (mode == bc1_approx_mode::cBC1IdealRound4)) + e += (iabs(hi_e - lo_e) * 3) / 100; + + // Favor equal endpoints, for lower error on actual GPU's which approximate the interpolation. + if ((e < lowest_e) || ((e == lowest_e) && (lo == hi))) + { + pTable[i].m_hi = static_cast(hi); + pTable[i].m_lo = static_cast(lo); + + assert(e <= UINT8_MAX); + pTable[i].m_e = static_cast(e); + + lowest_e = e; + } + + } // hi + } // lo + } + } + + // This table is: 9 * (w * w), 9 * ((1.0f - w) * w), 9 * ((1.0f - w) * (1.0f - w)) + // where w is [0,1/3,2/3,1]. 9 is the perfect multiplier. + static const uint32_t g_weight_vals4[4] = { 0x000009, 0x010204, 0x040201, 0x090000 }; + + // multiplier is 4 for 3-color + static const uint32_t g_weight_vals3[3] = { 0x000004, 0x040000, 0x010101 }; + + static inline void compute_selector_factors4(const hist4& h, float& iz00, float& iz10, float& iz11) + { + uint32_t weight_accum = 0; + for (uint32_t sel = 0; sel < 4; sel++) + weight_accum += g_weight_vals4[sel] * h.m_hist[sel]; + + float z00 = (float)((weight_accum >> 16) & 0xFF); + float z10 = (float)((weight_accum >> 8) & 0xFF); + float z11 = (float)(weight_accum & 0xFF); + float z01 = z10; + + float det = z00 * z11 - z01 * z10; + if (fabs(det) < 1e-8f) + det = 0.0f; + else + det = (3.0f / 255.0f) / det; + + iz00 = z11 * det; + iz10 = -z10 * det; + iz11 = z00 * det; + } + + static inline void compute_selector_factors3(const hist3& h, float& iz00, float& iz10, float& iz11) + { + uint32_t weight_accum = 0; + for (uint32_t sel = 0; sel < 3; sel++) + weight_accum += g_weight_vals3[sel] * h.m_hist[sel]; + + float z00 = (float)((weight_accum >> 16) & 0xFF); + float z10 = (float)((weight_accum >> 8) & 0xFF); + float z11 = (float)(weight_accum & 0xFF); + float z01 = z10; + + float det = z00 * z11 - z01 * z10; + if (fabs(det) < 1e-8f) + det = 0.0f; + else + det = (2.0f / 255.0f) / det; + + iz00 = z11 * det; + iz10 = -z10 * det; + iz11 = z00 * det; + } + + [[maybe_unused]] static bool g_initialized; + + void init(bc1_approx_mode mode) + { + g_bc1_approx_mode = mode; + + uint8_t bc1_expand5[32]; + for (int i = 0; i < 32; i++) + bc1_expand5[i] = static_cast((i << 3) | (i >> 2)); + prepare_bc1_single_color_table(g_bc1_match5_equals_1, bc1_expand5, 32, mode); + prepare_bc1_single_color_table_half(g_bc1_match5_half, bc1_expand5, 32, mode); + + uint8_t bc1_expand6[64]; + for (int i = 0; i < 64; i++) + bc1_expand6[i] = static_cast((i << 2) | (i >> 4)); + prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, mode); + prepare_bc1_single_color_table_half(g_bc1_match6_half, bc1_expand6, 64, mode); + + for (uint32_t i = 0; i < NUM_UNIQUE_TOTAL_ORDERINGS4; i++) + { + hist4 h; + h.m_hist[0] = (uint8_t)g_unique_total_orders4[i][0]; + h.m_hist[1] = (uint8_t)g_unique_total_orders4[i][1]; + h.m_hist[2] = (uint8_t)g_unique_total_orders4[i][2]; + h.m_hist[3] = (uint8_t)g_unique_total_orders4[i][3]; + + if (!h.any_16()) + { + const uint32_t index = h.m_hist[0] | (h.m_hist[1] << 4) | (h.m_hist[2] << 8); + assert(index < 4096); + g_total_ordering4_hash[index] = (uint16_t)i; + } + + compute_selector_factors4(h, g_selector_factors4[i][0], g_selector_factors4[i][1], g_selector_factors4[i][2]); + } + + for (uint32_t i = 0; i < NUM_UNIQUE_TOTAL_ORDERINGS3; i++) + { + hist3 h; + h.m_hist[0] = (uint8_t)g_unique_total_orders3[i][0]; + h.m_hist[1] = (uint8_t)g_unique_total_orders3[i][1]; + h.m_hist[2] = (uint8_t)g_unique_total_orders3[i][2]; + + if (!h.any_16()) + { + const uint32_t index = h.m_hist[0] | (h.m_hist[1] << 4); + assert(index < 256); + g_total_ordering3_hash[index] = (uint16_t)i; + } + + compute_selector_factors3(h, g_selector_factors3[i][0], g_selector_factors3[i][1], g_selector_factors3[i][2]); + } + + g_initialized = true; + } + + void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb, bool allow_3color) + { + bc1_block* pDst_block = static_cast(pDst); + + uint32_t mask = 0xAA; + int max16 = -1, min16 = 0; + + if (allow_3color) + { + const uint32_t err4 = g_bc1_match5_equals_1[fr].m_e + g_bc1_match6_equals_1[fg].m_e + g_bc1_match5_equals_1[fb].m_e; + const uint32_t err3 = g_bc1_match5_half[fr].m_e + g_bc1_match6_half[fg].m_e + g_bc1_match5_half[fb].m_e; + + if (err3 < err4) + { + max16 = (g_bc1_match5_half[fr].m_hi << 11) | (g_bc1_match6_half[fg].m_hi << 5) | g_bc1_match5_half[fb].m_hi; + min16 = (g_bc1_match5_half[fr].m_lo << 11) | (g_bc1_match6_half[fg].m_lo << 5) | g_bc1_match5_half[fb].m_lo; + + if (max16 > min16) + std::swap(max16, min16); + } + } + + if (max16 == -1) + { + max16 = (g_bc1_match5_equals_1[fr].m_hi << 11) | (g_bc1_match6_equals_1[fg].m_hi << 5) | g_bc1_match5_equals_1[fb].m_hi; + min16 = (g_bc1_match5_equals_1[fr].m_lo << 11) | (g_bc1_match6_equals_1[fg].m_lo << 5) | g_bc1_match5_equals_1[fb].m_lo; + + if (min16 == max16) + { + // Always forbid 3 color blocks + // This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's. + mask = 0; + + // Make l > h + if (min16 > 0) + min16--; + else + { + // l = h = 0 + assert(min16 == max16 && max16 == 0); + + max16 = 1; + min16 = 0; + mask = 0x55; + } + + assert(max16 > min16); + } + + if (max16 < min16) + { + std::swap(max16, min16); + mask ^= 0x55; + } + } + + pDst_block->set_low_color(static_cast(max16)); + pDst_block->set_high_color(static_cast(min16)); + pDst_block->m_selectors[0] = static_cast(mask); + pDst_block->m_selectors[1] = static_cast(mask); + pDst_block->m_selectors[2] = static_cast(mask); + pDst_block->m_selectors[3] = static_cast(mask); + } + + static const float g_midpoint5[32] = { .015686f, .047059f, .078431f, .111765f, .145098f, .176471f, .207843f, .241176f, .274510f, .305882f, .337255f, .370588f, .403922f, .435294f, .466667f, .5f, .533333f, .564706f, .596078f, .629412f, .662745f, .694118f, .725490f, .758824f, .792157f, .823529f, .854902f, .888235f, .921569f, .952941f, .984314f, 1e+37f }; + static const float g_midpoint6[64] = { .007843f, .023529f, .039216f, .054902f, .070588f, .086275f, .101961f, .117647f, .133333f, .149020f, .164706f, .180392f, .196078f, .211765f, .227451f, .245098f, .262745f, .278431f, .294118f, .309804f, .325490f, .341176f, .356863f, .372549f, .388235f, .403922f, .419608f, .435294f, .450980f, .466667f, .482353f, .500000f, .517647f, .533333f, .549020f, .564706f, .580392f, .596078f, .611765f, .627451f, .643137f, .658824f, .674510f, .690196f, .705882f, .721569f, .737255f, .754902f, .772549f, .788235f, .803922f, .819608f, .835294f, .850980f, .866667f, .882353f, .898039f, .913725f, .929412f, .945098f, .960784f, .976471f, .992157f, 1e+37f }; + + struct vec3F { float c[3]; }; + + static inline void compute_least_squares_endpoints4_rgb( + vec3F* pXl, vec3F* pXh, + int total_r, int total_g, int total_b, + float iz00, float iz10, float iz11, + uint32_t s, const uint32_t r_sum[17], const uint32_t g_sum[17], const uint32_t b_sum[17]) + { + const float iz01 = iz10; + + const uint32_t f1 = g_unique_total_orders4[s][0]; + const uint32_t f2 = g_unique_total_orders4[s][0] + g_unique_total_orders4[s][1]; + const uint32_t f3 = g_unique_total_orders4[s][0] + g_unique_total_orders4[s][1] + g_unique_total_orders4[s][2]; + uint32_t uq00_r = (r_sum[f2] - r_sum[f1]) + (r_sum[f3] - r_sum[f2]) * 2 + (r_sum[16] - r_sum[f3]) * 3; + uint32_t uq00_g = (g_sum[f2] - g_sum[f1]) + (g_sum[f3] - g_sum[f2]) * 2 + (g_sum[16] - g_sum[f3]) * 3; + uint32_t uq00_b = (b_sum[f2] - b_sum[f1]) + (b_sum[f3] - b_sum[f2]) * 2 + (b_sum[16] - b_sum[f3]) * 3; + + float q10_r = (float)(total_r * 3 - uq00_r); + float q10_g = (float)(total_g * 3 - uq00_g); + float q10_b = (float)(total_b * 3 - uq00_b); + + pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; + pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; + + pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; + pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; + + pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; + pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; + } + + static inline bool compute_least_squares_endpoints4_rgb(const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh, int total_r, int total_g, int total_b) + { + uint32_t uq00_r = 0, uq00_g = 0, uq00_b = 0; + uint32_t weight_accum = 0; + for (uint32_t i = 0; i < 16; i++) + { + const uint8_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2]; + const uint8_t sel = pSelectors[i]; + + weight_accum += g_weight_vals4[sel]; + uq00_r += sel * r; + uq00_g += sel * g; + uq00_b += sel * b; + } + + int q10_r = total_r * 3 - uq00_r; + int q10_g = total_g * 3 - uq00_g; + int q10_b = total_b * 3 - uq00_b; + + float z00 = (float)((weight_accum >> 16) & 0xFF); + float z10 = (float)((weight_accum >> 8) & 0xFF); + float z11 = (float)(weight_accum & 0xFF); + float z01 = z10; + + float det = z00 * z11 - z01 * z10; + if (fabs(det) < 1e-8f) + return false; + + det = (3.0f / 255.0f) / det; + + float iz00, iz01, iz10, iz11; + iz00 = z11 * det; + iz01 = -z01 * det; + iz10 = -z10 * det; + iz11 = z00 * det; + + pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; + pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; + + pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; + pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; + + pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; + pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; + + return true; + } + + static inline void compute_least_squares_endpoints3_rgb( + vec3F* pXl, vec3F* pXh, + int total_r, int total_g, int total_b, + float iz00, float iz10, float iz11, + uint32_t s, const uint32_t r_sum[17], const uint32_t g_sum[17], const uint32_t b_sum[17]) + { + const float iz01 = iz10; + + // Compensates for BC1 3-color ordering, which is selector 0, 2, 1 + const uint32_t f1 = g_unique_total_orders3[s][0]; + const uint32_t f2 = g_unique_total_orders3[s][0] + g_unique_total_orders3[s][2]; + uint32_t uq00_r = (r_sum[16] - r_sum[f2]) * 2 + (r_sum[f2] - r_sum[f1]); + uint32_t uq00_g = (g_sum[16] - g_sum[f2]) * 2 + (g_sum[f2] - g_sum[f1]); + uint32_t uq00_b = (b_sum[16] - b_sum[f2]) * 2 + (b_sum[f2] - b_sum[f1]); + + float q10_r = (float)(total_r * 2 - uq00_r); + float q10_g = (float)(total_g * 2 - uq00_g); + float q10_b = (float)(total_b * 2 - uq00_b); + + pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; + pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; + + pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; + pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; + + pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; + pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; + } + + static inline bool compute_least_squares_endpoints3_rgb(bool use_black, const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh) + { + int uq00_r = 0, uq00_g = 0, uq00_b = 0; + uint32_t weight_accum = 0; + int total_r = 0, total_g = 0, total_b = 0; + for (uint32_t i = 0; i < 16; i++) + { + const uint8_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2]; + if (use_black) + { + if ((r | g | b) < 4) + continue; + } + + const uint8_t sel = pSelectors[i]; + assert(sel <= 3); + if (sel == 3) + continue; + + weight_accum += g_weight_vals3[sel]; + + static const uint8_t s_tran[3] = { 0, 2, 1 }; + const uint8_t tsel = s_tran[sel]; + uq00_r += tsel * r; + uq00_g += tsel * g; + uq00_b += tsel * b; + + total_r += r; + total_g += g; + total_b += b; + } + + int q10_r = total_r * 2 - uq00_r; + int q10_g = total_g * 2 - uq00_g; + int q10_b = total_b * 2 - uq00_b; + + float z00 = (float)((weight_accum >> 16) & 0xFF); + float z10 = (float)((weight_accum >> 8) & 0xFF); + float z11 = (float)(weight_accum & 0xFF); + float z01 = z10; + + float det = z00 * z11 - z01 * z10; + if (fabs(det) < 1e-8f) + return false; + + det = (2.0f / 255.0f) / det; + + float iz00, iz01, iz10, iz11; + iz00 = z11 * det; + iz01 = -z01 * det; + iz10 = -z10 * det; + iz11 = z00 * det; + + pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; + pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; + + pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; + pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; + + pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; + pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; + + return true; + } + + static inline void bc1_get_block_colors4(uint32_t block_r[4], uint32_t block_g[4], uint32_t block_b[4], uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb) + { + block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2); + block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2); + + if (g_bc1_approx_mode == bc1_approx_mode::cBC1Ideal) + { + block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3; + block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3; + } + else if (g_bc1_approx_mode == bc1_approx_mode::cBC1IdealRound4) + { + block_r[1] = (block_r[0] * 2 + block_r[3] + 1) / 3; block_g[1] = (block_g[0] * 2 + block_g[3] + 1) / 3; block_b[1] = (block_b[0] * 2 + block_b[3] + 1) / 3; + block_r[2] = (block_r[3] * 2 + block_r[0] + 1) / 3; block_g[2] = (block_g[3] * 2 + block_g[0] + 1) / 3; block_b[2] = (block_b[3] * 2 + block_b[0] + 1) / 3; + } + else if (g_bc1_approx_mode == bc1_approx_mode::cBC1AMD) + { + block_r[1] = interp_5_6_amd(block_r[0], block_r[3]); block_g[1] = interp_5_6_amd(block_g[0], block_g[3]); block_b[1] = interp_5_6_amd(block_b[0], block_b[3]); + block_r[2] = interp_5_6_amd(block_r[3], block_r[0]); block_g[2] = interp_5_6_amd(block_g[3], block_g[0]); block_b[2] = interp_5_6_amd(block_b[3], block_b[0]); + } + else + { + block_r[1] = interp_5_nv(lr, hr); block_g[1] = interp_6_nv(block_g[0], block_g[3]); block_b[1] = interp_5_nv(lb, hb); + block_r[2] = interp_5_nv(hr, lr); block_g[2] = interp_6_nv(block_g[3], block_g[0]); block_b[2] = interp_5_nv(hb, lb); + } + } + + static inline void bc1_get_block_colors3(uint32_t block_r[3], uint32_t block_g[3], uint32_t block_b[3], uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb) + { + block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2); + block_r[1] = (hr << 3) | (hr >> 2); block_g[1] = (hg << 2) | (hg >> 4); block_b[1] = (hb << 3) | (hb >> 2); + + if ((g_bc1_approx_mode == bc1_approx_mode::cBC1Ideal) || (g_bc1_approx_mode == bc1_approx_mode::cBC1IdealRound4)) + { + block_r[2] = (block_r[0] + block_r[1]) / 2; block_g[2] = (block_g[0] + block_g[1]) / 2; block_b[2] = (block_b[0] + block_b[1]) / 2; + } + else if (g_bc1_approx_mode == bc1_approx_mode::cBC1AMD) + { + block_r[2] = interp_half_5_6_amd(block_r[0], block_r[1]); block_g[2] = interp_half_5_6_amd(block_g[0], block_g[1]); block_b[2] = interp_half_5_6_amd(block_b[0], block_b[1]); + } + else + { + block_r[2] = interp_half_5_nv(lr, hr); block_g[2] = interp_half_6_nv(block_g[0], block_g[1]); block_b[2] = interp_half_5_nv(lb, hb); + } + } + + static inline void bc1_find_sels4_noerr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], const uint8_t* pForce_selectors) + { + if (pForce_selectors) + { + memcpy(sels, pForce_selectors, 16); + return; + } + + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0]; + + int dots[4]; + for (uint32_t i = 0; i < 4; i++) + dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab; + + int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3]; + + ar *= 2; ag *= 2; ab *= 2; + + static const uint8_t s_sels[4] = { 3, 2, 1, 0 }; + + for (uint32_t i = 0; i < 16; i += 4) + { + const int d0 = pSrc_pixels[i + 0].r * ar + pSrc_pixels[i + 0].g * ag + pSrc_pixels[i + 0].b * ab; + const int d1 = pSrc_pixels[i + 1].r * ar + pSrc_pixels[i + 1].g * ag + pSrc_pixels[i + 1].b * ab; + const int d2 = pSrc_pixels[i + 2].r * ar + pSrc_pixels[i + 2].g * ag + pSrc_pixels[i + 2].b * ab; + const int d3 = pSrc_pixels[i + 3].r * ar + pSrc_pixels[i + 3].g * ag + pSrc_pixels[i + 3].b * ab; + + sels[i + 0] = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)]; + sels[i + 1] = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)]; + sels[i + 2] = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)]; + sels[i + 3] = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)]; + } + } + + static inline uint32_t bc1_find_sels4_fasterr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) + { + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0]; + + int dots[4]; + for (uint32_t i = 0; i < 4; i++) + dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab; + + int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3]; + + ar *= 2; ag *= 2; ab *= 2; + + static const uint8_t s_sels[4] = { 3, 2, 1, 0 }; + + uint32_t total_err = 0; + + for (uint32_t i = 0; i < 16; i += 4) + { + const int d0 = pSrc_pixels[i + 0].r * ar + pSrc_pixels[i + 0].g * ag + pSrc_pixels[i + 0].b * ab; + const int d1 = pSrc_pixels[i + 1].r * ar + pSrc_pixels[i + 1].g * ag + pSrc_pixels[i + 1].b * ab; + const int d2 = pSrc_pixels[i + 2].r * ar + pSrc_pixels[i + 2].g * ag + pSrc_pixels[i + 2].b * ab; + const int d3 = pSrc_pixels[i + 3].r * ar + pSrc_pixels[i + 3].g * ag + pSrc_pixels[i + 3].b * ab; + + uint8_t sel0 = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)]; + uint8_t sel1 = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)]; + uint8_t sel2 = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)]; + uint8_t sel3 = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)]; + + sels[i + 0] = sel0; + sels[i + 1] = sel1; + sels[i + 2] = sel2; + sels[i + 3] = sel3; + + total_err += squarei(pSrc_pixels[i + 0].r - block_r[sel0]) + squarei(pSrc_pixels[i + 0].g - block_g[sel0]) + squarei(pSrc_pixels[i + 0].b - block_b[sel0]); + total_err += squarei(pSrc_pixels[i + 1].r - block_r[sel1]) + squarei(pSrc_pixels[i + 1].g - block_g[sel1]) + squarei(pSrc_pixels[i + 1].b - block_b[sel1]); + total_err += squarei(pSrc_pixels[i + 2].r - block_r[sel2]) + squarei(pSrc_pixels[i + 2].g - block_g[sel2]) + squarei(pSrc_pixels[i + 2].b - block_b[sel2]); + total_err += squarei(pSrc_pixels[i + 3].r - block_r[sel3]) + squarei(pSrc_pixels[i + 3].g - block_g[sel3]) + squarei(pSrc_pixels[i + 3].b - block_b[sel3]); + + if (total_err >= cur_err) + break; + } + + return total_err; + } + + static inline uint32_t bc1_find_sels4_check2_err(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) + { + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + int dr = block_r[3] - block_r[0], dg = block_g[3] - block_g[0], db = block_b[3] - block_b[0]; + + const float f = 4.0f / (float)(squarei(dr) + squarei(dg) + squarei(db) + .00000125f); + + uint32_t total_err = 0; + + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r; + const int g = pSrc_pixels[i].g; + const int b = pSrc_pixels[i].b; + + int sel = (int)((float)((r - (int)block_r[0]) * dr + (g - (int)block_g[0]) * dg + (b - (int)block_b[0]) * db) * f + .5f); + sel = clampi(sel, 1, 3); + + uint32_t err0 = squarei((int)block_r[sel - 1] - (int)r) + squarei((int)block_g[sel - 1] - (int)g) + squarei((int)block_b[sel - 1] - (int)b); + uint32_t err1 = squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); + + int best_sel = sel; + uint32_t best_err = err1; + if (err0 == err1) + { + // Prefer non-interpolation + if ((best_sel - 1) == 0) + best_sel = 0; + } + else if (err0 < best_err) + { + best_sel = sel - 1; + best_err = err0; + } + + total_err += best_err; + + if (total_err >= cur_err) + break; + + sels[i] = (uint8_t)best_sel; + } + return total_err; + } + + static inline uint32_t bc1_find_sels4_fullerr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) + { + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + uint32_t total_err = 0; + + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r; + const int g = pSrc_pixels[i].g; + const int b = pSrc_pixels[i].b; + + uint32_t best_err = squarei((int)block_r[0] - (int)r) + squarei((int)block_g[0] - (int)g) + squarei((int)block_b[0] - (int)b); + uint8_t best_sel = 0; + + for (uint32_t j = 1; (j < 4) && best_err; j++) + { + uint32_t err = squarei((int)block_r[j] - (int)r) + squarei((int)block_g[j] - (int)g) + squarei((int)block_b[j] - (int)b); + if ((err < best_err) || ((err == best_err) && (j == 3))) + { + best_err = err; + best_sel = (uint8_t)j; + } + } + + total_err += best_err; + + if (total_err >= cur_err) + break; + + sels[i] = (uint8_t)best_sel; + } + return total_err; + } + + static inline uint32_t bc1_find_sels4(uint32_t flags, const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err, const uint8_t* pForce_selectors) + { + uint32_t err; + + if (pForce_selectors) + { + memcpy(sels, pForce_selectors, 16); + + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + err = 0; + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r; + const int g = pSrc_pixels[i].g; + const int b = pSrc_pixels[i].b; + + const uint32_t sel = pForce_selectors[i]; + assert(sel <= 3); + + err += squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); + } + } + else + { + if (flags & cEncodeBC1UseFasterMSEEval) + err = bc1_find_sels4_fasterr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); + else if (flags & cEncodeBC1UseFullMSEEval) + err = bc1_find_sels4_fullerr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); + else + err = bc1_find_sels4_check2_err(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); + } + + return err; + } + + static inline uint32_t bc1_find_sels3_fullerr(bool use_black, const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err, const uint8_t* pForce_selectors) + { + uint32_t block_r[4], block_g[4], block_b[4]; + bc1_get_block_colors3(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + uint32_t total_err = 0; + + if (pForce_selectors) + { + memcpy(sels, pForce_selectors, 16); + + //uint32_t block_r[4], block_g[4], block_b[4]; + //bc1_get_block_colors3(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); + + block_r[3] = 0; block_g[3] = 0; block_b[3] = 0; + + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r; + const int g = pSrc_pixels[i].g; + const int b = pSrc_pixels[i].b; + + const uint32_t sel = pForce_selectors[i]; + assert(sel <= 3); + + total_err += squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); + } + } + else + { + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r; + const int g = pSrc_pixels[i].g; + const int b = pSrc_pixels[i].b; + + uint32_t best_err = squarei((int)block_r[0] - (int)r) + squarei((int)block_g[0] - (int)g) + squarei((int)block_b[0] - (int)b); + uint32_t best_sel = 0; + + uint32_t err1 = squarei((int)block_r[1] - (int)r) + squarei((int)block_g[1] - (int)g) + squarei((int)block_b[1] - (int)b); + if (err1 < best_err) + { + best_err = err1; + best_sel = 1; + } + + uint32_t err2 = squarei((int)block_r[2] - (int)r) + squarei((int)block_g[2] - (int)g) + squarei((int)block_b[2] - (int)b); + if (err2 < best_err) + { + best_err = err2; + best_sel = 2; + } + + if (use_black) + { + uint32_t err3 = squarei(r) + squarei(g) + squarei(b); + if (err3 < best_err) + { + best_err = err3; + best_sel = 3; + } + } + + total_err += best_err; + if (total_err >= cur_err) + return total_err; + + sels[i] = (uint8_t)best_sel; + } + } + + return total_err; + } + + static inline void precise_round_565(const vec3F& xl, const vec3F& xh, + int& trial_lr, int& trial_lg, int& trial_lb, + int& trial_hr, int& trial_hg, int& trial_hb) + { + trial_lr = (int)(xl.c[0] * 31.0f); + trial_lg = (int)(xl.c[1] * 63.0f); + trial_lb = (int)(xl.c[2] * 31.0f); + + trial_hr = (int)(xh.c[0] * 31.0f); + trial_hg = (int)(xh.c[1] * 63.0f); + trial_hb = (int)(xh.c[2] * 31.0f); + + if ((uint32_t)(trial_lr | trial_lb | trial_hr | trial_hb) > 31U) + { + trial_lr = ((uint32_t)trial_lr > 31U) ? (~trial_lr >> 31) & 31 : trial_lr; + trial_hr = ((uint32_t)trial_hr > 31U) ? (~trial_hr >> 31) & 31 : trial_hr; + + trial_lb = ((uint32_t)trial_lb > 31U) ? (~trial_lb >> 31) & 31 : trial_lb; + trial_hb = ((uint32_t)trial_hb > 31U) ? (~trial_hb >> 31) & 31 : trial_hb; + } + + if ((uint32_t)(trial_lg | trial_hg) > 63U) + { + trial_lg = ((uint32_t)trial_lg > 63U) ? (~trial_lg >> 31) & 63 : trial_lg; + trial_hg = ((uint32_t)trial_hg > 63U) ? (~trial_hg >> 31) & 63 : trial_hg; + } + + trial_lr = (trial_lr + (xl.c[0] > g_midpoint5[trial_lr])) & 31; + trial_lg = (trial_lg + (xl.c[1] > g_midpoint6[trial_lg])) & 63; + trial_lb = (trial_lb + (xl.c[2] > g_midpoint5[trial_lb])) & 31; + + trial_hr = (trial_hr + (xh.c[0] > g_midpoint5[trial_hr])) & 31; + trial_hg = (trial_hg + (xh.c[1] > g_midpoint6[trial_hg])) & 63; + trial_hb = (trial_hb + (xh.c[2] > g_midpoint5[trial_hb])) & 31; + } + + static inline void precise_round_565_noscale(vec3F xl, vec3F xh, + int& trial_lr, int& trial_lg, int& trial_lb, + int& trial_hr, int& trial_hg, int& trial_hb) + { + xl.c[0] *= 1.0f / 255.0f; + xl.c[1] *= 1.0f / 255.0f; + xl.c[2] *= 1.0f / 255.0f; + + xh.c[0] *= 1.0f / 255.0f; + xh.c[1] *= 1.0f / 255.0f; + xh.c[2] *= 1.0f / 255.0f; + + precise_round_565(xl, xh, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb); + } + + static inline void bc1_encode4(bc1_block* pDst_block, int lr, int lg, int lb, int hr, int hg, int hb, const uint8_t sels[16]) + { + uint32_t lc16 = bc1_block::pack_unscaled_color(lr, lg, lb); + uint32_t hc16 = bc1_block::pack_unscaled_color(hr, hg, hb); + + // Always forbid 3 color blocks + if (lc16 == hc16) + { + uint8_t mask = 0; + + // Make l > h + if (hc16 > 0) + hc16--; + else + { + // lc16 = hc16 = 0 + assert(lc16 == hc16 && hc16 == 0); + + hc16 = 0; + lc16 = 1; + mask = 0x55; // select hc16 + } + + assert(lc16 > hc16); + pDst_block->set_low_color(static_cast(lc16)); + pDst_block->set_high_color(static_cast(hc16)); + + pDst_block->m_selectors[0] = mask; + pDst_block->m_selectors[1] = mask; + pDst_block->m_selectors[2] = mask; + pDst_block->m_selectors[3] = mask; + } + else + { + uint8_t invert_mask = 0; + if (lc16 < hc16) + { + std::swap(lc16, hc16); + invert_mask = 0x55; + } + + assert(lc16 > hc16); + pDst_block->set_low_color((uint16_t)lc16); + pDst_block->set_high_color((uint16_t)hc16); + + uint32_t packed_sels = 0; + static const uint8_t s_sel_trans[4] = { 0, 2, 3, 1 }; + for (uint32_t i = 0; i < 16; i++) + packed_sels |= ((uint32_t)s_sel_trans[sels[i]] << (i * 2)); + + pDst_block->m_selectors[0] = (uint8_t)packed_sels ^ invert_mask; + pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8) ^ invert_mask; + pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16) ^ invert_mask; + pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24) ^ invert_mask; + } + } + + static inline void bc1_encode3(bc1_block* pDst_block, int lr, int lg, int lb, int hr, int hg, int hb, const uint8_t sels[16]) + { + uint32_t lc16 = bc1_block::pack_unscaled_color(lr, lg, lb); + uint32_t hc16 = bc1_block::pack_unscaled_color(hr, hg, hb); + + bool invert_flag = false; + if (lc16 > hc16) + { + std::swap(lc16, hc16); + invert_flag = true; + } + + assert(lc16 <= hc16); + + pDst_block->set_low_color((uint16_t)lc16); + pDst_block->set_high_color((uint16_t)hc16); + + uint32_t packed_sels = 0; + + if (invert_flag) + { + static const uint8_t s_sel_trans_inv[4] = { 1, 0, 2, 3 }; + + for (uint32_t i = 0; i < 16; i++) + packed_sels |= ((uint32_t)s_sel_trans_inv[sels[i]] << (i * 2)); + } + else + { + for (uint32_t i = 0; i < 16; i++) + packed_sels |= ((uint32_t)sels[i] << (i * 2)); + } + + pDst_block->m_selectors[0] = (uint8_t)packed_sels; + pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8); + pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16); + pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24); + } + + struct bc1_encode_results + { + int lr, lg, lb; + int hr, hg, hb; + uint8_t sels[16]; + bool m_3color; + }; + + static bool try_3color_block_useblack(const color32* pSrc_pixels, uint32_t flags, uint32_t& cur_err, bc1_encode_results& results, const uint8_t* pForce_selectors) + { + int total_r = 0, total_g = 0, total_b = 0; + int max_r = 0, max_g = 0, max_b = 0; + int min_r = 255, min_g = 255, min_b = 255; + int total_pixels = 0; + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; + if ((r | g | b) < 4) + continue; + + max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b); + min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b); + total_r += r; total_g += g; total_b += b; + + total_pixels++; + } + + if (!total_pixels) + return false; + + int half_total_pixels = total_pixels >> 1; + int avg_r = (total_r + half_total_pixels) / total_pixels; + int avg_g = (total_g + half_total_pixels) / total_pixels; + int avg_b = (total_b + half_total_pixels) / total_pixels; + + uint32_t low_c = 0, high_c = 0; + + int icov[6] = { 0, 0, 0, 0, 0, 0 }; + for (uint32_t i = 0; i < 16; i++) + { + int r = (int)pSrc_pixels[i].r; + int g = (int)pSrc_pixels[i].g; + int b = (int)pSrc_pixels[i].b; + + if ((r | g | b) < 4) + continue; + + r -= avg_r; + g -= avg_g; + b -= avg_b; + + icov[0] += r * r; + icov[1] += r * g; + icov[2] += r * b; + icov[3] += g * g; + icov[4] += g * b; + icov[5] += b * b; + } + + float cov[6]; + for (uint32_t i = 0; i < 6; i++) + cov[i] = (float)(icov[i]) * (1.0f / 255.0f); + + float xr = (float)(max_r - min_r); + float xg = (float)(max_g - min_g); + float xb = (float)(max_b - min_b); + + if (icov[2] < 0) + xr = -xr; + + if (icov[4] < 0) + xg = -xg; + + for (uint32_t power_iter = 0; power_iter < 4; power_iter++) + { + float r = xr * cov[0] + xg * cov[1] + xb * cov[2]; + float g = xr * cov[1] + xg * cov[3] + xb * cov[4]; + float b = xr * cov[2] + xg * cov[4] + xb * cov[5]; + xr = r; xg = g; xb = b; + } + + float k = maximum(fabsf(xr), fabsf(xg), fabsf(xb)); + int saxis_r = 306, saxis_g = 601, saxis_b = 117; + if (k >= 2) + { + float m = 1024.0f / k; + saxis_r = (int)(xr * m); + saxis_g = (int)(xg * m); + saxis_b = (int)(xb * m); + } + + int low_dot = INT_MAX, high_dot = INT_MIN; + for (uint32_t i = 0; i < 16; i++) + { + int r = (int)pSrc_pixels[i].r, g = (int)pSrc_pixels[i].g, b = (int)pSrc_pixels[i].b; + + if ((r | g | b) < 4) + continue; + + int dot = r * saxis_r + g * saxis_g + b * saxis_b; + if (dot < low_dot) + { + low_dot = dot; + low_c = i; + } + if (dot > high_dot) + { + high_dot = dot; + high_c = i; + } + } + + int lr = to_5(pSrc_pixels[low_c].r); + int lg = to_6(pSrc_pixels[low_c].g); + int lb = to_5(pSrc_pixels[low_c].b); + + int hr = to_5(pSrc_pixels[high_c].r); + int hg = to_6(pSrc_pixels[high_c].g); + int hb = to_5(pSrc_pixels[high_c].b); + + uint8_t trial_sels[16]; + uint32_t trial_err = bc1_find_sels3_fullerr(true, pSrc_pixels, lr, lg, lb, hr, hg, hb, trial_sels, UINT32_MAX, pForce_selectors); + + if (trial_err) + { + const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; + for (uint32_t trials = 0; trials < total_ls_passes; trials++) + { + vec3F xl, xh; + int lr2, lg2, lb2, hr2, hg2, hb2; + if (!compute_least_squares_endpoints3_rgb(true, pSrc_pixels, trial_sels, &xl, &xh)) + { + lr2 = g_bc1_match5_half[avg_r].m_hi; + lg2 = g_bc1_match6_half[avg_g].m_hi; + lb2 = g_bc1_match5_half[avg_b].m_hi; + + hr2 = g_bc1_match5_half[avg_r].m_lo; + hg2 = g_bc1_match6_half[avg_g].m_lo; + hb2 = g_bc1_match5_half[avg_b].m_lo; + } + else + { + precise_round_565(xl, xh, hr2, hg2, hb2, lr2, lg2, lb2); + } + + if ((lr == lr2) && (lg == lg2) && (lb == lb2) && (hr == hr2) && (hg == hg2) && (hb == hb2)) + break; + + uint8_t trial_sels2[16]; + uint32_t trial_err2 = bc1_find_sels3_fullerr(true, pSrc_pixels, lr2, lg2, lb2, hr2, hg2, hb2, trial_sels2, trial_err, pForce_selectors); + + if (trial_err2 < trial_err) + { + trial_err = trial_err2; + lr = lr2; lg = lg2; lb = lb2; + hr = hr2; hg = hg2; hb = hb2; + memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); + } + else + break; + } + } + + if (trial_err < cur_err) + { + results.m_3color = true; + results.lr = lr; + results.lg = lg; + results.lb = lb; + results.hr = hr; + results.hg = hg; + results.hb = hb; + memcpy(results.sels, trial_sels, 16); + + cur_err = trial_err; + + return true; + } + + return false; + } + + static bool try_3color_block(const color32* pSrc_pixels, uint32_t flags, uint32_t& cur_err, + int avg_r, int avg_g, int avg_b, int lr, int lg, int lb, int hr, int hg, int hb, int total_r, int total_g, int total_b, uint32_t total_orderings_to_try, + bc1_encode_results& results, const uint8_t* pForce_selectors) + { + if (pForce_selectors) + { + for (uint32_t i = 0; i < 16; i++) + if (pForce_selectors[i] == 3) + return false; + } + + uint8_t trial_sels[16]; + uint32_t trial_err = bc1_find_sels3_fullerr(false, pSrc_pixels, lr, lg, lb, hr, hg, hb, trial_sels, UINT32_MAX, pForce_selectors); + + if (trial_err) + { + const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; + for (uint32_t trials = 0; trials < total_ls_passes; trials++) + { + vec3F xl, xh; + int lr2, lg2, lb2, hr2, hg2, hb2; + if (!compute_least_squares_endpoints3_rgb(false, pSrc_pixels, trial_sels, &xl, &xh)) + { + lr2 = g_bc1_match5_half[avg_r].m_hi; + lg2 = g_bc1_match6_half[avg_g].m_hi; + lb2 = g_bc1_match5_half[avg_b].m_hi; + + hr2 = g_bc1_match5_half[avg_r].m_lo; + hg2 = g_bc1_match6_half[avg_g].m_lo; + hb2 = g_bc1_match5_half[avg_b].m_lo; + } + else + { + precise_round_565(xl, xh, hr2, hg2, hb2, lr2, lg2, lb2); + } + + if ((lr == lr2) && (lg == lg2) && (lb == lb2) && (hr == hr2) && (hg == hg2) && (hb == hb2)) + break; + + uint8_t trial_sels2[16]; + uint32_t trial_err2 = bc1_find_sels3_fullerr(false, pSrc_pixels, lr2, lg2, lb2, hr2, hg2, hb2, trial_sels2, trial_err, pForce_selectors); + + if (trial_err2 < trial_err) + { + trial_err = trial_err2; + lr = lr2; lg = lg2; lb = lb2; + hr = hr2; hg = hg2; hb = hb2; + memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); + } + else + break; + } + } + + if ((trial_err) && (flags & cEncodeBC1UseLikelyTotalOrderings) && (total_orderings_to_try)) + { + hist3 h; + for (uint32_t i = 0; i < 16; i++) + { + assert(trial_sels[i] < 3); + h.m_hist[trial_sels[i]]++; + } + + const uint32_t orig_total_order_index = h.lookup_total_ordering_index(); + + int r0, g0, b0, r3, g3, b3; + r0 = (lr << 3) | (lr >> 2); g0 = (lg << 2) | (lg >> 4); b0 = (lb << 3) | (lb >> 2); + r3 = (hr << 3) | (hr >> 2); g3 = (hg << 2) | (hg >> 4); b3 = (hb << 3) | (hb >> 2); + + int ar = r3 - r0, ag = g3 - g0, ab = b3 - b0; + + int dots[16]; + for (uint32_t i = 0; i < 16; i++) + { + int r = pSrc_pixels[i].r; + int g = pSrc_pixels[i].g; + int b = pSrc_pixels[i].b; + int d = 0x1000000 + (r * ar + g * ag + b * ab); + assert(d >= 0); + dots[i] = (d << 4) + i; + } + + std::sort(dots, dots + 16); + + uint32_t r_sum[17], g_sum[17], b_sum[17]; + uint32_t r = 0, g = 0, b = 0; + for (uint32_t i = 0; i < 16; i++) + { + const uint32_t p = dots[i] & 15; + + r_sum[i] = r; + g_sum[i] = g; + b_sum[i] = b; + + r += pSrc_pixels[p].r; + g += pSrc_pixels[p].g; + b += pSrc_pixels[p].b; + } + + r_sum[16] = total_r; + g_sum[16] = total_g; + b_sum[16] = total_b; + + const uint32_t q_total = (flags & cEncodeBC1Exhaustive) ? NUM_UNIQUE_TOTAL_ORDERINGS3 : std::min(total_orderings_to_try, MAX_TOTAL_ORDERINGS3); + for (uint32_t q = 0; q < q_total; q++) + { + const uint32_t s = (flags & cEncodeBC1Exhaustive) ? q : g_best_total_orderings3[orig_total_order_index][q]; + + int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; + + vec3F xl, xh; + + if ((s == TOTAL_ORDER_3_0_16) || (s == TOTAL_ORDER_3_1_16) || (s == TOTAL_ORDER_3_2_16)) + { + trial_lr = g_bc1_match5_half[avg_r].m_hi; + trial_lg = g_bc1_match6_half[avg_g].m_hi; + trial_lb = g_bc1_match5_half[avg_b].m_hi; + + trial_hr = g_bc1_match5_half[avg_r].m_lo; + trial_hg = g_bc1_match6_half[avg_g].m_lo; + trial_hb = g_bc1_match5_half[avg_b].m_lo; + } + else + { + compute_least_squares_endpoints3_rgb(&xl, &xh, total_r, total_g, total_b, + g_selector_factors3[s][0], g_selector_factors3[s][1], g_selector_factors3[s][2], s, r_sum, g_sum, b_sum); + + precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); + } + + uint8_t trial_sels2[16]; + uint32_t trial_err2 = bc1_find_sels3_fullerr(false, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels2, UINT32_MAX, pForce_selectors); + + if (trial_err2 < trial_err) + { + trial_err = trial_err2; + + lr = trial_lr; + lg = trial_lg; + lb = trial_lb; + + hr = trial_hr; + hg = trial_hg; + hb = trial_hb; + + memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); + } + + } // s + } + + if (trial_err < cur_err) + { + results.m_3color = true; + results.lr = lr; + results.lg = lg; + results.lb = lb; + results.hr = hr; + results.hg = hg; + results.hb = hb; + memcpy(results.sels, trial_sels, 16); + + cur_err = trial_err; + + return true; + } + + return false; + } + + void encode_bc1(uint32_t level, void* pDst, const uint8_t* pPixels, bool allow_3color, bool allow_transparent_texels_for_black, const uint8_t* pForce_selectors) + { + uint32_t flags = 0, total_orderings4 = 1, total_orderings3 = 1; + + static_assert(MAX_TOTAL_ORDERINGS3 >= 32, "MAX_TOTAL_ORDERINGS3 >= 32"); + static_assert(MAX_TOTAL_ORDERINGS4 >= 32, "MAX_TOTAL_ORDERINGS4 >= 32"); + + switch (level) + { + case 0: + // Faster/higher quality than stb_dxt default. + flags = cEncodeBC1BoundingBoxInt; + break; + case 1: + // Faster/higher quality than stb_dxt default. A bit higher average quality vs. mode 0. + flags = cEncodeBC1Use2DLS; + break; + case 2: + // On average mode 2 is a little weaker than modes 0/1, but it's stronger on outliers (very tough textures). + // Slightly stronger than stb_dxt. + flags = 0; + break; + case 3: + // Slightly stronger than stb_dxt HIGHQUAL. + flags = cEncodeBC1TwoLeastSquaresPasses; + break; + case 4: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1Use6PowerIters; + break; + default: + case 5: + // stb_dxt HIGHQUAL + permit 3 color (if it's enabled). + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + break; + case 6: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + break; + case 7: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 4; + break; + case 8: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 8; + break; + case 9: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 11; + total_orderings3 = 3; + break; + case 10: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 20; + total_orderings3 = 8; + break; + case 11: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 28; + total_orderings3 = 16; + break; + case 12: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 32; + total_orderings3 = 32; + break; + case 13: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (20 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 32; + total_orderings3 = 32; + break; + case 14: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (32 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 32; + total_orderings3 = 32; + break; + case 15: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (32 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = ((((32 + MAX_TOTAL_ORDERINGS4) / 2) + 32) / 2); + total_orderings3 = 32; + break; + case 16: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = (32 + MAX_TOTAL_ORDERINGS4) / 2; + total_orderings3 = 32; + break; + case 17: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = MAX_TOTAL_ORDERINGS4; + total_orderings3 = 32; + break; + case 18: + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | cEncodeBC1Iterative | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = MAX_TOTAL_ORDERINGS4; + total_orderings3 = 32; + break; + case 19: + // This hidden mode is *extremely* slow and abuses the encoder. It's just for testing/training. + flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | cEncodeBC1Exhaustive | cEncodeBC1Iterative | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; + flags |= (allow_3color ? (uint32_t)cEncodeBC1Use3ColorBlocks : 0u) | (allow_transparent_texels_for_black ? (uint32_t)cEncodeBC1Use3ColorBlocksForBlackPixels : 0u); + total_orderings4 = 32; + total_orderings3 = 32; + break; + } + + encode_bc1(pDst, pPixels, flags, total_orderings4, total_orderings3, pForce_selectors); + } + + static inline void encode_bc1_pick_initial(const color32* pSrc_pixels, uint32_t flags, bool grayscale_flag, + int min_r, int min_g, int min_b, int max_r, int max_g, int max_b, + int avg_r, int avg_g, int avg_b, int total_r, int total_g, int total_b, + int& lr, int& lg, int& lb, int& hr, int& hg, int& hb) + { + if (grayscale_flag) + { + const int fr = pSrc_pixels[0].r; + + // Grayscale blocks are a common enough case to specialize. + if ((max_r - min_r) < 2) + { + lr = lb = hr = hb = to_5(fr); + lg = hg = to_6(fr); + } + else + { + lr = lb = to_5(min_r); + lg = to_6(min_r); + + hr = hb = to_5(max_r); + hg = to_6(max_r); + } + } + else if (flags & cEncodeBC1Use2DLS) + { + // 2D Least Squares approach from Humus's example, with added inset and optimal rounding. + int big_chan = 0, min_chan_val = min_r, max_chan_val = max_r; + if ((max_g - min_g) > (max_chan_val - min_chan_val)) + big_chan = 1, min_chan_val = min_g, max_chan_val = max_g; + + if ((max_b - min_b) > (max_chan_val - min_chan_val)) + big_chan = 2, min_chan_val = min_b, max_chan_val = max_b; + + int sum_xy_r = 0, sum_xy_g = 0, sum_xy_b = 0; + vec3F l, h; + if (big_chan == 0) + { + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; + sum_xy_r += r * r, sum_xy_g += r * g, sum_xy_b += r * b; + } + + int sum_x = total_r; + int sum_x2 = sum_xy_r; + + float div = (float)(16 * sum_x2 - sum_x * sum_x); + float b_y = 0.0f, b_z = 0.0f; + if (fabs(div) > 1e-8f) + { + div = 1.0f / div; + b_y = (16 * sum_xy_g - sum_x * total_g) * div; + b_z = (16 * sum_xy_b - sum_x * total_b) * div; + } + + float a_y = (total_g - b_y * sum_x) / 16.0f; + float a_z = (total_b - b_z * sum_x) / 16.0f; + + l.c[1] = a_y + b_y * min_chan_val; + l.c[2] = a_z + b_z * min_chan_val; + + h.c[1] = a_y + b_y * max_chan_val; + h.c[2] = a_z + b_z * max_chan_val; + + float dg = (h.c[1] - l.c[1]); + float db = (h.c[2] - l.c[2]); + + h.c[1] = l.c[1] + dg * (15.0f / 16.0f); + h.c[2] = l.c[2] + db * (15.0f / 16.0f); + + l.c[1] = l.c[1] + dg * (1.0f / 16.0f); + l.c[2] = l.c[2] + db * (1.0f / 16.0f); + + float d = (float)(max_chan_val - min_chan_val); + float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); + float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); + + l.c[0] = fmin_chan_val; + h.c[0] = fmax_chan_val; + } + else if (big_chan == 1) + { + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; + sum_xy_r += g * r, sum_xy_g += g * g, sum_xy_b += g * b; + } + + int sum_x = total_g; + int sum_x2 = sum_xy_g; + + float div = (float)(16 * sum_x2 - sum_x * sum_x); + float b_x = 0.0f, b_z = 0.0f; + if (fabs(div) > 1e-8f) + { + div = 1.0f / div; + b_x = (16 * sum_xy_r - sum_x * total_r) * div; + b_z = (16 * sum_xy_b - sum_x * total_b) * div; + } + + float a_x = (total_r - b_x * sum_x) / 16.0f; + float a_z = (total_b - b_z * sum_x) / 16.0f; + + l.c[0] = a_x + b_x * min_chan_val; + l.c[2] = a_z + b_z * min_chan_val; + + h.c[0] = a_x + b_x * max_chan_val; + h.c[2] = a_z + b_z * max_chan_val; + + float dr = (h.c[0] - l.c[0]); + float db = (h.c[2] - l.c[2]); + + h.c[0] = l.c[0] + dr * (15.0f / 16.0f); + h.c[2] = l.c[2] + db * (15.0f / 16.0f); + + l.c[0] = l.c[0] + dr * (1.0f / 16.0f); + l.c[2] = l.c[2] + db * (1.0f / 16.0f); + + float d = (float)(max_chan_val - min_chan_val); + float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); + float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); + + l.c[1] = fmin_chan_val; + h.c[1] = fmax_chan_val; + } + else + { + for (uint32_t i = 0; i < 16; i++) + { + const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; + sum_xy_r += b * r, sum_xy_g += b * g, sum_xy_b += b * b; + } + + int sum_x = total_b; + int sum_x2 = sum_xy_b; + + float div = (float)(16 * sum_x2 - sum_x * sum_x); + float b_x = 0.0f, b_y = 0.0f; + if (fabs(div) > 1e-8f) + { + div = 1.0f / div; + b_x = (16 * sum_xy_r - sum_x * total_r) * div; + b_y = (16 * sum_xy_g - sum_x * total_g) * div; + } + + float a_x = (total_r - b_x * sum_x) / 16.0f; + float a_y = (total_g - b_y * sum_x) / 16.0f; + + l.c[0] = a_x + b_x * min_chan_val; + l.c[1] = a_y + b_y * min_chan_val; + + h.c[0] = a_x + b_x * max_chan_val; + h.c[1] = a_y + b_y * max_chan_val; + + float dr = (h.c[0] - l.c[0]); + float dg = (h.c[1] - l.c[1]); + + h.c[0] = l.c[0] + dr * (15.0f / 16.0f); + h.c[1] = l.c[1] + dg * (15.0f / 16.0f); + + l.c[0] = l.c[0] + dr * (1.0f / 16.0f); + l.c[1] = l.c[1] + dg * (1.0f / 16.0f); + + float d = (float)(max_chan_val - min_chan_val); + float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); + float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); + + l.c[2] = fmin_chan_val; + h.c[2] = fmax_chan_val; + } + + precise_round_565_noscale(l, h, lr, lg, lb, hr, hg, hb); + } + else if (flags & cEncodeBC1BoundingBox) + { + // Algorithm from icbc.h compress_dxt1_fast() + vec3F l, h; + l.c[0] = min_r * (1.0f / 255.0f); + l.c[1] = min_g * (1.0f / 255.0f); + l.c[2] = min_b * (1.0f / 255.0f); + + h.c[0] = max_r * (1.0f / 255.0f); + h.c[1] = max_g * (1.0f / 255.0f); + h.c[2] = max_b * (1.0f / 255.0f); + + const float bias = 8.0f / 255.0f; + float inset_r = (h.c[0] - l.c[0] - bias) * (1.0f / 16.0f); + float inset_g = (h.c[1] - l.c[1] - bias) * (1.0f / 16.0f); + float inset_b = (h.c[2] - l.c[2] - bias) * (1.0f / 16.0f); + + l.c[0] = clampf(l.c[0] + inset_r, 0.0f, 1.0f); + l.c[1] = clampf(l.c[1] + inset_g, 0.0f, 1.0f); + l.c[2] = clampf(l.c[2] + inset_b, 0.0f, 1.0f); + + h.c[0] = clampf(h.c[0] - inset_r, 0.0f, 1.0f); + h.c[1] = clampf(h.c[1] - inset_g, 0.0f, 1.0f); + h.c[2] = clampf(h.c[2] - inset_b, 0.0f, 1.0f); + + int icov_xz = 0, icov_yz = 0; + for (uint32_t i = 0; i < 16; i++) + { + int r = (int)pSrc_pixels[i].r - avg_r; + int g = (int)pSrc_pixels[i].g - avg_g; + int b = (int)pSrc_pixels[i].b - avg_b; + icov_xz += r * b; + icov_yz += g * b; + } + + if (icov_xz < 0) + std::swap(l.c[0], h.c[0]); + + if (icov_yz < 0) + std::swap(l.c[1], h.c[1]); + + precise_round_565(l, h, lr, lg, lb, hr, hg, hb); + } + else if (flags & cEncodeBC1BoundingBoxInt) + { + // Algorithm from icbc.h compress_dxt1_fast(), but converted to integer. + int inset_r = (max_r - min_r - 8) >> 4; + int inset_g = (max_g - min_g - 8) >> 4; + int inset_b = (max_b - min_b - 8) >> 4; + + min_r += inset_r; + min_g += inset_g; + min_b += inset_b; + if ((uint32_t)(min_r | min_g | min_b) > 255U) + { + min_r = clampi(min_r, 0, 255); + min_g = clampi(min_g, 0, 255); + min_b = clampi(min_b, 0, 255); + } + + max_r -= inset_r; + max_g -= inset_g; + max_b -= inset_b; + if ((uint32_t)(max_r | max_g | max_b) > 255U) + { + max_r = clampi(max_r, 0, 255); + max_g = clampi(max_g, 0, 255); + max_b = clampi(max_b, 0, 255); + } + + int icov_xz = 0, icov_yz = 0; + for (uint32_t i = 0; i < 16; i++) + { + int r = (int)pSrc_pixels[i].r - avg_r; + int g = (int)pSrc_pixels[i].g - avg_g; + int b = (int)pSrc_pixels[i].b - avg_b; + icov_xz += r * b; + icov_yz += g * b; + } + + int x0 = min_r; + int y0 = min_g; + int x1 = max_r; + int y1 = max_g; + + if (icov_xz < 0) + std::swap(x0, x1); + + if (icov_yz < 0) + std::swap(y0, y1); + + lr = to_5(x0); + lg = to_6(y0); + lb = to_5(min_b); + + hr = to_5(x1); + hg = to_6(y1); + hb = to_5(max_b); + } + else + { + // Select 2 colors along the principle axis. (There must be a faster/simpler way.) + uint32_t low_c = 0, high_c = 0; + + int icov[6] = { 0, 0, 0, 0, 0, 0 }; + for (uint32_t i = 0; i < 16; i++) + { + int r = (int)pSrc_pixels[i].r - avg_r; + int g = (int)pSrc_pixels[i].g - avg_g; + int b = (int)pSrc_pixels[i].b - avg_b; + icov[0] += r * r; + icov[1] += r * g; + icov[2] += r * b; + icov[3] += g * g; + icov[4] += g * b; + icov[5] += b * b; + } + + int saxis_r = 306, saxis_g = 601, saxis_b = 117; + + float xr = (float)(max_r - min_r); + float xg = (float)(max_g - min_g); + float xb = (float)(max_b - min_b); + + if (icov[2] < 0) + xr = -xr; + + if (icov[4] < 0) + xg = -xg; + + float cov[6]; + for (uint32_t i = 0; i < 6; i++) + cov[i] = (float)(icov[i]) * (1.0f / 255.0f); + + const uint32_t total_power_iters = (flags & cEncodeBC1Use6PowerIters) ? 6 : 4; + for (uint32_t power_iter = 0; power_iter < total_power_iters; power_iter++) + { + float r = xr * cov[0] + xg * cov[1] + xb * cov[2]; + float g = xr * cov[1] + xg * cov[3] + xb * cov[4]; + float b = xr * cov[2] + xg * cov[4] + xb * cov[5]; + xr = r; xg = g; xb = b; + } + + float k = maximum(fabsf(xr), fabsf(xg), fabsf(xb)); + if (k >= 2) + { + float m = 2048.0f / k; + saxis_r = (int)(xr * m); + saxis_g = (int)(xg * m); + saxis_b = (int)(xb * m); + } + + int low_dot = INT_MAX, high_dot = INT_MIN; + + saxis_r = (int)((uint32_t)saxis_r << 4U); + saxis_g = (int)((uint32_t)saxis_g << 4U); + saxis_b = (int)((uint32_t)saxis_b << 4U); + + for (uint32_t i = 0; i < 16; i += 4) + { + int dot0 = ((pSrc_pixels[i].r * saxis_r + pSrc_pixels[i].g * saxis_g + pSrc_pixels[i].b * saxis_b) & ~0xF) + i; + int dot1 = ((pSrc_pixels[i + 1].r * saxis_r + pSrc_pixels[i + 1].g * saxis_g + pSrc_pixels[i + 1].b * saxis_b) & ~0xF) + i + 1; + int dot2 = ((pSrc_pixels[i + 2].r * saxis_r + pSrc_pixels[i + 2].g * saxis_g + pSrc_pixels[i + 2].b * saxis_b) & ~0xF) + i + 2; + int dot3 = ((pSrc_pixels[i + 3].r * saxis_r + pSrc_pixels[i + 3].g * saxis_g + pSrc_pixels[i + 3].b * saxis_b) & ~0xF) + i + 3; + + int min_d01 = std::min(dot0, dot1); + int max_d01 = std::max(dot0, dot1); + + int min_d23 = std::min(dot2, dot3); + int max_d23 = std::max(dot2, dot3); + + int min_d = std::min(min_d01, min_d23); + int max_d = std::max(max_d01, max_d23); + + low_dot = std::min(low_dot, min_d); + high_dot = std::max(high_dot, max_d); + } + low_c = low_dot & 15; + high_c = high_dot & 15; + + lr = to_5(pSrc_pixels[low_c].r); + lg = to_6(pSrc_pixels[low_c].g); + lb = to_5(pSrc_pixels[low_c].b); + + hr = to_5(pSrc_pixels[high_c].r); + hg = to_6(pSrc_pixels[high_c].g); + hb = to_5(pSrc_pixels[high_c].b); + } + } + + static const int8_t s_adjacent_voxels[16][4] = + { + { 1,0,0, 3 }, // 0 + { 0,1,0, 4 }, // 1 + { 0,0,1, 5 }, // 2 + { -1,0,0, 0 }, // 3 + { 0,-1,0, 1 }, // 4 + { 0,0,-1, 2 }, // 5 + { 1,1,0, 9 }, // 6 + { 1,0,1, 10 }, // 7 + { 0,1,1, 11 }, // 8 + { -1,-1,0, 6 }, // 9 + { -1,0,-1, 7 }, // 10 + { 0,-1,-1, 8 }, // 11 + { -1,1,0, 13 }, // 12 + { 1,-1,0, 12 }, // 13 + { 0,-1,1, 15 }, // 14 + { 0,1,-1, 14 }, // 15 + }; + + // From icbc's high quality mode. + static inline void encode_bc1_endpoint_search(const color32* pSrc_pixels, bool any_black_pixels, + uint32_t flags, bc1_encode_results& results, uint32_t cur_err, const uint8_t* pForce_selectors) + { + int& lr = results.lr, & lg = results.lg, & lb = results.lb, & hr = results.hr, & hg = results.hg, & hb = results.hb; + uint8_t* sels = results.sels; + + int prev_improvement_index = 0, forbidden_direction = -1; + + const int endpoint_search_rounds = (flags & cEncodeBC1EndpointSearchRoundsMask) >> cEncodeBC1EndpointSearchRoundsShift; + for (int i = 0; i < endpoint_search_rounds; i++) + { + assert(s_adjacent_voxels[s_adjacent_voxels[i & 15][3]][3] == (i & 15)); + + if (forbidden_direction == (i & 31)) + continue; + + const int8_t delta[3] = { s_adjacent_voxels[i & 15][0], s_adjacent_voxels[i & 15][1], s_adjacent_voxels[i & 15][2] }; + + int trial_lr = lr, trial_lg = lg, trial_lb = lb, trial_hr = hr, trial_hg = hg, trial_hb = hb; + + if ((i >> 4) & 1) + { + trial_lr = clampi(trial_lr + delta[0], 0, 31); + trial_lg = clampi(trial_lg + delta[1], 0, 63); + trial_lb = clampi(trial_lb + delta[2], 0, 31); + } + else + { + trial_hr = clampi(trial_hr + delta[0], 0, 31); + trial_hg = clampi(trial_hg + delta[1], 0, 63); + trial_hb = clampi(trial_hb + delta[2], 0, 31); + } + + uint8_t trial_sels[16]; + + uint32_t trial_err; + if (results.m_3color) + { + trial_err = bc1_find_sels3_fullerr( + ((any_black_pixels) && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)), + pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); + } + else + { + trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); + } + + if (trial_err < cur_err) + { + cur_err = trial_err; + + forbidden_direction = s_adjacent_voxels[i & 15][3] | (i & 16); + + lr = trial_lr, lg = trial_lg, lb = trial_lb, hr = trial_hr, hg = trial_hg, hb = trial_hb; + + memcpy(sels, trial_sels, 16); + + prev_improvement_index = i; + } + + if (i - prev_improvement_index > 32) + break; + } + } + + void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags, uint32_t total_orderings_to_try, uint32_t total_orderings_to_try3, const uint8_t* pForce_selectors) + { + assert(g_initialized); + + const color32* pSrc_pixels = (const color32*)pPixels; + bc1_block* pDst_block = static_cast(pDst); + + int avg_r, avg_g, avg_b, min_r, min_g, min_b, max_r, max_g, max_b; + + const uint32_t fr = pSrc_pixels[0].r, fg = pSrc_pixels[0].g, fb = pSrc_pixels[0].b; + + uint32_t j; + for (j = 15; j >= 1; --j) + if ((pSrc_pixels[j].r != fr) || (pSrc_pixels[j].g != fg) || (pSrc_pixels[j].b != fb)) + break; + + if (j == 0) + { + encode_bc1_solid_block(pDst, fr, fg, fb, (flags & (cEncodeBC1Use3ColorBlocks | cEncodeBC1Use3ColorBlocksForBlackPixels)) != 0); + return; + } + + int total_r = fr, total_g = fg, total_b = fb; + + max_r = fr, max_g = fg, max_b = fb; + min_r = fr, min_g = fg, min_b = fb; + + uint32_t grayscale_flag = (fr == fg) && (fr == fb); + uint32_t any_black_pixels = (fr | fg | fb) < 4; + + for (uint32_t i = 1; i < 16; i++) + { + const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; + + grayscale_flag &= ((r == g) && (r == b)); + any_black_pixels |= ((r | g | b) < 4); + + max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b); + min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b); + total_r += r; total_g += g; total_b += b; + } + + avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4; + + bc1_encode_results results; + results.m_3color = false; + + uint8_t* sels = results.sels; + int& lr = results.lr, & lg = results.lg, & lb = results.lb, & hr = results.hr, & hg = results.hg, & hb = results.hb; + int orig_lr = 0, orig_lg = 0, orig_lb = 0, orig_hr = 0, orig_hg = 0, orig_hb = 0; + + lr = 0, lg = 0, lb = 0, hr = 0, hg = 0, hb = 0; + + const bool needs_block_error = ((flags & (cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use3ColorBlocks | cEncodeBC1UseFullMSEEval | cEncodeBC1EndpointSearchRoundsMask)) != 0) || + (any_black_pixels && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)); + + uint32_t cur_err = UINT32_MAX; + + if (!needs_block_error) + { + assert((flags & cEncodeBC1TryAllInitialEndponts) == 0); + + encode_bc1_pick_initial(pSrc_pixels, flags, grayscale_flag != 0, + min_r, min_g, min_b, max_r, max_g, max_b, + avg_r, avg_g, avg_b, total_r, total_g, total_b, + lr, lg, lb, hr, hg, hb); + + orig_lr = lr, orig_lg = lg, orig_lb = lb, orig_hr = hr, orig_hg = hg, orig_hb = hb; + + bc1_find_sels4_noerr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, pForce_selectors); + + const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; + for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++) + { + int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; + + vec3F xl, xh; + if (!compute_least_squares_endpoints4_rgb(pSrc_pixels, sels, &xl, &xh, total_r, total_g, total_b)) + { + // All selectors equal - treat it as a solid block which should always be equal or better. + trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; + trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; + trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; + + trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; + trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; + trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; + + // In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge. + } + else + { + precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); + } + + if ((lr == trial_lr) && (lg == trial_lg) && (lb == trial_lb) && (hr == trial_hr) && (hg == trial_hg) && (hb == trial_hb)) + break; + + bc1_find_sels4_noerr(pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, sels, pForce_selectors); + + lr = trial_lr; + lg = trial_lg; + lb = trial_lb; + hr = trial_hr; + hg = trial_hg; + hb = trial_hb; + + } // ls_pass + } + else + { + const uint32_t total_rounds = (flags & cEncodeBC1TryAllInitialEndponts) ? 2 : 1; + for (uint32_t round = 0; round < total_rounds; round++) + { + uint32_t modified_flags = flags; + if (round == 1) + { + modified_flags &= ~(cEncodeBC1Use2DLS | cEncodeBC1BoundingBox); + modified_flags |= cEncodeBC1BoundingBox; + } + + int round_lr, round_lg, round_lb, round_hr, round_hg, round_hb; + uint8_t round_sels[16]; + + encode_bc1_pick_initial(pSrc_pixels, modified_flags, grayscale_flag != 0, + min_r, min_g, min_b, max_r, max_g, max_b, + avg_r, avg_g, avg_b, total_r, total_g, total_b, + round_lr, round_lg, round_lb, round_hr, round_hg, round_hb); + + int orig_round_lr = round_lr, orig_round_lg = round_lg, orig_round_lb = round_lb, orig_round_hr = round_hr, orig_round_hg = round_hg, orig_round_hb = round_hb; + + uint32_t round_err = bc1_find_sels4(flags, pSrc_pixels, round_lr, round_lg, round_lb, round_hr, round_hg, round_hb, round_sels, UINT32_MAX, pForce_selectors); + + const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; + for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++) + { + int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; + + vec3F xl, xh; + if (!compute_least_squares_endpoints4_rgb(pSrc_pixels, round_sels, &xl, &xh, total_r, total_g, total_b)) + { + // All selectors equal - treat it as a solid block which should always be equal or better. + trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; + trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; + trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; + + trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; + trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; + trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; + + // In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge. + } + else + { + precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); + } + + if ((round_lr == trial_lr) && (round_lg == trial_lg) && (round_lb == trial_lb) && (round_hr == trial_hr) && (round_hg == trial_hg) && (round_hb == trial_hb)) + break; + + uint8_t trial_sels[16]; + uint32_t trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, round_err, pForce_selectors); + + if (trial_err < round_err) + { + round_lr = trial_lr; + round_lg = trial_lg; + round_lb = trial_lb; + + round_hr = trial_hr; + round_hg = trial_hg; + round_hb = trial_hb; + + round_err = trial_err; + memcpy(round_sels, trial_sels, 16); + } + else + break; + + } // ls_pass + + if (round_err <= cur_err) + { + cur_err = round_err; + + lr = round_lr; + lg = round_lg; + lb = round_lb; + hr = round_hr; + hg = round_hg; + hb = round_hb; + + orig_lr = orig_round_lr; + orig_lg = orig_round_lg; + orig_lb = orig_round_lb; + orig_hr = orig_round_hr; + orig_hg = orig_round_hg; + orig_hb = orig_round_hb; + + memcpy(sels, round_sels, 16); + } + + } // round + } + + if ((cur_err) && (flags & cEncodeBC1UseLikelyTotalOrderings)) + { + assert(needs_block_error); + + const uint32_t total_iters = (flags & cEncodeBC1Iterative) ? 2 : 1; + for (uint32_t iter_index = 0; iter_index < total_iters; iter_index++) + { + const uint32_t orig_err = cur_err; + + hist4 h; + for (uint32_t i = 0; i < 16; i++) + { + assert(sels[i] < 4); + h.m_hist[sels[i]]++; + } + + const uint32_t orig_total_order_index = h.lookup_total_ordering_index(); + + int r0, g0, b0, r3, g3, b3; + r0 = (lr << 3) | (lr >> 2); g0 = (lg << 2) | (lg >> 4); b0 = (lb << 3) | (lb >> 2); + r3 = (hr << 3) | (hr >> 2); g3 = (hg << 2) | (hg >> 4); b3 = (hb << 3) | (hb >> 2); + + int ar = r3 - r0, ag = g3 - g0, ab = b3 - b0; + + int dots[16]; + for (uint32_t i = 0; i < 16; i++) + { + int r = pSrc_pixels[i].r; + int g = pSrc_pixels[i].g; + int b = pSrc_pixels[i].b; + int d = 0x1000000 + (r * ar + g * ag + b * ab); + assert(d >= 0); + dots[i] = (d << 4) + i; + } + + std::sort(dots, dots + 16); + + uint32_t r_sum[17], g_sum[17], b_sum[17]; + uint32_t r = 0, g = 0, b = 0; + for (uint32_t i = 0; i < 16; i++) + { + const uint32_t p = dots[i] & 15; + + r_sum[i] = r; + g_sum[i] = g; + b_sum[i] = b; + + r += pSrc_pixels[p].r; + g += pSrc_pixels[p].g; + b += pSrc_pixels[p].b; + } + + r_sum[16] = total_r; + g_sum[16] = total_g; + b_sum[16] = total_b; + + const uint32_t q_total = (flags & cEncodeBC1Exhaustive) ? NUM_UNIQUE_TOTAL_ORDERINGS4 : clampi(total_orderings_to_try, MIN_TOTAL_ORDERINGS, MAX_TOTAL_ORDERINGS4); + for (uint32_t q = 0; q < q_total; q++) + { + const uint32_t s = (flags & cEncodeBC1Exhaustive) ? q : g_best_total_orderings4[orig_total_order_index][q]; + + int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; + + vec3F xl, xh; + + if ((s == TOTAL_ORDER_4_0_16) || (s == TOTAL_ORDER_4_1_16) || (s == TOTAL_ORDER_4_2_16) || (s == TOTAL_ORDER_4_3_16)) + { + trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; + trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; + trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; + + trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; + trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; + trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; + } + else + { + compute_least_squares_endpoints4_rgb(&xl, &xh, total_r, total_g, total_b, + g_selector_factors4[s][0], g_selector_factors4[s][1], g_selector_factors4[s][2], s, r_sum, g_sum, b_sum); + + precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); + } + + uint8_t trial_sels[16]; + + uint32_t trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); + + if (trial_err < cur_err) + { + cur_err = trial_err; + + lr = trial_lr; + lg = trial_lg; + lb = trial_lb; + + hr = trial_hr; + hg = trial_hg; + hb = trial_hb; + + memcpy(sels, trial_sels, 16); + } + + } // s + + if ((!cur_err) || (cur_err == orig_err)) + break; + + } // iter_index + } + + if (((flags & (cEncodeBC1Use3ColorBlocks | cEncodeBC1Use3ColorBlocksForBlackPixels)) != 0) && (cur_err)) + { + if (flags & cEncodeBC1Use3ColorBlocks) + { + assert(needs_block_error); + try_3color_block(pSrc_pixels, flags, cur_err, avg_r, avg_g, avg_b, orig_lr, orig_lg, orig_lb, orig_hr, orig_hg, orig_hb, total_r, total_g, total_b, total_orderings_to_try3, results, pForce_selectors); + } + + if ((any_black_pixels) && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)) + { + assert(needs_block_error); + try_3color_block_useblack(pSrc_pixels, flags, cur_err, results, pForce_selectors); + } + } + + if ((flags & cEncodeBC1EndpointSearchRoundsMask) && (cur_err)) + { + assert(needs_block_error); + + encode_bc1_endpoint_search(pSrc_pixels, any_black_pixels != 0, flags, results, cur_err, pForce_selectors); + } + + if (results.m_3color) + bc1_encode3(pDst_block, results.lr, results.lg, results.lb, results.hr, results.hg, results.hb, results.sels); + else + bc1_encode4(pDst_block, results.lr, results.lg, results.lb, results.hr, results.hg, results.hb, results.sels); + } + + // BC3-5 + + void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride) + { + assert(g_initialized); + + uint32_t min0_v, max0_v, min1_v, max1_v, min2_v, max2_v, min3_v, max3_v; + + { + min0_v = max0_v = pPixels[0 * stride]; + min1_v = max1_v = pPixels[1 * stride]; + min2_v = max2_v = pPixels[2 * stride]; + min3_v = max3_v = pPixels[3 * stride]; + } + + { + uint32_t v0 = pPixels[4 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); + uint32_t v1 = pPixels[5 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); + uint32_t v2 = pPixels[6 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); + uint32_t v3 = pPixels[7 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); + } + + { + uint32_t v0 = pPixels[8 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); + uint32_t v1 = pPixels[9 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); + uint32_t v2 = pPixels[10 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); + uint32_t v3 = pPixels[11 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); + } + + { + uint32_t v0 = pPixels[12 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); + uint32_t v1 = pPixels[13 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); + uint32_t v2 = pPixels[14 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); + uint32_t v3 = pPixels[15 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); + } + + const uint32_t min_v = minimum(min0_v, min1_v, min2_v, min3_v); + const uint32_t max_v = maximum(max0_v, max1_v, max2_v, max3_v); + + uint8_t* pDst_bytes = static_cast(pDst); + pDst_bytes[0] = (uint8_t)max_v; + pDst_bytes[1] = (uint8_t)min_v; + + if (max_v == min_v) + { + memset(pDst_bytes + 2, 0, 6); + return; + } + + const uint32_t delta = max_v - min_v; + + // min_v is now 0. Compute thresholds between values by scaling max_v. It's x14 because we're adding two x7 scale factors. + const int t0 = delta * 13; + const int t1 = delta * 11; + const int t2 = delta * 9; + const int t3 = delta * 7; + const int t4 = delta * 5; + const int t5 = delta * 3; + const int t6 = delta * 1; + + // BC4 floors in its divisions, which we compensate for with the 4 bias. + // This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one). + const int bias = 4 - min_v * 14; + + static const uint32_t s_tran0[8] = { 1U , 7U , 6U , 5U , 4U , 3U , 2U , 0U }; + static const uint32_t s_tran1[8] = { 1U << 3U, 7U << 3U, 6U << 3U, 5U << 3U, 4U << 3U, 3U << 3U, 2U << 3U, 0U << 3U }; + static const uint32_t s_tran2[8] = { 1U << 6U, 7U << 6U, 6U << 6U, 5U << 6U, 4U << 6U, 3U << 6U, 2U << 6U, 0U << 6U }; + static const uint32_t s_tran3[8] = { 1U << 9U, 7U << 9U, 6U << 9U, 5U << 9U, 4U << 9U, 3U << 9U, 2U << 9U, 0U << 9U }; + + uint64_t a0, a1, a2, a3; + { + const int v0 = pPixels[0 * stride] * 14 + bias; + const int v1 = pPixels[1 * stride] * 14 + bias; + const int v2 = pPixels[2 * stride] * 14 + bias; + const int v3 = pPixels[3 * stride] * 14 + bias; + a0 = s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]; + a1 = s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]; + a2 = s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]; + a3 = s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]; + } + + { + const int v0 = pPixels[4 * stride] * 14 + bias; + const int v1 = pPixels[5 * stride] * 14 + bias; + const int v2 = pPixels[6 * stride] * 14 + bias; + const int v3 = pPixels[7 * stride] * 14 + bias; + a0 |= (uint64_t)(s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)] << 12U); + a1 |= (uint64_t)(s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)] << 12U); + a2 |= (uint64_t)(s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)] << 12U); + a3 |= (uint64_t)(s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)] << 12U); + } + + { + const int v0 = pPixels[8 * stride] * 14 + bias; + const int v1 = pPixels[9 * stride] * 14 + bias; + const int v2 = pPixels[10 * stride] * 14 + bias; + const int v3 = pPixels[11 * stride] * 14 + bias; + a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 24U); + a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 24U); + a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 24U); + a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 24U); + } + + { + const int v0 = pPixels[12 * stride] * 14 + bias; + const int v1 = pPixels[13 * stride] * 14 + bias; + const int v2 = pPixels[14 * stride] * 14 + bias; + const int v3 = pPixels[15 * stride] * 14 + bias; + a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 36U); + a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 36U); + a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 36U); + a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 36U); + } + + const uint64_t f = a0 | a1 | a2 | a3; + + pDst_bytes[2] = (uint8_t)f; + pDst_bytes[3] = (uint8_t)(f >> 8U); + pDst_bytes[4] = (uint8_t)(f >> 16U); + pDst_bytes[5] = (uint8_t)(f >> 24U); + pDst_bytes[6] = (uint8_t)(f >> 32U); + pDst_bytes[7] = (uint8_t)(f >> 40U); + } + + uint32_t encode_bc4_hq(void* pDst, const uint8_t* pPixels, uint32_t stride, uint32_t search_rad, uint32_t mode_flag, const uint8_t* pForce_selectors) + { + assert(mode_flag); + + uint8_t* pDst_bytes = static_cast(pDst); + + uint32_t min_val = 255, max_val = 0; + for (uint32_t i = 0; i < 16; i++) + { + uint32_t val = pPixels[i * stride]; + min_val = std::min(val, min_val); + max_val = std::max(val, max_val); + } + + if (min_val == max_val) + { + if (mode_flag & BC4_USE_MODE6_FLAG) + { + pDst_bytes[0] = (uint8_t)min_val; + pDst_bytes[1] = (uint8_t)min_val; + + memset(pDst_bytes + 2, 0, 6); + + assert(!(pDst_bytes[0] > pDst_bytes[1])); + } + else + { + // Use an 8 value encoding + if (min_val > 0) + { + pDst_bytes[0] = (uint8_t)min_val; + pDst_bytes[1] = (uint8_t)min_val - 1; + + memset(pDst_bytes + 2, 0, 6); + } + else + { + static const uint8_t s_const_1_vals[8] = { 1, 0, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24 }; + memcpy(pDst_bytes, s_const_1_vals, 8); + } + + assert(pDst_bytes[0] > pDst_bytes[1]); + } + +#if defined(_DEBUG) || defined(DEBUG) + { + bc4_block* pBlock = (bc4_block*)pDst; + uint8_t pixels[16]; + unpack_bc4(pDst, pixels, 1); + for (uint32_t i = 0; i < 16; i++) + assert(pixels[i] == min_val); + if (mode_flag & BC4_USE_MODE6_FLAG) + { + assert(pBlock->is_alpha6_block()); + } + else + { + assert(!pBlock->is_alpha6_block()); + } + } +#endif + + return 0; + } + + uint32_t best_err = UINT32_MAX; + for (uint32_t mode = 0; mode < 2; mode++) + { + if ((mode_flag & (1 << mode)) == 0) + continue; + + for (int lo_delta = -(int)search_rad; lo_delta <= (int)search_rad; lo_delta++) + { + for (int hi_delta = -(int)search_rad; hi_delta <= (int)search_rad; hi_delta++) + { + bc4_block trial_block; + trial_block.m_endpoints[0] = (uint8_t)clamp(max_val + hi_delta, 0, 255); + trial_block.m_endpoints[1] = (uint8_t)clamp(min_val + lo_delta, 0, 255); + + if (trial_block.m_endpoints[0] == trial_block.m_endpoints[1]) + continue; + + if (mode == 0) + { + if (trial_block.is_alpha6_block()) + std::swap(trial_block.m_endpoints[0], trial_block.m_endpoints[1]); + } + else if (!trial_block.is_alpha6_block()) + std::swap(trial_block.m_endpoints[0], trial_block.m_endpoints[1]); + + uint8_t block_vals[8]; + trial_block.get_block_values(block_vals, trial_block.m_endpoints[0], trial_block.m_endpoints[1]); + + uint32_t trial_err = 0; + uint8_t trial_sels[16]; + + if (pForce_selectors) + { + memcpy(trial_sels, pForce_selectors, 16); + + for (uint32_t i = 0; i < 16; i++) + trial_err += squarei(block_vals[pForce_selectors[i]] - pPixels[i * stride]); + } + else + { + for (uint32_t i = 0; i < 16; i++) + { + uint32_t best_index_err = UINT32_MAX; + uint32_t best_index = 0; + for (uint32_t j = 0; j < 8; j++) + { + uint32_t err = squarei(block_vals[j] - pPixels[i * stride]); + if (err < best_index_err) + { + best_index_err = err; + best_index = j; + if (!err) + break; + } + } + + trial_err += best_index_err; + if (trial_err >= best_err) + break; + + trial_sels[i] = (uint8_t)best_index; + } // i + } + + if (trial_err < best_err) + { + best_err = trial_err; + + uint64_t sel_vals = 0; + for (uint32_t i = 0; i < 16; i++) + sel_vals |= ((uint64_t)trial_sels[i] << (i * 3)); + + trial_block.m_selectors[0] = (uint8_t)sel_vals; + trial_block.m_selectors[1] = (uint8_t)(sel_vals >> 8); + trial_block.m_selectors[2] = (uint8_t)(sel_vals >> 16); + trial_block.m_selectors[3] = (uint8_t)(sel_vals >> 24); + trial_block.m_selectors[4] = (uint8_t)(sel_vals >> 32); + trial_block.m_selectors[5] = (uint8_t)(sel_vals >> 40); + + memcpy(pDst_bytes, &trial_block, sizeof(bc4_block)); + } // if (trial_err < best_err) + + } // hi_delta + + } // lo_delta + + } // mode + + return best_err; + } + + void encode_bc3(void* pDst, const uint8_t* pPixels, uint32_t flags, uint32_t total_orderings_to_try) + { + assert(g_initialized); + + // 3-color blocks are not allowed with BC3 (on most GPU's). + flags &= ~(cEncodeBC1Use3ColorBlocksForBlackPixels | cEncodeBC1Use3ColorBlocks); + + encode_bc4(pDst, pPixels + 3, 4); + encode_bc1(static_cast(pDst) + 8, pPixels, flags, total_orderings_to_try); + } + + void encode_bc3(uint32_t level, void* pDst, const uint8_t* pPixels) + { + assert(g_initialized); + + encode_bc4(pDst, pPixels + 3, 4); + encode_bc1(level, static_cast(pDst) + 8, pPixels, false, false); + } + + void encode_bc3_hq(uint32_t level, void* pDst, const uint8_t* pPixels, uint32_t alpha_search_rad, uint32_t alpha_modes) + { + assert(g_initialized); + + encode_bc4_hq(pDst, pPixels + 3, 4, alpha_search_rad, alpha_modes); + encode_bc1(level, static_cast(pDst) + 8, pPixels, false, false); + } + + void encode_bc5(void* pDst, const uint8_t* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride) + { + assert(g_initialized); + + encode_bc4(pDst, pPixels + chan0, stride); + encode_bc4(static_cast(pDst) + 8, pPixels + chan1, stride); + } + + void encode_bc5_hq(void* pDst, const uint8_t* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride, uint32_t alpha_search_rad, uint32_t alpha_modes) + { + assert(g_initialized); + + encode_bc4_hq(pDst, pPixels + chan0, stride, alpha_search_rad, alpha_modes); + encode_bc4_hq(static_cast(pDst) + 8, pPixels + chan1, stride, alpha_search_rad, alpha_modes); + } + + bool unpack_bc1_block_colors(const void* pBlock_bits, color32* c, bc1_approx_mode mode) + { + const bc1_block* pBlock = static_cast(pBlock_bits); + + const uint32_t l = pBlock->get_low_color(); + const uint32_t h = pBlock->get_high_color(); + + const int cr0 = (l >> 11) & 31; + const int cg0 = (l >> 5) & 63; + const int cb0 = l & 31; + const int r0 = (cr0 << 3) | (cr0 >> 2); + const int g0 = (cg0 << 2) | (cg0 >> 4); + const int b0 = (cb0 << 3) | (cb0 >> 2); + + const int cr1 = (h >> 11) & 31; + const int cg1 = (h >> 5) & 63; + const int cb1 = h & 31; + const int r1 = (cr1 << 3) | (cr1 >> 2); + const int g1 = (cg1 << 2) | (cg1 >> 4); + const int b1 = (cb1 << 3) | (cb1 >> 2); + + bool used_punchthrough = false; + + if (l > h) + { + c[0].set_noclamp_rgba(r0, g0, b0, 255); + c[1].set_noclamp_rgba(r1, g1, b1, 255); + switch (mode) + { + case bc1_approx_mode::cBC1Ideal: + c[2].set_noclamp_rgba((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 255); + c[3].set_noclamp_rgba((r1 * 2 + r0) / 3, (g1 * 2 + g0) / 3, (b1 * 2 + b0) / 3, 255); + break; + case bc1_approx_mode::cBC1IdealRound4: + c[2].set_noclamp_rgba((r0 * 2 + r1 + 1) / 3, (g0 * 2 + g1 + 1) / 3, (b0 * 2 + b1 + 1) / 3, 255); + c[3].set_noclamp_rgba((r1 * 2 + r0 + 1) / 3, (g1 * 2 + g0 + 1) / 3, (b1 * 2 + b0 + 1) / 3, 255); + break; + case bc1_approx_mode::cBC1NVidia: + c[2].set_noclamp_rgba(interp_5_nv(cr0, cr1), interp_6_nv(g0, g1), interp_5_nv(cb0, cb1), 255); + c[3].set_noclamp_rgba(interp_5_nv(cr1, cr0), interp_6_nv(g1, g0), interp_5_nv(cb1, cb0), 255); + break; + case bc1_approx_mode::cBC1AMD: + c[2].set_noclamp_rgba(interp_5_6_amd(r0, r1), interp_5_6_amd(g0, g1), interp_5_6_amd(b0, b1), 255); + c[3].set_noclamp_rgba(interp_5_6_amd(r1, r0), interp_5_6_amd(g1, g0), interp_5_6_amd(b1, b0), 255); + break; + } + } + else + { + c[0].set_noclamp_rgba(r0, g0, b0, 255); + c[1].set_noclamp_rgba(r1, g1, b1, 255); + switch (mode) + { + case bc1_approx_mode::cBC1Ideal: + case bc1_approx_mode::cBC1IdealRound4: + c[2].set_noclamp_rgba((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255); + break; + case bc1_approx_mode::cBC1NVidia: + c[2].set_noclamp_rgba(interp_half_5_nv(cr0, cr1), interp_half_6_nv(g0, g1), interp_half_5_nv(cb0, cb1), 255); + break; + case bc1_approx_mode::cBC1AMD: + c[2].set_noclamp_rgba(interp_half_5_6_amd(r0, r1), interp_half_5_6_amd(g0, g1), interp_half_5_6_amd(b0, b1), 255); + break; + } + + c[3].set_noclamp_rgba(0, 0, 0, 0); + used_punchthrough = true; + } + + return used_punchthrough; + } + + // Returns true if the block uses 3 color punchthrough alpha mode. + bool unpack_bc1(const void* pBlock_bits, void* pPixels, bool set_alpha, bc1_approx_mode mode) + { + color32* pDst_pixels = static_cast(pPixels); + + static_assert(sizeof(bc1_block) == 8, "sizeof(bc1_block) == 8"); + static_assert(sizeof(bc4_block) == 8, "sizeof(bc4_block) == 8"); + + const bc1_block* pBlock = static_cast(pBlock_bits); + + color32 c[4]; + const bool used_punchthrough = unpack_bc1_block_colors(pBlock_bits, c, mode); + + if (set_alpha) + { + for (uint32_t y = 0; y < 4; y++, pDst_pixels += 4) + { + pDst_pixels[0] = c[pBlock->get_selector(0, y)]; + pDst_pixels[1] = c[pBlock->get_selector(1, y)]; + pDst_pixels[2] = c[pBlock->get_selector(2, y)]; + pDst_pixels[3] = c[pBlock->get_selector(3, y)]; + } + } + else + { + for (uint32_t y = 0; y < 4; y++, pDst_pixels += 4) + { + pDst_pixels[0].set_rgb(c[pBlock->get_selector(0, y)]); + pDst_pixels[1].set_rgb(c[pBlock->get_selector(1, y)]); + pDst_pixels[2].set_rgb(c[pBlock->get_selector(2, y)]); + pDst_pixels[3].set_rgb(c[pBlock->get_selector(3, y)]); + } + } + + return used_punchthrough; + } + + void unpack_bc4(const void* pBlock_bits, uint8_t* pPixels, uint32_t stride) + { + static_assert(sizeof(bc4_block) == 8, "sizeof(bc4_block) == 8"); + + const bc4_block* pBlock = static_cast(pBlock_bits); + + uint8_t sel_values[8]; + bc4_block::get_block_values(sel_values, pBlock->get_low_alpha(), pBlock->get_high_alpha()); + + const uint64_t selector_bits = pBlock->get_selector_bits(); + + for (uint32_t y = 0; y < 4; y++, pPixels += (stride * 4U)) + { + pPixels[0] = sel_values[pBlock->get_selector(0, y, selector_bits)]; + pPixels[stride * 1] = sel_values[pBlock->get_selector(1, y, selector_bits)]; + pPixels[stride * 2] = sel_values[pBlock->get_selector(2, y, selector_bits)]; + pPixels[stride * 3] = sel_values[pBlock->get_selector(3, y, selector_bits)]; + } + } + + bool unpack_bc2(const void* pBlock_bits, void* pPixels, bc1_approx_mode mode) + { + color32* pDst_pixels = static_cast(pPixels); + auto pBlock = static_cast(pBlock_bits); + + bool success = true; + + // unpack BC1-encoded block (8 bytes) + if (unpack_bc1(pBlock + 8, pDst_pixels, true, mode)) + success = false; + + // then unpack sharp alpha block (8 bytes - 4 x uint16_t) + auto pAlpha = reinterpret_cast(pBlock); + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + pDst_pixels[j + i * 4].a = ((pAlpha[i] >> (4 * j)) & 0x0F) * 17; + + return success; + } + + // Returns false if the block uses 3-color punchthrough alpha mode, which isn't supported on some GPU's for BC3. + bool unpack_bc3(const void* pBlock_bits, void* pPixels, bc1_approx_mode mode) + { + color32* pDst_pixels = static_cast(pPixels); + + bool success = true; + + if (unpack_bc1((const uint8_t*)pBlock_bits + sizeof(bc4_block), pDst_pixels, true, mode)) + success = false; + + unpack_bc4(pBlock_bits, &pDst_pixels[0].a, sizeof(color32)); + + return success; + } + + // writes RG + void unpack_bc5(const void* pBlock_bits, void* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride) + { + unpack_bc4(pBlock_bits, (uint8_t*)pPixels + chan0, stride); + unpack_bc4((const uint8_t*)pBlock_bits + sizeof(bc4_block), (uint8_t*)pPixels + chan1, stride); + } + +} // namespace rgbcx diff --git a/external/bc7enc_rdo/rgbcx.h b/external/bc7enc_rdo/rgbcx.h new file mode 100644 index 0000000000..ffa48167c9 --- /dev/null +++ b/external/bc7enc_rdo/rgbcx.h @@ -0,0 +1,470 @@ +// rgbcx.h v1.13 +// High-performance scalar encoders and RDO (Rate Distortion Optimization) post processors for BC1-5. +// Public Domain or MIT license (you choose - see below), written by Richard Geldreich 2020 . +// +// Influential references: +// https://tinyurl.com/y3vxz457 (Ortego and Ramchandran, "Rate-distortion Methods for Image and Video Compression", 1998) +// http://sjbrown.co.uk/2006/01/19/dxt-compression-techniques/ +// https://github.com/nothings/stb/blob/master/stb_dxt.h +// https://gist.github.com/castano/c92c7626f288f9e99e158520b14a61cf +// https://github.com/castano/icbc/blob/master/icbc.h +// http://www.humus.name/index.php?page=3D&ID=79 +// +// This is a single header file library. Be sure to "#define RGBCX_IMPLEMENTATION" in one .cpp file somewhere. +// +// Instructions: +// +// The library MUST be initialized by calling this function at least once before using any encoder or decoder functions: +// +// void rgbcx::init(bc1_approx_mode mode = cBC1Ideal); +// +// This function manipulates global state, so it is not thread safe. +// You can call it multiple times to change the global BC1 approximation mode. +// Important: BC1/3 textures encoded using non-ideal BC1 approximation modes should only be sampled on parts from that vendor. +// If you encode for AMD, average error on AMD parts will go down, but average error on NVidia parts will go up and vice versa. +// If in doubt, encode in ideal BC1 mode. +// +// Call these functions to encode BC1-5: +// void rgbcx::encode_bc1(uint32_t level, void* pDst, const uint8_t* pPixels, bool allow_3color, bool use_transparent_texels_for_black); +// void rgbcx::encode_bc3(uint32_t level, void* pDst, const uint8_t* pPixels); +// void rgbcx::encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride = 4); +// void rgbcx::encode_bc5(void* pDst, const uint8_t* pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4); +// +// - level ranges from MIN_LEVEL to MAX_LEVEL. The higher the level, the slower the encoder goes, but the higher the average quality. +// levels [0,4] are fast and compete against stb_dxt (default and HIGHQUAL). The remaining levels compete against squish/NVTT/icbc and icbc HQ. +// If in doubt just use level 10, set allow_3color to true and use_transparent_texels_for_black to false, and adjust as needed. +// +// - pDst is a pointer to the 8-byte (BC1/4) or 16-byte (BC3/5) destination block. +// +// - pPixels is a pointer to the 32-bpp pixels, in either RGBX or RGBA format (R is first in memory). +// Alpha is always ignored by encode_bc1(). +// +// - allow_3color: If true the encoder will use 3-color blocks. This flag is ignored unless level is >= 5 (because lower levels compete against stb_dxt and it doesn't support 3-color blocks). +// Do not enable on BC3-5 textures. 3-color block usage slows down encoding. +// +// - use_transparent_texels_for_black: If true the encoder will use 3-color block transparent black pixels to code very dark or black texels. Your engine/shader MUST ignore the sampled +// alpha value for textures encoded in this mode. This is how NVidia's classic "nvdxt" encoder (used by many original Xbox titles) used to work by default on DXT1C textures. It increases +// average quality substantially (because dark texels/black are very common) and is highly recommended. +// Do not enable on BC3-5 textures. +// +// - stride is the source pixel stride, in bytes. It's typically 4. +// +// - chan0 and chan1 are the source channels. Typically they will be 0 and 1. +// +// All encoding and decoding functions are thread-safe. +// +// To reduce the compiled size of the encoder, set #define RGBCX_USE_SMALLER_TABLES to 1 before including this header. +// +#pragma once + +#ifdef _MSC_VER +#pragma warning (disable:4201) //nameless struct/union +#endif + +#include +#include +#include +#include +#include + +// By default, the table used to accelerate cluster fit on 4 color blocks uses a 969x128 entry table. +// To reduce the executable size, set RGBCX_USE_SMALLER_TABLES to 1, which selects the smaller 969x32 entry table. +#ifndef RGBCX_USE_SMALLER_TABLES +#define RGBCX_USE_SMALLER_TABLES 0 +#endif + +namespace rgbcx +{ + enum class bc1_approx_mode + { + // The default mode. No rounding for 4-color colors 2,3. My older tools/compressors use this mode. + // This matches the D3D10 docs on BC1. + cBC1Ideal = 0, + + // NVidia GPU mode. + cBC1NVidia = 1, + + // AMD GPU mode. + cBC1AMD = 2, + + // This mode matches AMD Compressonator's output. It rounds 4-color colors 2,3 (not 3-color color 2). + // This matches the D3D9 docs on DXT1. + cBC1IdealRound4 = 3 + }; + + enum class eNoClamp { cNoClamp }; + static inline uint8_t clamp255(int32_t i) { return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i); } + + template inline S maximum(S a, S b) { return (a > b) ? a : b; } + template inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); } + template inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); } + + template inline S minimum(S a, S b) { return (a < b) ? a : b; } + template inline S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); } + template inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); } + + // TODO: this shares a lot of similarities with ert.h's color_rgba, maybe use this and remove that? + struct color32 + { + union + { + struct + { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + }; + + uint8_t c[4]; + + uint32_t m; + }; + + color32() { } + + color32(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); } + color32(eNoClamp unused, uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { (void)unused; set_noclamp_rgba(vr, vg, vb, va); } + + void set(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { c[0] = static_cast(vr); c[1] = static_cast(vg); c[2] = static_cast(vb); c[3] = static_cast(va); } + + void set_noclamp_rgb(uint32_t vr, uint32_t vg, uint32_t vb) { c[0] = static_cast(vr); c[1] = static_cast(vg); c[2] = static_cast(vb); } + void set_noclamp_rgba(uint32_t vr, uint32_t vg, uint32_t vb, uint32_t va) { set(vr, vg, vb, va); } + + void set_clamped(int vr, int vg, int vb, int va) { c[0] = clamp255(vr); c[1] = clamp255(vg); c[2] = clamp255(vb); c[3] = clamp255(va); } + + uint8_t operator[] (uint32_t idx) const { assert(idx < 4); return c[idx]; } + uint8_t& operator[] (uint32_t idx) { assert(idx < 4); return c[idx]; } + + bool operator== (const color32& rhs) const { return m == rhs.m; } + + void set_rgb(const color32& other) { c[0] = static_cast(other.c[0]); c[1] = static_cast(other.c[1]); c[2] = static_cast(other.c[2]); } + + static color32 comp_min(const color32& a, const color32& b) { return color32(eNoClamp::cNoClamp, std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); } + static color32 comp_max(const color32& a, const color32& b) { return color32(eNoClamp::cNoClamp, std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); } + }; + + // init() MUST be called once before using the BC1 encoder. + // This function may be called multiple times to change the BC1 approximation mode. + // This function initializes global state, so don't call it while other threads inside the encoder. + // Important: If you encode textures for a specific vendor's GPU's, beware that using that texture data on other GPU's may result in ugly artifacts. + // Encode to cBC1Ideal unless you know the texture data will only be deployed or used on a specific vendor's GPU. + void init(bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal); + + // Optimally encodes a solid color block to BC1 format. + void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb, bool allow_3color); + + // BC1 low-level API encoder flags. You can ignore this if you use the simple level API. + enum + { + // Try to improve quality using the most likely total orderings. + // The total_orderings_to_try parameter will then control the number of total orderings to try for 4 color blocks, and the + // total_orderings_to_try3 parameter will control the number of total orderings to try for 3 color blocks (if they are enabled). + cEncodeBC1UseLikelyTotalOrderings = 2, + + // Use 2 least squares pass, instead of one (same as stb_dxt's HIGHQUAL option). + // Recommended if you're enabling cEncodeBC1UseLikelyTotalOrderings. + cEncodeBC1TwoLeastSquaresPasses = 4, + + // cEncodeBC1Use3ColorBlocksForBlackPixels allows the BC1 encoder to use 3-color blocks for blocks containing black or very dark pixels. + // You shader/engine MUST ignore the alpha channel on textures encoded with this flag. + // Average quality goes up substantially for my 100 texture corpus (~.5 dB), so it's worth using if you can. + // Note the BC1 encoder does not actually support transparency in 3-color mode. + // Don't set when encoding to BC3. + cEncodeBC1Use3ColorBlocksForBlackPixels = 8, + + // If cEncodeBC1Use3ColorBlocks is set, the encoder can use 3-color mode for a small but noticeable gain in average quality, but lower perf. + // If you also specify the cEncodeBC1UseLikelyTotalOrderings flag, set the total_orderings_to_try3 paramter to the number of total orderings to try. + // Don't set when encoding to BC3. + cEncodeBC1Use3ColorBlocks = 16, + + // cEncodeBC1Iterative will greatly increase encode time, but is very slightly higher quality. + // Same as squish's iterative cluster fit option. Not really worth the tiny boost in quality, unless you just don't care about perf. at all. + cEncodeBC1Iterative = 32, + + // cEncodeBC1BoundingBox enables a fast all-integer PCA approximation on 4-color blocks. + // At level 0 options (no other flags), this is ~15% faster, and higher *average* quality. + cEncodeBC1BoundingBox = 64, + + // Use a slightly lower quality, but ~30% faster MSE evaluation function for 4-color blocks. + cEncodeBC1UseFasterMSEEval = 128, + + // Examine all colors to compute selectors/MSE (slower than default) + cEncodeBC1UseFullMSEEval = 256, + + // Use 2D least squares+inset+optimal rounding (the method used in Humus's GPU texture encoding demo), instead of PCA. + // Around 18% faster, very slightly lower average quality to better (depends on the content). + cEncodeBC1Use2DLS = 512, + + // Use 6 power iterations vs. 4 for PCA. + cEncodeBC1Use6PowerIters = 2048, + + // Check all total orderings - *very* slow. The encoder is not designed to be used in this way. + cEncodeBC1Exhaustive = 8192, + + // Try 2 different ways of choosing the initial endpoints. + cEncodeBC1TryAllInitialEndponts = 16384, + + // Same as cEncodeBC1BoundingBox, but implemented using integer math (faster, slightly less quality) + cEncodeBC1BoundingBoxInt = 32768, + + // Try refining the final endpoints by examining nearby colors. + cEncodeBC1EndpointSearchRoundsShift = 22, + cEncodeBC1EndpointSearchRoundsMask = 1023U << cEncodeBC1EndpointSearchRoundsShift, + }; + + const uint32_t MIN_TOTAL_ORDERINGS = 1; + const uint32_t MAX_TOTAL_ORDERINGS3 = 32; + +#if RGBCX_USE_SMALLER_TABLES + const uint32_t MAX_TOTAL_ORDERINGS4 = 32; +#else + const uint32_t MAX_TOTAL_ORDERINGS4 = 128; +#endif + + // DEFAULT_TOTAL_ORDERINGS_TO_TRY is around 3x faster than libsquish at slightly higher average quality. 10-16 is a good range to start to compete against libsquish. + const uint32_t DEFAULT_TOTAL_ORDERINGS_TO_TRY = 10; + + const uint32_t DEFAULT_TOTAL_ORDERINGS_TO_TRY3 = 1; + + // Encodes a 4x4 block of RGBX (X=ignored) pixels to BC1 format. + // This is the simplified interface for BC1 encoding, which accepts a level parameter and converts that to the best overall flags. + // The pixels are in RGBA format, where R is first in memory. The BC1 encoder completely ignores the alpha channel (i.e. there is no punchthrough alpha support). + // This is the recommended function to use for BC1 encoding, becuase it configures the encoder for you in the best possible way (on average). + // Note that the 3 color modes won't be used at all until level 5 or higher. + // No transparency supported, however if you set use_transparent_texels_for_black to true the encocer will use transparent selectors on very dark/black texels to reduce MSE. + const uint32_t MIN_LEVEL = 0, MAX_LEVEL = 18; + void encode_bc1(uint32_t level, void* pDst, const uint8_t* pPixels, bool allow_3color, bool use_transparent_texels_for_black, const uint8_t* pForce_selectors = nullptr); + + // Low-level interface for BC1 encoding. + // Always returns a 4 color block, unless cEncodeBC1Use3ColorBlocksForBlackPixels or cEncodeBC1Use3ColorBlock flags are specified. + // total_orderings_to_try controls the perf. vs. quality tradeoff on 4-color blocks when the cEncodeBC1UseLikelyTotalOrderings flag is used. It must range between [MIN_TOTAL_ORDERINGS, MAX_TOTAL_ORDERINGS4]. + // total_orderings_to_try3 controls the perf. vs. quality tradeoff on 3-color bocks when the cEncodeBC1UseLikelyTotalOrderings and the cEncodeBC1Use3ColorBlocks flags are used. Valid range is [0,MAX_TOTAL_ORDERINGS3] (0=disabled). + void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags = 0, uint32_t total_orderings_to_try = DEFAULT_TOTAL_ORDERINGS_TO_TRY, uint32_t total_orderings_to_try3 = DEFAULT_TOTAL_ORDERINGS_TO_TRY3, const uint8_t *pForce_selectors = nullptr); + + // Constants used for high quality BC4/BC5 encoding (and alpha of BC3) + const uint32_t BC4_DEFAULT_SEARCH_RAD = 3; + const uint32_t BC4_USE_MODE8_FLAG = 1; + const uint32_t BC4_USE_MODE6_FLAG = 2; + const uint32_t BC4_USE_ALL_MODES = 3; + + // Encodes a 4x4 block of RGBA pixels to BC3 format. + // There are two encode_bc3() functions. + // The first is the recommended function, which accepts a level parameter. + // The second is a low-level version that allows fine control over BC1 encoding. + void encode_bc3(uint32_t level, void* pDst, const uint8_t* pPixels); + void encode_bc3(void* pDst, const uint8_t* pPixels, uint32_t flags = 0, uint32_t total_orderings_to_try = DEFAULT_TOTAL_ORDERINGS_TO_TRY); + void encode_bc3_hq(uint32_t level, void* pDst, const uint8_t* pPixels, uint32_t alpha_search_rad = BC4_DEFAULT_SEARCH_RAD, uint32_t alpha_modes = BC4_USE_ALL_MODES); + + // Encodes a single channel to BC4. + // stride is the source pixel stride in bytes. + void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride = 4); + uint32_t encode_bc4_hq(void* pDst, const uint8_t* pPixels, uint32_t stride = 4, uint32_t search_rad = BC4_DEFAULT_SEARCH_RAD, uint32_t mode_flag = BC4_USE_ALL_MODES, const uint8_t* pForce_selectors = nullptr); + + // Encodes two channels to BC5. + // chan0/chan1 control which channels, stride is the source pixel stride in bytes. + void encode_bc5(void* pDst, const uint8_t* pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4); + void encode_bc5_hq(void* pDst, const uint8_t* pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4, uint32_t alpha_search_rad = BC4_DEFAULT_SEARCH_RAD, uint32_t alpha_modes = BC4_USE_ALL_MODES); + + // Decompression functions. + + bool unpack_bc1_block_colors(const void* pBlock_bits, color32* c, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal); + + // Returns true if the block uses 3 color punchthrough alpha mode. + bool unpack_bc1(const void* pBlock_bits, void* pPixels, bool set_alpha = true, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal); + + void unpack_bc4(const void* pBlock_bits, uint8_t* pPixels, uint32_t stride = 4); + + bool unpack_bc2(const void* pBlock_bits, void* pPixels, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal); + + // Returns true if the block uses 3 color punchthrough alpha mode. + bool unpack_bc3(const void* pBlock_bits, void* pPixels, bc1_approx_mode mode = bc1_approx_mode::cBC1Ideal); + + void unpack_bc5(const void* pBlock_bits, void* pPixels, uint32_t chan0 = 0, uint32_t chan1 = 1, uint32_t stride = 4); + + // Rate Distortion Optimization (RDO) + enum dxt_constants + { + cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U, + cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U, + }; + + struct bc1_block + { + enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 }; + + uint8_t m_low_color[cTotalEndpointBytes]; + uint8_t m_high_color[cTotalEndpointBytes]; + uint8_t m_selectors[cTotalSelectorBytes]; + + inline uint32_t get_low_color() const { return m_low_color[0] | (m_low_color[1] << 8U); } + inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); } + inline bool is_3color() const { return get_low_color() <= get_high_color(); } + inline void set_low_color(uint16_t c) { m_low_color[0] = static_cast(c & 0xFF); m_low_color[1] = static_cast((c >> 8) & 0xFF); } + inline void set_high_color(uint16_t c) { m_high_color[0] = static_cast(c & 0xFF); m_high_color[1] = static_cast((c >> 8) & 0xFF); } + inline uint32_t get_selector(uint32_t x, uint32_t y) const { assert((x < 4U) && (y < 4U)); return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask; } + inline void set_selector(uint32_t x, uint32_t y, uint32_t val) { assert((x < 4U) && (y < 4U) && (val < 4U)); m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); m_selectors[y] |= (val << (x * cDXT1SelectorBits)); } + + inline uint32_t get_endpoint_bits() const { return m_low_color[0] | (m_low_color[1] << 8) | (m_high_color[0] << 16) | (m_high_color[1] << 24); } + inline void set_endpoint_bits(uint32_t s) { m_low_color[0] = (uint8_t)s; m_low_color[1] = (uint8_t)(s >> 8); m_high_color[0] = (uint8_t)(s >> 16); m_high_color[1] = (uint8_t)(s >> 24); } + + inline uint32_t get_selector_bits() const { return m_selectors[0] | (m_selectors[1] << 8) | (m_selectors[2] << 16) | (m_selectors[3] << 24); } + inline void set_selector_bits(uint32_t s) { m_selectors[0] = (uint8_t)s; m_selectors[1] = (uint8_t)(s >> 8); m_selectors[2] = (uint8_t)(s >> 16); m_selectors[3] = (uint8_t)(s >> 24); } + + inline bool any_selectors_transparent() const + { + uint32_t sel_bits = get_selector_bits(); + for (uint32_t i = 0; i < 16; i++) + { + if ((sel_bits & 3) == 3) + return true; + + sel_bits >>= 2; + } + return false; + } + + static inline uint16_t pack_color(const color32& color, bool scaled, uint32_t bias = 127U) + { + uint32_t r = color.r, g = color.g, b = color.b; + if (scaled) + { + r = (r * 31U + bias) / 255U; + g = (g * 63U + bias) / 255U; + b = (b * 31U + bias) / 255U; + } + return static_cast(minimum(b, 31U) | (minimum(g, 63U) << 5U) | (minimum(r, 31U) << 11U)); + } + + static inline uint16_t pack_unscaled_color(uint32_t r, uint32_t g, uint32_t b) { return static_cast(b | (g << 5U) | (r << 11U)); } + + static inline void unpack_color(uint32_t c, uint32_t& r, uint32_t& g, uint32_t& b) + { + r = (c >> 11) & 31; + g = (c >> 5) & 63; + b = c & 31; + + r = (r << 3) | (r >> 2); + g = (g << 2) | (g >> 4); + b = (b << 3) | (b >> 2); + } + + static inline void unpack_color_unscaled(uint32_t c, uint32_t& r, uint32_t& g, uint32_t& b) + { + r = (c >> 11) & 31; + g = (c >> 5) & 63; + b = c & 31; + } + }; + + struct bc4_block + { + enum { cBC4SelectorBits = 3, cTotalSelectorBytes = 6, cMaxSelectorValues = 8 }; + uint8_t m_endpoints[2]; + + uint8_t m_selectors[cTotalSelectorBytes]; + + inline uint32_t get_low_alpha() const { return m_endpoints[0]; } + inline uint32_t get_high_alpha() const { return m_endpoints[1]; } + inline bool is_alpha6_block() const { return get_low_alpha() <= get_high_alpha(); } + + inline uint64_t get_selector_bits() const + { + return ((uint64_t)((uint32_t)m_selectors[0] | ((uint32_t)m_selectors[1] << 8U) | ((uint32_t)m_selectors[2] << 16U) | ((uint32_t)m_selectors[3] << 24U))) | + (((uint64_t)m_selectors[4]) << 32U) | + (((uint64_t)m_selectors[5]) << 40U); + } + + inline void set_selector_bits(uint64_t v) + { + for (uint32_t i = 0; i < 6; i++) + { + m_selectors[i] = (uint8_t)v; + v >>= 8; + } + } + + inline uint32_t get_selector(uint32_t x, uint32_t y, uint64_t selector_bits) const + { + assert((x < 4U) && (y < 4U)); + return (selector_bits >> (((y * 4) + x) * cBC4SelectorBits)) & (cMaxSelectorValues - 1); + } + + static inline uint32_t get_block_values6(uint8_t* pDst, uint32_t l, uint32_t h) + { + pDst[0] = static_cast(l); + pDst[1] = static_cast(h); + pDst[2] = static_cast((l * 4 + h) / 5); + pDst[3] = static_cast((l * 3 + h * 2) / 5); + pDst[4] = static_cast((l * 2 + h * 3) / 5); + pDst[5] = static_cast((l + h * 4) / 5); + pDst[6] = 0; + pDst[7] = 255; + return 6; + } + + static inline uint32_t get_block_values8(uint8_t* pDst, uint32_t l, uint32_t h) + { + pDst[0] = static_cast(l); + pDst[1] = static_cast(h); + pDst[2] = static_cast((l * 6 + h) / 7); + pDst[3] = static_cast((l * 5 + h * 2) / 7); + pDst[4] = static_cast((l * 4 + h * 3) / 7); + pDst[5] = static_cast((l * 3 + h * 4) / 7); + pDst[6] = static_cast((l * 2 + h * 5) / 7); + pDst[7] = static_cast((l + h * 6) / 7); + return 8; + } + + static inline uint32_t get_block_values(uint8_t* pDst, uint32_t l, uint32_t h) + { + if (l > h) + return get_block_values8(pDst, l, h); + else + return get_block_values6(pDst, l, h); + } + }; + +} + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright(c) 2020 Richard Geldreich, Jr. +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain(www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non - commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain.We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors.We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/external/bc7enc_rdo/rgbcx_table4.h b/external/bc7enc_rdo/rgbcx_table4.h new file mode 100644 index 0000000000..cda3f0a1ac --- /dev/null +++ b/external/bc7enc_rdo/rgbcx_table4.h @@ -0,0 +1,969 @@ +{ 202,403,51,450,318,23,120,13,494,5,523,260,77,21,15,128,48,141,33,817,137,269,12,102,1,475,4,326,7,32,453,64,196,115,730,49,165,2,82,342,6,401,180,31,899,59,402,351,197,17,125,457,14,386,40,3,61,58,37,221,95,16,99,719,98,20,10,854,117,153,27,576,134,341,308,216,11,194,30,24,922,285,9,50,86,65,19,615,391,44,75,237,177,90,352,93,56,22,18,116,70,96,247,291,45,110,105,60,57,54,43,36,0,217,421,203,46,629,569,365,210,92,69,66,38,28,144,551 }, +{ 13,115,51,23,141,217,269,137,318,202,77,21,317,5,64,4,291,2,352,7,32,33,6,372,0,48,365,3,102,349,12,341,177,180,10,237,9,221,403,165,752,22,11,244,28,308,15,128,247,197,49,16,494,125,98,40,120,498,31,90,304,143,95,93,437,196,144,86,44,864,153,18,678,342,901,39,24,30,203,160,117,105,475,14,482,253,457,873,59,35,579,36,569,450,206,72,99,17,717,401,241,453,110,546,201,83,421,61,37,326,260,509,284,275,96,8,109,57,391,386,468,134,82,961,822,576,89,68 }, +{ 29,58,262,74,287,52,151,331,171,500,396,334,75,27,635,220,53,1,26,409,6,324,5,70,145,223,163,112,73,50,45,549,445,113,297,19,675,651,46,61,502,307,14,17,34,573,186,80,394,436,583,315,138,38,21,767,250,491,820,350,64,2,32,357,42,852,25,764,309,67,802,96,631,48,606,780,243,881,103,241,115,807,13,4,155,181,11,106,611,614,278,153,23,108,69,845,841,308,400,72,853,200,66,196,226,237,587,632,24,281,785,133,128,125,117,380,95,474,294,83,197,90,161,134,366,236,760,311 }, +{ 40,51,62,33,453,14,23,56,730,196,475,99,12,34,139,775,153,403,585,440,102,38,130,47,162,17,108,880,117,11,202,57,361,84,123,27,184,236,4,128,19,2,670,136,54,26,111,134,13,161,277,719,152,6,377,183,5,59,211,53,412,485,46,328,77,711,78,21,556,452,65,283,421,129,37,514,507,402,176,29,25,229,401,488,757,45,76,205,899,322,32,177,494,527,383,497,457,249,300,114,215,555,386,119,133,0,349,115,3,862,1,165,73,41,523,72,200,64,291,217,80,305,22,318,194,308,18,714 }, +{ 33,51,102,202,23,128,196,13,64,153,40,12,141,99,403,15,10,269,77,139,475,453,5,318,117,2,14,134,1,86,165,120,401,3,0,22,137,6,197,277,17,402,180,21,421,494,37,115,31,82,38,9,95,342,93,7,110,351,125,457,65,36,361,49,54,19,260,111,44,291,237,28,8,11,194,961,719,352,217,18,485,523,308,730,386,326,210,450,328,300,98,899,27,32,341,48,25,670,34,50,629,26,24,503,498,285,317,67,817,143,365,177,509,41,164,116,757,55,46,203,569,898,373,114,144,16,349,775 }, +{ 13,23,51,115,77,177,202,4,141,32,40,33,217,64,128,90,269,102,59,318,11,291,0,137,403,22,21,7,165,197,15,210,247,180,16,117,153,352,14,2,125,494,10,49,237,18,95,453,6,9,1,24,28,349,475,216,12,342,317,304,3,196,86,98,275,134,31,17,35,25,365,308,19,341,44,36,372,30,93,20,401,76,241,402,139,752,57,244,105,386,37,26,260,110,288,120,116,48,326,421,221,285,901,38,99,82,498,39,468,55,523,143,111,482,65,300,214,450,203,457,351,43,29,27,8,576,678,569 }, +{ 235,26,19,648,624,630,78,47,122,444,145,27,665,112,85,42,390,416,813,113,130,65,680,64,711,25,453,108,644,6,53,73,138,256,95,412,22,730,29,289,90,194,34,525,74,690,32,297,128,45,196,250,287,133,445,86,283,214,236,237,137,222,884,200,746,59,52,312,77,280,632,0,17,114,835,51,123,14,288,881,28,307,180,57,381,141,183,70,4,472,10,33,76,337,3,389,247,315,2,18,368,849,874,573,23,58,544,153,151,436,102,322,99,352,36,1,177,714,719,125,597,63,165,519,285,49,13,396 }, +{ 64,352,751,141,217,247,237,304,269,954,437,372,177,947,312,86,318,349,180,875,95,864,291,597,498,128,678,77,232,92,115,317,752,32,202,588,59,197,579,90,638,392,652,76,210,546,260,1,448,126,340,164,18,303,13,165,275,14,582,120,633,205,179,69,156,296,35,230,391,888,267,572,28,16,379,851,660,96,717,314,0,23,863,49,195,39,223,5,386,137,75,509,107,571,563,46,194,526,309,21,450,342,288,140,83,24,330,87,70,50,12,9,514,365,246,199,100,65,36,385,55,125,658,244,326,264,696,453 }, +{ 642,898,638,180,901,341,82,197,951,10,762,253,165,15,752,811,479,244,143,365,569,873,242,110,506,351,31,8,116,55,137,115,317,499,141,352,44,338,206,373,386,854,164,22,758,93,37,217,36,1,417,77,629,661,21,717,33,9,564,160,401,456,30,4,203,559,5,509,86,28,326,102,282,268,535,0,13,308,170,269,11,498,391,51,120,2,457,64,704,965,40,32,23,299,421,482,148,615,39,318,533,3,169,961,801,202,260,851,576,117,247,221,349,372,670,968,403,468,291,285,128,824,134,719,237,734,177,342 }, +{ 51,221,23,254,308,438,21,125,166,39,530,656,159,401,49,13,158,115,386,421,688,613,629,228,743,48,518,190,627,453,0,829,333,30,105,912,341,9,670,424,827,165,406,217,404,840,669,391,137,274,197,301,286,272,170,468,457,95,89,22,770,242,33,310,109,569,938,393,960,1,101,36,88,773,538,364,890,131,616,100,28,219,456,344,64,479,679,734,327,846,365,261,55,317,32,5,4,492,180,68,67,482,237,475,793,385,2,367,16,7,347,293,371,224,86,35,719,762,578,332,202,102,442,865,12,949,351,98 }, +{ 1,143,31,197,44,116,242,2,180,125,23,55,206,13,93,498,5,8,115,341,36,752,638,338,141,268,64,901,165,456,0,203,951,546,479,365,873,253,417,4,569,137,15,762,506,237,12,352,965,120,3,299,244,170,164,7,22,282,51,144,6,86,864,21,269,384,558,128,37,9,457,326,844,492,160,961,391,499,202,509,599,870,260,317,685,434,127,217,824,393,373,489,629,77,98,903,308,33,559,332,811,473,301,318,401,102,963,28,386,564,918,367,110,105,911,285,959,523,134,482,740,678,169,617,14,96,758,450 }, +{ 12,23,51,37,13,99,14,38,15,196,328,54,21,134,153,64,453,401,117,128,2,33,139,120,308,5,82,141,361,0,277,29,403,351,719,165,10,115,629,457,46,475,89,450,22,32,110,260,1,31,269,93,9,137,8,386,202,125,86,18,40,111,52,180,17,49,48,4,102,72,477,523,474,326,318,45,898,3,44,402,197,77,730,203,28,24,6,19,421,854,961,352,237,194,114,65,155,373,116,50,36,498,242,236,221,98,55,16,25,7,217,391,569,880,615,41,546,144,509,59,316,79,57,514,494,317,899,488 }, +{ 13,23,15,961,457,115,569,51,165,38,197,2,4,1,474,5,719,99,629,77,0,14,37,401,3,33,7,94,6,128,11,40,10,453,59,64,153,79,509,482,54,17,21,328,111,89,139,277,12,102,498,188,9,152,141,734,76,157,873,386,196,402,32,475,177,117,72,391,36,8,730,758,237,45,304,57,403,134,579,148,202,775,22,523,341,29,431,318,317,308,120,546,62,313,468,437,86,405,217,180,361,50,269,46,342,90,31,143,16,48,44,339,184,19,49,27,18,749,41,450,880,542,205,901,300,216,164,52 }, +{ 15,10,12,0,1,23,961,197,5,82,2,4,3,120,134,457,7,165,260,33,51,9,6,11,351,31,22,21,14,64,8,180,40,450,110,141,386,898,719,523,37,17,153,102,77,117,44,341,217,32,326,93,36,854,269,317,115,18,730,352,49,922,196,365,202,901,28,38,99,59,291,817,125,318,16,569,453,403,55,498,475,111,48,30,94,372,349,342,752,41,391,139,72,474,128,19,899,373,210,203,57,873,661,177,494,98,79,86,143,95,509,27,76,401,89,864,615,304,116,431,144,482,758,137,35,45,29,277 }, +{ 23,51,13,961,719,457,730,401,453,386,758,165,99,899,328,0,10,9,2,1,308,15,134,33,7,6,5,117,54,12,153,11,523,4,391,403,197,629,40,139,14,22,361,28,202,509,128,402,236,231,888,488,369,111,87,57,196,514,79,115,880,317,65,35,817,377,3,291,46,176,77,49,44,38,31,8,450,64,585,477,244,180,141,498,120,507,412,283,102,59,775,475,114,108,62,52,546,237,188,160,157,152,137,94,452,383,260,95,56,440,215,830,774,757,622,569,494,485,472,342,330,300,275,210,203,201,187,144 }, +{ 134,898,82,117,13,23,33,102,153,77,351,341,196,197,110,260,961,180,901,10,120,165,115,111,457,8,450,854,177,40,569,51,94,365,37,11,922,523,277,4,326,291,0,79,55,342,1,9,93,642,12,217,59,352,817,202,5,730,317,21,873,719,141,31,661,811,629,349,16,98,509,373,386,453,372,44,64,18,54,32,3,139,203,116,615,275,22,125,269,210,899,38,318,128,2,144,752,57,479,49,704,17,28,90,30,6,564,638,95,7,403,475,137,304,494,758,474,300,48,36,391,237,498,482,105,499,14,39 }, +{ 60,126,167,191,18,71,35,121,199,16,271,24,212,92,146,150,118,575,107,68,727,566,446,132,21,273,463,108,246,7,279,467,48,653,695,608,517,589,1,101,9,598,5,354,14,649,55,562,345,522,97,98,441,375,140,0,195,22,407,640,149,28,32,223,10,156,49,298,125,13,423,755,508,953,363,221,23,411,314,46,50,781,346,447,154,37,12,818,356,95,647,302,31,230,43,69,674,2,388,20,783,809,232,621,359,189,267,362,124,175,67,293,105,51,109,65,360,493,520,236,163,607,535,75,165,103,39,33 }, +{ 136,62,129,123,128,41,162,789,211,618,249,214,183,678,248,507,38,216,710,119,152,177,269,130,64,17,57,141,245,597,318,205,184,4,179,90,503,77,59,402,305,106,3,313,229,157,266,54,210,139,102,257,275,556,209,45,117,40,13,11,292,99,328,76,457,33,339,23,452,378,217,86,291,80,194,471,369,947,349,383,188,524,497,304,542,381,255,153,519,751,494,240,361,84,803,588,202,32,440,372,115,403,742,164,56,529,12,137,37,790,21,5,504,432,207,196,10,14,285,215,134,342,36,15,587,0,358,165 }, +{ 523,494,475,403,51,899,817,453,202,23,450,730,421,13,805,120,128,757,102,5,4,719,33,21,342,318,958,326,196,153,260,402,7,1,386,12,48,133,65,20,32,457,141,9,8,615,16,114,108,14,10,77,19,0,361,137,17,300,84,82,59,351,123,3,194,576,11,700,221,401,269,95,64,49,177,541,27,40,57,26,115,125,117,22,474,308,99,37,862,701,304,193,56,503,91,697,551,134,105,283,50,129,86,24,249,240,2,942,814,673,671,579,569,519,490,468,437,397,389,365,352,349,306,291,281,247,218,208 }, +{ 59,4,84,62,77,40,3,102,76,119,27,33,56,177,32,12,161,73,21,196,136,123,17,210,11,193,216,80,90,18,64,57,128,51,6,25,13,611,300,63,202,65,48,415,42,605,106,23,152,0,99,275,22,217,803,26,49,205,122,503,291,53,5,2,37,29,349,87,667,453,402,318,209,361,19,690,283,403,718,445,342,129,14,61,194,79,436,245,370,397,297,141,86,862,485,269,240,52,91,85,494,304,510,153,95,573,115,1,45,416,249,184,139,757,475,255,133,81,529,292,41,469,111,680,250,813,113,31 }, +{ 43,414,422,319,20,819,945,7,0,376,61,904,97,470,325,173,707,5,804,693,22,104,14,49,560,426,330,95,595,32,392,17,107,147,207,887,577,246,46,21,324,356,77,74,344,725,58,52,1,198,113,784,347,28,27,18,66,38,76,427,135,105,329,31,858,25,12,158,526,291,263,112,9,523,357,239,232,120,115,362,81,230,359,118,19,10,715,450,119,241,64,44,259,424,315,24,260,140,13,942,292,350,165,102,56,45,37,36,861,125,879,817,719,600,86,33,30,2,814,204,500,476,205,98,26,16,180,179 }, +{ 13,23,51,217,115,77,141,2,33,10,4,0,64,7,180,6,102,40,11,22,317,9,14,269,352,3,1,5,202,197,15,177,341,165,59,237,291,125,365,16,12,128,28,18,247,318,308,31,153,196,349,95,372,117,36,17,24,137,49,498,98,752,32,403,304,134,90,475,342,457,453,210,143,401,93,391,19,86,44,120,30,901,275,386,8,57,105,216,244,164,569,39,65,494,509,20,29,110,25,421,864,961,139,116,873,38,55,27,719,37,35,82,50,221,482,46,260,56,26,546,450,316,468,351,326,45,899,523 }, +{ 28,49,24,105,35,65,159,0,55,109,1,16,239,320,14,7,347,95,424,158,168,170,124,9,2,185,67,68,301,18,169,654,286,149,12,289,175,36,5,127,154,21,10,71,69,114,98,64,740,83,512,39,108,13,272,48,164,327,31,23,623,420,374,395,46,50,713,125,371,518,51,194,338,577,312,6,37,143,916,22,346,344,429,570,32,86,835,103,336,761,101,928,44,638,242,890,737,60,133,202,237,293,115,538,30,762,128,141,137,38,302,398,747,484,479,165,439,189,96,844,610,92,221,299,892,308,54,131 }, +{ 15,10,0,12,1,457,13,165,5,2,82,9,4,117,120,7,961,3,134,6,64,22,197,14,11,260,351,37,33,32,51,21,31,719,77,141,180,38,341,17,8,365,450,317,102,217,153,28,523,269,110,115,44,93,898,372,854,202,386,18,196,40,291,326,99,901,49,730,349,453,352,16,128,125,922,36,237,72,111,318,509,177,76,59,342,403,48,475,817,401,98,391,29,30,89,752,90,474,373,569,27,137,45,704,24,275,19,95,328,55,494,54,498,86,899,143,437,139,210,304,629,758,57,25,402,116,546,873 }, +{ 7,97,71,14,92,150,140,60,149,189,18,16,517,69,336,398,298,154,124,66,302,35,43,68,175,467,273,38,230,118,251,508,314,429,24,2,28,399,172,232,20,0,882,36,279,55,423,173,267,6,46,1,107,126,67,375,83,566,108,22,74,65,547,29,607,104,192,5,426,246,135,847,52,81,531,420,109,596,501,10,809,9,13,952,387,533,505,608,484,169,21,458,330,674,628,49,823,86,25,345,158,51,191,705,48,101,637,12,386,723,439,570,324,653,462,185,168,697,779,535,783,796,346,114,722,861,799,95 }, +{ 102,4,77,59,33,210,11,342,40,32,12,64,128,139,624,5,503,196,202,90,494,21,300,91,51,233,318,403,23,17,291,177,416,269,275,678,141,817,450,13,529,0,25,10,3,485,277,153,151,49,37,42,789,216,496,457,115,57,197,680,214,22,18,788,523,249,205,27,389,194,86,72,453,122,145,480,81,26,961,280,148,138,134,111,104,74,65,6,899,730,204,165,120,89,209,804,475,348,123,95,19,636,396,174,693,651,287,125,869,750,629,306,257,76,48,386,349,221,61,45,803,784,544,402,397,240,58,31 }, +{ 25,42,256,235,19,122,85,436,813,6,297,680,53,26,573,416,233,445,390,63,605,73,250,343,80,27,65,630,389,283,106,255,194,444,45,17,138,544,396,690,525,503,648,269,141,64,119,33,145,123,611,113,128,102,174,112,84,91,29,789,70,624,4,412,77,0,736,483,209,358,205,597,179,56,117,249,243,280,287,108,153,650,196,619,114,32,136,78,678,18,746,348,5,129,2,495,200,57,14,378,76,240,40,511,86,137,161,90,216,10,684,881,23,665,222,22,432,12,289,51,177,1,21,214,370,245,134,11 }, +{ 102,300,402,128,202,403,33,23,342,77,40,99,21,757,84,12,115,196,483,318,19,119,117,210,494,139,90,32,25,4,569,153,469,277,26,475,91,134,63,59,80,523,73,14,275,51,485,453,44,36,31,10,6,48,576,37,49,22,352,193,56,165,18,790,152,415,53,42,880,125,370,291,177,74,13,1,197,180,81,64,62,3,814,611,343,161,136,106,391,386,138,129,66,54,45,38,34,27,17,2,678,11,378,770,697,543,511,504,120,65,82,226,961,899,862,730,718,690,643,317,256,235,89,87,72,61,47,605 }, +{ 158,109,9,39,49,0,35,65,168,68,24,124,159,344,185,154,22,55,333,577,272,175,254,289,810,169,16,327,101,346,95,105,570,149,747,627,286,374,71,310,703,67,439,793,30,28,252,429,131,419,1,455,912,114,610,671,347,18,424,364,7,36,835,100,194,14,21,420,239,336,86,688,10,634,69,108,83,538,706,709,578,484,23,518,2,320,64,51,13,166,48,12,60,118,962,170,128,5,190,133,913,398,50,46,125,677,535,137,127,96,98,345,302,189,826,584,103,251,31,224,638,37,265,92,160,722,219,32 }, +{ 237,498,546,180,115,197,873,287,5,34,923,61,297,509,165,45,400,80,445,2,311,341,3,58,52,468,176,231,186,881,250,64,13,56,151,48,17,217,62,490,14,153,141,23,513,901,51,391,21,667,334,247,352,903,171,84,482,161,549,10,72,401,243,53,738,592,196,46,316,73,719,308,869,666,396,365,645,106,130,585,125,887,635,569,215,86,749,1,207,864,472,177,211,317,22,659,573,37,95,40,227,136,33,372,527,29,507,483,128,651,90,469,229,629,436,833,721,787,758,515,437,54,36,27,117,32,430,304 }, +{ 120,968,704,260,373,450,202,137,661,326,717,269,349,110,217,77,318,473,1,652,95,851,291,93,177,244,33,21,372,141,719,102,351,82,518,156,51,48,848,340,210,928,713,160,922,98,296,128,817,146,86,827,304,264,32,197,225,115,623,317,212,96,23,751,386,854,35,13,7,863,698,180,919,494,385,64,800,253,730,678,523,140,15,70,65,303,453,437,275,247,49,642,341,352,127,37,888,737,5,942,898,167,776,87,658,744,692,615,389,203,101,36,478,196,190,393,59,55,363,132,118,850,163,131,916,836,811,599 }, +{ 44,116,144,268,489,434,367,384,127,918,98,855,940,948,617,206,203,959,93,137,105,31,332,393,492,22,131,9,911,282,276,141,219,202,0,49,284,28,299,120,326,36,39,417,269,1,51,523,30,473,935,899,293,318,352,125,23,395,253,64,13,244,55,160,10,551,128,95,403,373,201,143,450,115,260,8,692,386,21,86,109,110,187,730,165,456,272,737,159,791,261,499,170,7,16,341,728,295,77,951,5,48,96,944,559,242,258,365,371,494,24,254,2,317,421,578,752,18,224,716,164,197,391,285,301,498,924,12 }, +{ 77,13,23,51,102,33,141,64,115,0,177,291,217,202,269,6,2,7,4,318,40,14,180,128,59,1,10,196,117,153,22,11,16,15,352,95,134,9,137,3,165,197,210,18,86,403,17,237,28,49,304,342,125,275,12,349,24,341,247,317,453,5,90,19,475,216,57,25,36,494,65,365,27,20,457,31,120,143,93,498,372,308,401,402,391,29,26,752,260,98,110,719,312,82,961,164,45,56,386,35,44,139,21,99,509,30,730,194,38,244,144,351,8,37,111,203,421,546,50,300,96,288,285,208,46,54,39,901 }, +{ 15,4,11,40,141,165,23,32,202,180,21,197,64,269,125,13,59,386,120,93,51,110,77,5,128,57,318,457,82,3,12,523,260,0,17,49,10,450,730,134,117,31,403,1,719,352,8,102,137,22,115,90,153,899,351,98,44,326,237,2,494,475,139,203,391,144,9,111,961,56,37,402,116,177,210,48,801,453,65,196,6,95,217,341,509,14,421,99,76,291,7,86,216,373,758,401,498,18,28,752,27,36,275,365,300,72,342,615,901,854,898,247,317,45,922,105,127,30,308,152,304,123,817,551,19,791,393,55 }, +{ 176,231,585,62,56,161,412,34,236,527,14,440,202,3,57,17,283,361,84,402,136,23,369,51,128,4,803,13,130,472,383,548,322,507,46,719,457,555,53,73,38,514,503,250,381,119,59,200,162,64,77,452,401,495,153,469,11,229,318,29,205,211,196,497,453,152,2,730,556,240,33,390,27,372,210,184,106,961,215,183,123,65,757,629,99,494,108,129,52,37,18,0,21,40,529,515,475,54,377,828,275,209,197,217,117,1,899,543,300,269,245,862,483,305,45,504,378,342,297,111,102,76,50,24,10,95,12,611 }, +{ 101,9,237,180,0,352,217,230,498,141,100,88,702,115,68,317,83,175,213,39,365,1,252,30,345,752,64,546,482,208,321,864,265,341,490,562,13,372,818,336,952,399,128,69,36,244,197,212,96,410,21,154,689,269,158,28,251,289,166,194,65,382,419,48,443,759,596,901,873,23,468,67,51,16,547,831,797,165,570,55,285,264,930,329,49,756,646,346,124,694,144,391,579,799,854,247,35,190,189,164,699,755,24,364,203,777,323,238,429,569,768,435,37,724,466,201,349,12,509,723,840,50,789,826,160,676,291,137 }, +{ 28,22,49,109,9,39,1,95,0,105,158,168,83,185,159,55,86,30,96,208,141,272,128,12,420,5,505,13,31,327,169,160,131,69,67,137,23,202,21,64,115,251,286,254,51,35,98,127,2,101,244,44,238,194,48,197,50,531,10,7,217,927,180,533,175,352,584,103,285,269,165,37,65,218,100,535,317,323,68,374,946,239,597,24,837,735,289,797,896,418,341,347,333,601,170,116,346,365,237,143,318,345,678,32,344,125,16,164,498,627,253,752,402,577,424,310,4,219,99,403,252,6,312,144,578,166,789,242 }, +{ 72,76,38,89,51,477,4,413,308,179,474,12,152,401,57,148,59,11,205,99,54,421,670,23,245,17,629,431,79,488,405,90,94,184,13,569,21,453,32,229,128,3,358,45,197,14,475,188,77,528,157,464,316,165,257,543,115,41,288,202,457,428,961,216,15,341,403,40,734,266,5,339,749,10,64,33,2,452,328,217,645,460,141,775,111,313,468,1,247,361,102,277,432,743,365,381,587,482,62,509,37,269,0,769,497,471,285,137,402,177,794,449,155,6,318,542,377,86,805,181,129,9,369,7,153,524,349,243 }, +{ 23,13,457,961,15,51,165,629,12,719,11,3,197,2,1,37,0,117,5,569,7,509,33,153,40,196,6,134,328,99,9,542,391,237,17,474,139,111,277,64,453,54,730,546,386,401,57,4,38,14,514,45,775,59,734,361,115,21,102,41,498,477,77,873,431,475,880,369,339,19,587,62,89,377,29,27,523,479,248,128,758,308,482,352,291,275,210,32,8,468,413,164,94,155,659,428,864,752,751,579,494,405,403,397,342,247,240,216,184,180,171,148,122,106,91,84,72,56,52,34,721,188,157,79,654,597,312,95 }, +{ 254,310,100,166,625,9,265,272,30,731,190,39,404,557,688,1,578,131,98,593,101,616,438,264,0,31,829,252,795,716,127,21,382,51,812,49,88,93,613,48,105,827,478,274,18,639,329,36,44,656,23,203,363,68,411,96,682,159,16,116,327,160,242,128,333,13,5,938,910,224,35,447,286,676,144,165,921,530,67,228,137,520,125,219,223,22,724,455,86,33,846,110,284,118,627,905,120,24,77,115,65,83,737,435,32,158,856,664,95,326,924,821,102,518,167,960,202,385,395,931,371,406,10,69,403,55,709,40 }, +{ 51,23,202,99,128,12,15,13,120,403,10,453,153,196,141,64,165,5,318,117,475,82,197,269,401,33,260,2,37,3,1,110,0,14,180,93,386,22,21,450,421,31,402,115,125,137,17,351,134,326,102,494,6,38,308,457,44,36,4,77,11,95,523,28,49,7,9,8,719,237,352,730,32,203,116,391,373,57,18,98,59,361,86,54,19,89,285,775,328,277,757,498,341,854,111,16,27,139,72,45,48,41,143,79,509,961,898,758,342,291,65,474,488,30,569,217,144,56,901,242,899,670,34,24,678,817,922,194 }, +{ 38,99,542,453,139,196,117,457,23,328,13,111,37,134,961,11,12,775,401,51,587,40,474,153,277,477,54,629,475,33,719,79,165,41,14,569,157,4,413,188,89,115,64,339,734,308,15,361,369,197,313,102,94,247,155,45,248,236,57,514,72,758,402,128,181,485,377,509,86,237,488,0,403,266,141,46,10,164,211,471,184,588,65,180,29,2,875,528,490,95,76,22,5,50,17,7,721,670,391,49,18,503,817,789,431,21,257,216,32,208,305,177,136,1,880,803,352,140,342,260,229,202,67,867,597,59,757,899 }, +{ 85,6,233,343,91,25,138,26,63,174,256,235,122,29,19,81,283,65,605,648,42,718,445,370,4,74,59,573,389,416,194,396,45,177,544,297,355,64,141,348,145,77,114,216,123,280,436,881,133,680,432,87,70,511,137,108,27,128,57,630,0,11,415,18,90,503,619,86,412,40,287,17,397,789,222,14,73,269,32,66,52,291,204,243,56,22,209,275,597,104,76,72,529,148,678,84,80,119,53,643,304,210,208,285,33,46,2,523,5,311,813,636,510,381,255,10,524,112,102,403,289,214,152,483,62,736,525,349 }, +{ 49,5,197,408,193,18,20,61,713,350,173,21,64,97,509,95,729,841,263,0,241,32,165,104,43,422,65,125,35,510,60,474,7,120,102,523,397,14,48,232,260,128,347,68,546,387,38,58,693,403,16,105,81,71,115,784,10,560,91,72,6,862,27,92,51,707,344,22,9,237,319,29,414,107,55,66,643,147,663,450,391,498,240,357,70,468,90,2,751,370,98,701,84,819,903,330,207,623,141,63,52,718,569,308,864,475,25,159,74,283,356,326,158,86,12,900,494,137,171,42,221,202,85,734,386,847,873,650 }, +{ 434,144,268,489,116,384,367,855,940,206,617,332,959,911,127,93,44,98,203,137,276,282,219,202,131,22,141,31,417,284,918,9,36,105,28,201,269,258,244,120,473,23,318,523,51,326,253,1,450,39,0,30,352,160,948,143,10,13,8,110,49,64,125,55,551,935,951,692,295,187,128,21,86,261,567,260,499,559,373,96,224,170,899,386,242,115,299,817,95,662,728,293,165,197,811,395,159,48,492,365,109,317,498,403,37,752,341,393,836,638,717,5,180,254,77,15,272,851,164,24,944,494,237,385,16,7,301,391 }, +{ 106,17,255,32,64,62,136,292,162,129,476,352,237,214,123,519,241,720,141,752,498,247,211,41,119,209,864,507,249,180,179,290,710,954,425,148,317,618,197,90,186,21,5,115,248,77,76,269,217,183,45,48,14,227,130,3,263,12,304,38,165,128,243,546,23,2,358,207,117,164,57,579,51,245,205,54,454,313,152,350,177,13,155,873,339,270,50,4,428,378,157,655,449,496,305,400,365,171,86,1,437,742,556,372,369,408,471,328,266,216,33,80,153,139,460,181,597,542,316,40,102,29,751,59,361,257,84,341 }, +{ 2,29,52,58,5,50,27,6,53,26,171,45,366,151,14,287,61,19,357,74,17,73,764,1,113,34,297,309,218,262,181,38,133,48,583,70,487,600,739,334,25,396,250,760,281,96,186,445,516,75,502,635,315,114,308,108,3,223,80,549,42,545,103,331,324,106,200,163,112,21,785,67,436,145,603,238,401,243,51,37,651,433,155,834,491,675,236,99,745,209,161,13,845,32,227,115,409,23,894,388,765,12,56,36,311,255,220,920,766,138,323,453,573,24,594,62,59,65,832,754,160,86,54,307,283,83,285,629 }, +{ 130,711,412,381,59,196,210,730,236,77,402,401,803,202,453,361,99,108,283,291,153,57,183,128,503,421,4,51,14,719,432,11,318,275,117,23,529,757,3,475,13,339,457,133,488,123,670,899,603,369,134,33,65,46,266,386,2,47,754,12,32,389,114,38,838,56,300,40,102,177,739,111,961,523,629,328,758,64,54,90,216,514,269,21,594,0,743,37,775,657,576,894,403,152,650,194,471,34,200,5,832,162,50,494,805,472,18,165,22,6,29,181,95,285,10,129,188,480,15,1,322,24,229,342,139,714,79,86 }, +{ 13,23,51,217,115,77,141,33,4,64,10,202,102,180,0,40,317,352,177,11,291,269,197,165,9,32,22,59,7,2,128,318,341,18,15,21,237,137,117,6,16,5,95,3,153,403,125,349,365,14,247,752,49,12,196,28,31,453,90,1,24,98,304,17,143,494,372,308,244,475,498,134,210,221,120,901,401,36,342,421,86,93,44,30,216,260,35,164,457,19,110,275,569,391,57,386,203,105,326,55,864,82,402,523,241,139,8,450,39,873,730,719,144,65,20,253,160,27,351,116,72,29,437,509,37,629,285,546 }, +{ 23,13,202,51,21,269,141,120,5,48,128,137,318,523,15,102,125,98,1,450,93,403,165,260,64,197,77,33,180,12,457,32,221,82,817,115,44,110,31,7,352,494,2,386,475,308,117,4,153,326,14,401,719,116,196,421,37,237,16,453,9,144,730,351,17,99,6,30,291,402,203,18,11,899,19,24,217,96,498,40,391,20,10,39,95,854,3,341,50,134,349,293,373,177,615,35,365,65,139,0,45,285,372,61,8,317,90,922,805,901,342,59,43,898,76,29,509,393,127,86,752,170,208,105,89,72,67,38 }, +{ 2,1,46,14,52,29,171,67,38,103,58,75,70,69,163,24,83,223,5,96,309,181,54,186,112,334,108,45,394,675,155,366,220,35,238,151,16,236,409,18,281,760,307,133,537,400,36,71,189,328,227,433,28,218,766,61,545,65,17,487,149,7,48,552,21,502,0,12,262,19,87,6,765,114,25,388,267,150,113,820,418,50,549,27,26,37,251,22,99,335,430,486,32,60,536,287,68,587,9,828,55,64,74,243,10,516,137,160,86,66,128,311,600,696,739,852,583,451,336,315,514,72,135,80,175,145,89,294 }, +{ 15,2,1,13,0,10,153,5,9,7,12,23,3,14,117,6,165,134,4,719,457,196,82,341,180,197,33,22,120,115,77,8,961,21,64,217,102,317,569,365,351,141,260,32,31,18,28,291,11,16,110,111,37,125,93,386,17,202,40,450,269,372,730,901,99,898,44,36,177,854,38,342,349,352,48,523,758,318,453,57,128,401,391,304,72,76,403,19,922,326,752,29,629,49,509,475,237,137,116,98,89,55,373,95,24,27,26,30,494,247,59,482,86,25,210,96,54,46,437,899,139,65,253,817,661,498,308,873 }, +{ 509,197,546,391,165,180,308,13,23,498,569,29,873,115,901,5,401,61,457,903,80,734,629,468,3,287,34,2,45,758,311,445,250,579,297,341,961,237,17,186,171,749,386,51,14,38,217,231,48,58,62,400,46,752,247,64,211,176,490,106,719,56,764,738,667,227,215,349,151,11,558,316,502,513,833,32,161,243,54,317,153,196,50,21,53,40,365,583,155,923,10,6,587,334,305,483,33,4,369,372,181,136,1,125,119,12,482,130,552,263,57,52,95,123,814,266,84,832,495,645,739,864,96,15,787,472,129,36 }, +{ 73,19,250,27,714,200,444,472,26,34,813,53,390,322,17,283,78,297,106,630,436,113,122,690,123,432,136,128,176,862,161,389,503,684,611,495,80,378,62,42,108,529,14,56,469,57,25,412,236,177,59,47,65,102,6,216,275,3,381,680,235,179,527,605,84,129,383,130,619,183,368,46,255,814,33,23,318,2,789,77,141,13,453,194,245,51,11,445,45,4,152,63,650,269,22,90,64,0,133,440,29,349,40,585,86,678,736,291,95,196,153,184,361,18,210,38,58,803,401,5,117,10,114,358,61,730,249,119 }, +{ 17,21,3,45,62,32,155,119,106,38,5,328,14,828,227,76,2,401,209,123,129,243,270,115,540,29,56,536,46,12,221,488,99,13,629,57,72,719,181,51,552,290,361,136,41,37,514,165,543,152,421,408,84,48,77,308,207,255,90,306,23,263,569,11,454,52,587,162,758,378,4,89,80,754,130,402,236,197,186,440,457,27,383,157,50,1,183,311,832,128,10,794,425,59,576,961,211,741,453,805,790,86,742,496,386,34,313,54,700,600,266,148,659,202,19,94,341,22,880,509,229,240,697,770,670,548,73,177 }, +{ 115,341,873,365,197,13,901,180,569,752,317,1,509,498,10,143,15,4,634,165,268,943,261,5,258,23,237,116,141,0,127,601,244,843,282,253,164,44,12,33,64,860,638,533,219,77,144,9,567,31,134,7,2,352,864,276,160,535,51,206,217,824,125,391,8,28,951,295,11,93,332,201,40,964,844,308,137,6,3,120,531,260,22,36,482,98,177,111,318,474,417,37,170,82,961,291,196,102,169,924,434,203,153,94,954,395,734,898,384,269,242,479,965,735,405,326,30,728,662,617,386,159,24,18,944,401,301,367 }, +{ 453,51,23,33,403,475,421,196,153,102,670,13,117,753,134,40,494,523,4,730,342,0,21,65,59,22,12,880,77,11,86,801,401,719,202,48,28,899,18,805,757,64,14,10,24,128,1,629,457,99,108,57,308,5,2,217,317,139,16,485,569,165,141,36,361,341,365,177,143,125,283,386,120,82,44,9,115,221,7,817,576,300,152,137,124,123,98,61,50,46,32,6,450,111,775,543,488,417,269,197,114,110,95,93,54,35,31,402,34,17,3,911,642,564,561,554,511,431,332,277,261,253,242,239,194,170,116,96 }, +{ 23,13,51,33,12,453,196,117,99,153,134,40,102,403,14,128,202,77,15,401,475,37,139,38,54,11,64,165,421,21,2,0,141,115,308,318,719,328,65,108,670,197,10,46,457,361,269,730,79,1,22,402,28,111,120,95,82,18,3,386,9,389,6,17,283,50,36,24,86,236,114,125,775,5,494,180,4,260,31,94,41,32,899,523,19,210,110,137,59,391,7,326,488,450,351,291,217,177,412,341,93,300,485,477,509,880,377,44,39,30,277,52,221,16,8,961,629,365,203,29,758,133,26,817,350,342,237,194 }, +{ 2,7,14,5,52,29,70,61,112,46,87,592,135,1,66,45,38,16,32,6,186,21,315,290,48,227,24,72,307,113,75,172,425,20,243,58,74,97,666,128,845,631,76,226,155,294,18,738,23,270,25,35,64,141,334,115,0,89,22,50,43,163,151,37,51,171,77,207,69,17,220,36,177,202,19,262,10,13,137,9,90,232,4,54,86,92,263,269,255,278,59,496,655,60,28,95,140,26,106,27,180,12,454,192,148,67,40,209,102,318,33,292,400,849,49,65,324,331,409,405,217,491,311,461,549,476,431,210 }, +{ 33,102,128,202,51,23,141,196,64,153,13,269,318,403,77,117,10,137,134,40,1,5,453,2,6,165,3,0,14,475,86,99,139,15,180,120,12,22,494,402,17,197,352,260,19,7,95,342,21,110,237,65,82,115,93,125,31,36,37,277,18,217,210,9,49,44,25,177,291,326,523,38,450,401,194,32,457,421,285,386,26,28,351,16,111,719,27,4,11,114,48,341,203,300,8,752,98,373,143,730,361,498,57,54,365,317,24,164,961,116,569,108,275,899,50,34,391,45,41,901,96,244,29,629,133,72,509,144 }, +{ 92,16,43,20,126,107,7,35,71,97,14,60,140,246,279,68,18,149,230,121,575,356,375,191,362,24,783,150,118,55,695,566,345,48,0,359,175,173,319,598,298,232,939,22,653,441,167,463,21,414,125,819,467,407,33,69,346,38,422,146,77,446,154,608,28,649,336,172,10,199,102,101,104,168,189,637,251,212,49,314,376,2,124,51,9,595,399,221,23,66,5,392,541,755,196,470,386,13,6,158,117,768,393,147,628,551,493,683,165,67,46,36,259,108,81,1,727,535,364,342,325,672,267,281,674,120,203,153 }, +{ 13,23,77,115,51,33,59,4,177,40,90,64,102,32,141,202,10,128,22,210,291,0,11,21,117,95,137,217,16,318,196,9,180,153,7,197,165,49,28,269,6,342,14,18,2,216,1,237,134,86,98,352,349,203,326,24,403,36,494,31,125,450,120,475,275,15,93,351,341,260,194,386,105,3,247,48,57,365,373,20,372,402,317,457,44,5,139,110,17,35,30,25,12,498,76,144,116,72,39,453,304,19,300,244,208,82,60,401,143,285,65,214,55,38,26,817,569,50,678,551,391,308,123,111,46,854,509,523 }, +{ 507,162,129,211,41,183,38,123,4,248,59,62,57,556,130,99,361,402,305,128,202,11,266,136,17,3,77,803,152,497,313,452,184,210,369,33,40,471,339,56,117,757,157,54,328,229,205,494,381,23,139,457,153,12,403,84,503,257,19,34,65,13,76,514,119,275,37,188,542,14,80,106,440,475,775,318,618,102,21,134,453,300,27,432,64,880,95,89,49,141,86,196,32,529,377,249,73,79,460,177,50,5,10,90,53,619,548,342,94,401,269,179,288,587,961,383,148,114,579,115,477,216,72,45,805,285,291,217 }, +{ 643,123,193,718,650,18,802,65,389,84,415,91,177,25,90,619,256,216,87,289,511,183,736,370,217,324,380,235,108,63,808,500,432,102,249,57,275,194,152,6,673,510,304,862,240,524,355,588,561,397,233,372,13,523,130,81,96,4,0,343,605,141,1,22,103,77,59,283,214,64,789,437,306,630,23,85,153,80,33,475,597,529,210,544,349,196,129,56,291,32,27,847,300,114,889,550,138,86,886,444,269,124,28,26,416,421,280,208,174,70,10,2,814,401,115,19,851,117,42,730,469,352,204,17,202,150,73,51 }, +{ 23,13,51,15,5,1,2,7,115,0,12,6,165,141,21,3,4,457,9,197,180,32,77,14,37,202,217,10,33,120,341,352,11,269,22,8,16,93,569,961,31,401,318,102,247,719,453,308,629,17,365,18,260,403,177,317,901,386,40,48,509,137,752,44,730,28,391,498,72,117,523,128,110,153,59,450,98,494,99,196,36,372,237,30,90,49,475,134,291,326,125,24,210,95,349,89,899,482,38,116,421,468,351,304,19,76,873,82,20,139,57,734,203,86,342,373,546,54,39,437,45,65,817,854,758,922,144,96 }, +{ 51,23,13,453,21,403,12,475,202,165,141,64,128,115,32,5,95,523,125,730,494,180,197,401,899,308,48,120,37,719,386,269,318,817,457,4,99,421,3,326,1,6,237,17,450,2,260,10,19,22,14,0,217,194,18,402,77,221,90,247,15,177,27,25,26,961,59,9,7,758,137,16,670,391,352,117,11,801,312,33,110,82,50,28,241,57,40,351,123,153,49,615,317,291,72,341,210,24,86,54,61,29,196,102,216,93,56,569,31,304,170,98,275,289,46,42,20,629,361,108,58,89,389,283,144,45,84,203 }, +{ 204,74,135,174,192,66,138,85,6,280,353,348,580,172,7,673,97,889,426,500,792,568,95,87,355,32,620,64,712,29,65,91,233,81,232,222,52,448,43,872,25,825,42,104,173,888,22,289,226,63,526,86,387,5,312,636,861,49,90,237,278,2,343,415,194,560,21,0,715,92,38,214,145,140,20,256,835,324,235,330,18,10,750,61,28,75,808,1,4,26,681,288,77,36,24,70,69,58,107,45,874,347,125,14,112,314,33,72,337,102,298,241,294,707,59,729,108,180,37,115,370,40,287,76,48,356,164,117 }, +{ 35,68,149,24,69,0,65,133,103,16,189,9,28,18,50,114,175,336,124,101,1,71,154,46,83,251,14,7,2,55,283,118,230,108,218,238,38,398,345,140,705,75,54,302,443,39,596,22,532,309,335,181,96,361,418,21,399,12,52,394,565,433,412,267,346,109,60,5,29,36,486,236,48,160,375,732,831,545,23,480,487,516,51,220,92,366,86,429,10,13,64,323,328,67,49,252,30,37,330,514,364,212,100,439,547,158,389,95,87,458,194,115,32,646,891,930,246,208,70,641,171,163,6,191,223,722,167,99 }, +{ 101,118,16,71,24,9,18,154,191,212,0,35,60,520,124,68,694,806,167,364,375,252,604,346,55,302,562,628,28,39,149,917,439,126,419,92,65,826,329,100,706,589,166,108,175,265,264,21,1,273,199,109,22,701,48,925,676,49,158,7,150,363,404,10,146,942,5,88,121,815,105,411,14,967,682,67,23,535,69,13,818,566,882,50,777,907,647,429,95,159,51,96,30,12,333,621,31,2,857,848,32,107,140,722,98,575,821,246,37,847,46,382,905,221,677,169,36,336,125,553,695,83,467,593,185,759,114,286 }, +{ 35,16,9,0,68,24,65,1,18,71,149,103,67,189,101,28,21,48,212,114,23,335,486,124,13,51,39,118,167,191,55,154,7,133,238,433,50,30,443,141,96,230,22,100,140,218,60,5,137,64,537,352,115,12,88,330,217,223,75,10,128,399,37,269,49,83,411,202,32,283,175,36,31,221,246,264,98,163,398,2,281,723,375,302,69,166,478,447,125,108,487,180,54,372,317,589,44,99,520,336,181,361,345,418,318,363,177,86,695,341,664,565,77,160,95,545,562,252,70,237,116,752,532,190,72,105,92,14 }, +{ 13,23,202,77,51,128,33,32,115,102,21,269,177,141,318,64,5,217,137,494,291,11,4,10,0,48,22,153,95,210,117,86,90,196,3,15,6,317,2,49,125,349,40,18,12,342,352,9,31,17,247,304,403,194,134,36,16,7,14,450,19,275,98,180,120,1,61,44,372,216,28,341,65,165,221,326,116,76,20,260,59,523,237,365,285,203,24,35,105,453,288,143,386,678,30,351,101,68,60,55,25,817,421,475,96,901,401,367,308,293,99,93,82,39,37,27,752,312,144,615,323,730,58,72,437,859,899,316 }, +{ 16,92,60,35,7,18,68,150,24,126,149,97,191,246,375,118,14,230,107,140,71,273,575,175,517,653,154,566,55,43,298,345,167,399,0,121,28,467,20,108,279,212,101,441,346,302,336,66,423,232,508,48,22,674,124,38,356,608,314,9,21,2,189,199,359,446,49,10,695,46,5,69,1,463,65,815,589,12,172,439,607,755,251,146,37,362,67,392,23,173,6,109,13,722,125,809,702,796,628,36,32,51,95,637,74,135,354,398,52,132,83,29,429,221,407,768,882,105,672,50,64,104,86,271,501,364,535,96 }, +{ 76,90,464,21,179,316,205,288,460,245,32,257,184,148,45,358,152,57,270,48,241,38,449,89,14,2,1,350,769,645,229,155,5,497,221,54,17,524,37,243,12,59,227,405,266,428,4,452,413,308,749,432,50,619,99,720,51,29,292,476,217,11,290,46,157,115,79,188,339,129,313,216,401,381,431,471,171,341,247,77,10,94,106,513,400,3,23,64,317,141,52,7,0,365,773,474,477,72,430,528,36,659,162,369,453,181,62,214,128,33,237,741,186,9,6,281,328,13,202,102,502,349,536,498,177,291,180,372 }, +{ 19,297,26,27,813,80,436,495,17,73,53,250,122,106,200,113,378,390,690,42,4,45,469,361,59,25,742,128,136,161,77,255,202,736,445,605,402,123,862,630,40,63,318,78,383,803,56,11,235,102,283,444,119,527,6,62,210,503,680,14,57,33,148,472,209,129,684,256,84,412,667,611,18,573,85,365,483,0,177,12,3,300,814,153,504,65,22,76,139,61,34,205,133,341,117,64,51,23,13,58,269,152,90,257,10,236,2,358,497,38,432,342,95,47,368,243,183,494,757,453,619,196,184,548,171,714,475,322 }, +{ 6,26,235,138,145,112,19,42,624,122,70,648,27,331,262,85,78,47,25,500,52,632,630,226,113,444,307,644,409,453,29,222,665,163,75,50,849,324,2,65,108,390,294,64,675,53,416,711,130,220,22,34,196,17,73,813,87,32,95,680,14,280,223,852,412,133,730,151,74,237,315,33,86,45,115,66,197,287,241,58,236,153,583,51,256,28,5,114,1,297,283,792,180,4,165,38,90,63,135,102,337,690,250,21,3,171,820,396,546,36,853,889,357,10,183,137,46,200,445,56,475,125,99,461,603,309,117,0 }, +{ 16,7,14,92,24,35,60,18,46,140,126,71,87,50,171,54,9,2,38,5,0,220,108,69,23,230,21,13,246,128,163,70,12,107,394,58,20,68,64,121,155,150,330,309,28,48,22,67,32,10,51,141,267,137,55,314,236,202,146,1,37,281,115,181,502,461,269,189,307,852,232,774,132,97,571,328,75,43,223,65,278,199,52,217,86,820,77,366,335,172,72,112,186,6,409,622,318,99,61,95,103,149,262,49,780,552,177,133,36,227,29,294,462,430,17,334,135,352,572,45,675,563,270,66,33,451,39,180 }, +{ 257,90,205,184,460,769,245,32,179,524,5,45,229,497,288,152,619,21,57,432,452,2,17,292,59,381,266,14,476,1,4,48,106,241,316,358,216,255,29,77,38,217,11,54,155,58,313,64,263,157,425,76,471,12,129,6,227,72,243,148,186,141,3,79,247,37,128,7,339,464,214,188,290,209,207,99,720,519,369,317,177,40,50,115,365,33,710,51,89,270,269,102,221,588,86,46,162,61,202,529,237,291,111,454,94,10,62,372,408,875,23,277,350,618,210,645,349,123,308,19,52,25,180,36,449,352,119,181 }, +{ 15,4,5,11,13,23,141,21,32,40,202,180,1,0,33,269,3,128,17,318,352,134,51,120,2,64,153,117,450,31,326,12,137,196,22,197,102,82,260,44,6,752,165,59,10,49,7,48,177,217,494,57,341,115,403,523,95,901,237,386,457,37,210,14,317,98,351,365,90,56,9,961,203,817,498,719,125,93,28,730,18,110,19,372,116,291,244,678,86,475,615,8,194,76,342,27,45,453,349,36,61,139,96,873,864,216,402,391,111,72,373,16,275,143,285,30,25,247,39,144,300,253,24,922,214,38,70,164 }, +{ 130,711,47,412,108,453,730,196,283,390,78,381,27,183,236,503,472,51,14,200,803,128,153,719,59,250,133,432,34,318,57,73,123,4,114,117,65,17,53,177,33,576,714,216,657,56,378,322,475,46,23,3,529,754,134,369,275,339,19,11,269,12,389,899,291,813,402,650,368,40,603,401,21,444,361,436,266,210,99,457,297,202,480,77,102,54,666,592,513,349,403,129,194,106,894,38,471,0,523,32,739,137,18,22,2,690,136,386,594,152,161,684,678,181,162,548,26,670,141,37,10,184,814,743,421,789,45,629 }, +{ 12,277,51,474,153,111,99,23,37,13,629,961,431,94,569,38,453,542,139,115,79,134,117,401,196,33,40,188,157,89,11,775,457,72,485,15,405,54,4,339,413,300,377,197,477,148,313,328,165,59,734,308,102,719,76,14,45,210,179,21,128,730,152,3,64,17,509,498,386,10,266,880,2,77,32,391,57,482,41,475,0,471,403,304,141,177,898,523,82,18,365,490,205,758,275,5,217,29,402,546,372,488,260,351,341,120,24,202,452,358,237,22,352,873,468,361,6,269,317,216,155,437,162,48,1,229,31,811 }, +{ 495,469,17,378,106,26,27,483,161,862,383,742,19,73,80,527,53,436,814,136,297,84,62,119,611,56,250,25,440,209,113,42,6,122,34,4,255,128,240,14,3,684,200,59,585,129,65,45,29,123,504,57,573,2,5,11,445,361,141,630,61,64,205,179,86,833,76,32,667,690,33,235,202,245,162,618,40,283,680,548,318,472,402,269,503,77,555,102,194,184,108,183,207,95,151,21,152,112,177,514,46,153,887,491,311,803,54,58,257,12,38,10,507,90,648,196,51,176,22,614,306,48,0,229,130,145,1,114 }, +{ 681,636,81,750,91,370,138,718,0,104,348,784,174,397,673,693,18,29,355,192,173,280,74,426,306,65,207,6,701,715,86,319,64,124,671,85,52,32,289,95,510,22,90,511,887,204,343,712,560,879,526,422,240,233,214,7,49,312,40,120,72,77,519,43,24,707,484,4,577,353,42,523,84,63,11,177,27,145,835,730,57,474,643,151,448,756,28,25,10,61,19,21,20,260,415,102,194,888,580,87,861,610,825,33,288,232,193,115,66,59,817,76,45,172,847,17,2,719,457,273,114,26,23,450,168,13,414,302 }, +{ 15,33,77,102,115,117,4,134,153,40,196,11,13,217,1,5,23,317,21,3,365,2,291,341,32,12,59,51,177,139,342,6,0,349,210,7,372,37,180,48,351,111,17,90,569,141,275,36,57,197,457,202,137,72,89,304,14,308,9,120,854,901,898,300,277,10,55,96,64,148,247,961,160,27,165,450,128,437,269,719,352,260,752,38,401,318,523,216,730,734,93,629,76,221,94,482,56,579,18,16,143,253,873,922,453,61,638,179,49,19,22,31,244,498,125,8,697,45,24,817,44,245,403,485,79,326,84,899 }, +{ 9,0,28,68,35,65,101,39,67,175,1,124,154,69,24,22,55,18,16,238,103,30,336,114,12,49,251,109,149,96,50,13,21,51,7,23,252,5,345,100,596,218,158,141,831,418,189,480,532,37,10,166,115,346,2,433,64,323,217,128,137,48,443,88,86,160,36,439,75,237,547,269,202,71,289,866,646,429,118,335,389,364,285,230,317,930,197,352,180,159,143,181,486,503,402,283,99,133,281,95,223,194,498,208,165,168,318,32,60,14,83,4,657,105,212,131,265,694,745,956,54,185,163,484,6,403,77,722 }, +{ 23,33,102,40,51,77,13,403,117,196,153,64,11,128,453,134,137,300,59,291,141,4,10,202,22,475,180,95,57,719,139,99,0,342,86,65,494,90,12,28,9,49,485,421,21,115,114,1,194,901,752,210,177,165,120,108,15,37,18,349,328,2,323,269,899,450,391,351,260,197,125,36,961,523,457,361,48,14,341,958,954,947,922,854,817,758,730,546,509,498,412,365,289,275,236,160,93,54,32,16,7,847,805,576,401,217,962,570,467,285,218,124,105,874,835,775,701,670,629,577,569,541,386,373,372,347,277,247 }, +{ 214,90,289,874,6,65,256,235,64,751,25,42,312,249,194,746,288,875,525,519,247,835,174,85,233,348,544,217,437,32,524,352,343,95,123,138,26,63,355,954,86,122,91,280,630,416,0,177,605,22,204,317,752,648,222,736,141,77,45,579,864,180,74,216,415,304,389,59,597,57,66,306,81,237,287,56,881,588,243,115,84,208,19,445,370,108,498,114,183,87,29,671,4,316,49,283,76,145,275,18,444,205,573,396,240,52,28,112,337,104,10,636,202,624,70,368,11,511,353,33,21,135,102,650,680,137,291,619 }, +{ 1,22,2,28,67,36,50,5,12,49,0,95,14,168,55,7,83,46,103,105,114,69,194,9,38,21,159,584,35,65,239,133,169,109,24,218,23,16,158,208,10,323,48,170,54,51,96,37,160,597,164,13,29,6,18,320,244,708,32,115,892,202,128,503,505,424,185,874,776,253,312,64,347,143,68,75,238,654,137,141,756,31,124,480,301,531,39,181,269,289,824,3,251,835,646,389,361,318,789,52,98,189,797,532,30,638,285,180,165,308,420,99,286,751,127,237,17,283,45,4,175,44,577,125,533,154,72,197 }, +{ 269,318,352,202,141,13,77,51,180,102,349,23,372,291,137,403,33,128,125,197,21,44,31,494,342,317,217,32,402,117,1,365,16,901,475,177,49,22,11,5,116,98,93,386,165,160,153,115,59,40,18,752,253,244,15,14,12,10,7,2,0,4,947,704,692,678,661,615,597,576,473,373,326,208,203,48,39,30,6,90,28,24,498,453,421,127,96,17,961,899,817,474,408,401,350,308,304,285,263,241,196,194,185,169,168,148,134,113,111,101,95,94,89,86,71,64,55,36,35,769,638,569,275,257,152,9,8,968 }, +{ 435,144,274,88,203,418,410,778,190,30,530,960,521,795,686,466,1,321,326,96,367,100,166,822,382,264,692,855,539,131,438,576,335,213,9,265,201,98,613,921,93,615,859,44,31,840,765,403,800,83,772,662,225,744,523,494,284,473,116,957,924,0,909,36,478,217,817,141,28,966,69,934,831,551,137,21,120,731,269,829,219,128,948,669,406,51,639,625,23,5,230,206,39,13,268,77,944,372,110,617,238,260,404,125,716,557,67,856,691,224,32,7,442,349,332,16,805,492,254,242,228,10,704,291,223,35,842,64 }, +{ 76,72,90,179,205,21,245,316,428,288,32,464,184,37,38,257,460,413,148,54,645,405,57,749,45,308,12,358,152,221,431,14,449,4,155,350,229,48,401,50,270,2,59,1,99,5,497,769,17,11,528,524,452,474,266,241,115,51,243,247,10,128,773,477,79,741,432,281,430,217,129,188,77,181,619,292,64,659,157,339,46,313,227,106,23,453,476,720,36,202,468,7,328,743,381,488,216,94,341,141,3,0,498,794,471,197,137,421,180,536,317,403,237,629,490,29,62,482,86,13,6,165,540,369,33,402,352,162 }, +{ 77,102,33,141,64,115,23,40,177,13,51,291,2,269,202,217,6,128,10,210,180,318,1,196,7,14,352,137,197,0,165,3,22,4,153,237,95,117,19,17,59,5,275,15,134,139,86,11,342,125,9,16,317,18,25,349,341,12,36,26,494,216,49,403,31,498,752,27,93,143,111,65,300,24,110,312,28,120,457,391,372,386,44,98,21,247,20,901,164,304,29,82,203,453,402,401,365,99,569,57,719,56,509,475,961,194,260,34,730,678,546,45,116,32,50,48,244,35,38,864,37,308,629,46,450,326,8,54 }, +{ 397,32,788,81,65,693,249,681,90,422,4,804,194,123,21,59,56,348,11,64,23,61,95,91,690,3,725,718,84,319,0,280,152,237,138,43,355,817,174,6,510,57,494,17,523,899,141,114,49,214,52,165,414,370,784,77,526,60,86,51,386,217,74,40,12,904,85,365,102,450,326,208,18,120,945,750,636,343,242,233,115,650,615,5,20,511,342,29,33,887,457,888,524,880,350,263,180,137,133,108,36,24,15,13,222,204,183,177,160,582,532,391,278,135,730,719,289,715,671,597,10,566,421,403,221,218,207,202 }, +{ 126,60,16,314,150,121,199,146,107,92,246,7,132,35,71,359,232,18,24,14,298,392,649,672,423,517,140,441,97,279,508,388,95,653,32,462,877,195,108,21,230,598,607,407,167,46,172,451,5,118,68,22,387,357,38,267,640,55,522,191,28,156,467,360,271,380,594,0,10,953,733,49,48,2,356,64,66,955,633,809,603,273,278,75,149,65,50,1,52,660,783,135,43,12,362,354,674,236,711,212,702,86,309,13,9,115,493,37,330,20,125,36,220,312,241,54,501,324,101,87,29,345,23,90,550,928,446,77 }, +{ 217,15,141,115,317,365,341,13,23,260,51,137,120,202,77,372,180,33,349,4,269,352,64,102,40,1,110,373,450,291,197,21,32,36,160,117,244,752,318,128,5,854,351,0,11,922,59,247,134,661,9,304,10,901,165,12,153,22,457,177,143,96,253,28,86,82,48,523,2,196,730,494,57,90,811,49,403,7,326,237,16,83,3,8,18,437,125,39,817,30,72,111,139,55,37,17,961,898,498,899,308,719,24,475,386,76,164,35,717,288,873,704,6,89,221,569,14,642,391,241,203,67,56,509,210,285,95,342 }, +{ 111,134,117,474,23,13,961,569,12,431,51,37,115,99,277,15,405,38,197,89,72,629,148,457,76,54,4,45,165,11,139,94,719,498,106,59,873,14,79,128,40,10,196,33,482,3,17,179,188,2,64,413,730,775,245,177,153,205,471,57,509,477,401,358,453,0,403,6,485,22,5,152,304,1,180,402,275,475,300,341,901,36,880,339,237,210,82,141,386,32,110,7,758,120,452,77,365,317,260,129,391,31,95,202,308,46,864,542,523,8,757,86,18,44,752,217,162,93,65,734,437,497,421,678,269,229,546,285 }, +{ 23,13,51,5,202,21,32,1,12,2,141,15,165,115,269,7,4,453,197,403,318,6,48,120,9,0,3,386,128,64,180,14,125,37,475,308,401,137,457,16,10,99,18,450,260,196,523,33,352,237,494,730,719,17,77,11,421,341,326,31,22,40,221,102,8,391,90,24,82,217,30,93,59,117,177,153,351,365,28,19,44,110,899,98,317,569,39,72,61,629,961,817,49,134,509,57,498,247,76,20,29,402,45,27,203,38,36,96,752,89,241,50,56,373,144,25,304,758,86,291,46,54,65,35,349,116,901,576 }, +{ 13,141,23,5,115,202,4,51,269,352,217,11,2,318,180,137,317,21,7,341,77,3,6,32,197,752,33,1,291,12,64,372,10,365,128,9,102,22,0,165,403,40,15,349,494,498,482,48,308,177,901,31,28,16,37,873,44,237,14,49,125,153,247,18,244,98,30,59,453,120,143,55,401,475,304,36,39,457,342,93,17,72,95,221,24,196,35,117,134,253,86,90,509,579,76,116,203,864,326,961,576,569,210,717,523,859,144,421,678,160,8,719,110,127,216,89,391,260,468,730,65,386,629,450,437,805,164,105 }, +{ 165,125,391,197,21,13,558,380,23,48,386,237,900,221,66,509,180,14,326,120,457,33,32,357,115,961,97,426,401,298,135,278,308,719,51,98,758,841,172,75,38,56,12,6,571,204,770,74,19,72,554,867,629,408,393,77,670,64,63,25,10,1,42,498,462,134,40,7,563,247,102,715,773,90,31,763,324,808,494,451,523,450,317,293,244,210,177,43,29,20,171,22,572,501,681,582,350,743,615,365,335,262,217,203,105,87,69,490,482,641,370,26,11,81,73,65,53,4,579,192,963,367,342,267,260,241,239,216 }, +{ 120,82,260,450,351,15,141,21,854,77,217,13,23,51,1,202,817,180,33,898,110,48,326,93,102,32,128,137,5,165,523,269,352,318,125,291,922,2,177,203,275,403,317,4,210,12,373,494,115,64,197,96,386,7,144,0,342,196,36,457,752,37,349,453,9,6,16,221,50,237,615,59,10,475,341,14,372,730,244,18,22,223,285,160,67,365,86,498,661,421,402,401,31,901,308,719,391,70,3,99,35,44,24,95,90,28,473,899,69,30,65,864,704,116,11,29,8,83,367,17,163,49,551,304,961,216,61,692 }, +{ 23,51,13,15,961,0,2,10,115,1,457,5,569,453,4,7,9,719,14,33,629,11,165,40,197,6,77,21,64,3,474,139,38,401,59,22,72,57,102,12,111,475,32,79,341,730,386,128,54,37,509,177,17,8,196,391,36,237,141,202,277,18,89,148,76,99,734,28,758,308,41,117,498,180,90,403,873,365,482,31,45,328,94,546,48,153,86,210,93,317,24,468,494,247,304,300,269,82,16,95,402,120,670,134,46,488,757,194,110,55,62,125,155,29,188,52,405,49,899,775,50,275,221,880,477,44,152,523 }, +{ 264,166,435,9,39,254,30,382,921,404,438,363,656,190,100,897,411,478,93,203,812,274,625,1,795,691,88,156,101,473,326,110,265,639,340,51,447,272,141,18,35,165,131,33,329,67,0,354,77,910,616,291,914,530,105,68,118,197,731,137,120,23,21,578,128,36,333,49,593,494,271,98,676,310,102,44,664,613,48,349,28,228,195,5,960,658,800,180,24,724,96,682,678,403,160,688,523,127,829,242,521,83,31,716,13,342,159,69,16,144,260,352,32,269,55,167,158,219,576,364,704,4,615,202,65,60,318,456 }, +{ 252,9,0,166,265,100,364,329,404,39,101,520,676,419,68,118,553,604,382,88,167,274,18,212,593,333,158,264,16,49,806,865,438,48,812,21,154,30,677,310,1,35,656,24,96,124,51,857,22,13,23,105,28,60,31,71,286,562,65,777,435,455,190,98,639,159,759,109,917,254,344,191,647,125,10,55,907,694,363,221,44,127,5,557,131,327,682,411,67,628,95,83,538,936,149,925,272,36,664,175,375,478,121,64,793,146,821,50,671,69,302,271,921,116,625,541,942,446,530,724,709,706,33,634,463,170,128,223 }, +{ 15,4,11,141,5,13,21,23,202,40,269,32,12,165,523,3,180,64,318,17,352,457,197,1,93,0,110,2,125,120,128,386,51,59,77,31,117,326,49,10,719,403,961,82,153,48,137,134,899,450,115,260,494,196,44,33,37,730,6,57,203,817,177,752,22,98,9,90,18,373,237,8,7,217,958,351,291,14,139,95,341,144,27,210,475,615,402,391,56,453,901,116,19,143,86,349,244,317,72,105,498,123,485,365,111,152,342,401,76,28,30,247,678,801,65,45,216,36,569,300,308,509,221,99,127,84,164,873 }, +{ 24,35,69,18,71,68,46,67,16,1,2,50,14,149,108,133,189,28,38,75,83,0,7,9,218,65,238,114,52,335,29,54,251,60,55,309,236,175,118,336,486,267,96,394,181,220,366,87,418,124,154,5,545,412,516,140,70,22,487,12,21,36,101,171,433,641,230,328,58,48,458,345,361,398,283,92,6,37,103,514,302,906,10,388,596,565,273,443,64,160,532,163,399,705,223,891,86,150,262,537,32,51,39,831,23,109,95,375,155,13,99,480,571,45,49,128,281,696,112,194,754,25,137,294,602,208,323,191 }, +{ 90,214,289,874,64,712,751,77,51,13,177,32,312,23,192,66,194,202,216,59,288,4,835,875,65,0,249,247,95,519,102,57,5,123,33,22,524,234,104,450,173,49,437,500,889,210,217,636,319,597,21,153,416,196,38,20,117,954,304,115,134,120,18,715,86,583,113,494,61,204,10,784,291,28,275,342,147,318,879,43,74,142,11,588,888,403,864,237,2,198,352,426,752,707,48,1,639,17,81,180,750,861,52,315,207,560,24,128,402,40,141,16,671,31,372,36,130,455,260,12,9,317,37,325,349,389,904,306 }, +{ 9,0,49,127,28,301,98,371,395,159,512,158,916,737,761,31,101,24,623,16,272,68,18,39,35,293,242,1,286,109,44,385,327,65,170,299,55,125,51,23,22,518,105,836,21,116,616,137,685,284,48,599,344,252,7,124,13,276,64,187,95,506,5,131,890,202,456,627,36,2,219,30,254,333,419,763,14,347,12,154,578,320,96,713,141,935,201,144,338,442,870,118,71,128,10,364,168,374,67,60,37,310,221,100,32,318,424,93,269,393,910,165,403,86,688,160,793,908,50,83,69,203,421,492,401,6,244,166 }, +{ 17,136,62,214,123,129,249,710,119,162,618,292,209,141,32,183,77,128,211,106,64,45,4,11,179,269,507,519,207,59,177,130,3,40,90,86,255,378,33,202,76,57,205,655,790,152,678,38,21,245,37,742,248,5,102,597,476,306,556,41,291,229,117,349,80,339,153,313,266,318,84,54,947,184,56,12,155,134,240,197,305,157,304,217,148,186,165,275,257,789,99,216,13,497,317,72,137,115,10,196,460,383,619,61,51,188,361,457,369,425,50,290,440,14,342,263,180,503,452,241,208,237,194,432,381,65,111,22 }, +{ 5,107,581,441,356,493,362,660,279,534,32,683,298,423,259,786,22,612,590,359,21,13,49,147,61,508,241,263,929,48,97,246,14,126,0,132,7,465,939,232,64,230,607,95,121,941,23,92,771,115,470,314,51,387,626,172,1,58,12,347,779,392,407,17,150,687,16,357,28,146,43,77,87,60,199,197,37,40,711,178,501,65,10,9,180,140,35,33,24,595,102,120,56,30,70,207,344,324,125,46,31,165,18,20,577,414,376,2,350,4,260,427,173,408,195,141,247,76,603,217,86,11,379,55,729,90,75,50 }, +{ 51,475,453,13,23,15,730,403,457,421,386,523,899,719,670,401,12,361,6,99,33,1,128,269,961,141,27,19,5,494,165,21,64,2,450,153,202,117,65,115,236,7,196,77,95,32,328,37,14,10,0,318,134,17,391,197,102,308,40,26,120,9,22,4,125,412,389,16,34,194,57,569,402,54,53,8,352,25,758,291,59,3,880,757,503,488,217,90,38,72,48,45,801,177,11,304,509,326,283,260,180,139,137,76,47,41,867,350,629,558,949,838,832,803,775,773,770,754,749,657,597,576,543,514,468,297,255,250 }, +{ 49,159,272,28,9,327,254,158,22,347,95,627,374,286,131,424,105,168,35,39,912,65,68,578,688,0,538,55,24,518,175,154,124,239,634,346,289,224,169,913,577,16,320,364,747,219,185,616,101,716,67,344,1,890,570,170,30,810,109,333,228,420,21,36,439,64,149,23,127,371,194,13,706,827,125,51,114,312,83,187,98,429,613,10,137,829,610,86,310,166,910,31,835,190,406,71,18,242,202,846,141,938,160,44,14,2,385,69,7,48,308,301,258,128,638,237,115,12,165,96,100,103,562,703,221,116,164,50 }, +{ 15,33,77,4,102,317,341,141,117,365,134,13,180,1,40,153,5,115,23,11,197,10,217,196,21,901,22,64,752,352,143,253,244,351,137,51,32,2,269,177,82,59,165,12,3,291,569,202,120,0,854,139,342,86,55,36,372,31,9,898,48,498,349,37,260,7,28,111,125,17,482,49,318,160,457,6,450,44,128,90,275,237,95,210,57,386,873,638,98,523,105,164,89,93,30,719,922,961,509,308,8,14,72,951,403,401,730,391,304,116,326,39,468,56,38,717,94,24,579,817,76,61,96,16,18,546,629,494 }, +{ 134,13,15,23,12,474,569,51,37,10,197,457,961,99,115,4,38,2,165,0,153,5,3,11,719,64,139,431,1,277,120,82,33,6,94,873,14,401,110,7,89,117,351,40,498,128,260,148,54,93,72,629,453,180,21,31,22,9,405,143,141,8,17,77,36,102,44,386,41,509,196,59,450,125,730,55,342,523,86,403,326,45,95,898,98,32,854,237,365,217,391,775,116,349,291,210,179,203,202,317,752,901,372,341,352,177,402,49,57,76,922,475,28,30,144,373,421,318,308,269,90,494,413,46,18,678,899,242 }, +{ 7,2,66,20,58,29,52,74,128,14,135,5,32,97,192,43,38,61,6,631,16,151,87,21,25,315,172,177,46,48,173,186,45,884,70,90,113,77,227,72,226,23,102,51,137,35,210,337,202,59,40,104,402,155,115,269,89,592,81,33,122,13,60,174,112,107,24,19,243,0,145,222,580,76,37,49,64,10,416,318,738,18,353,204,26,491,678,95,4,290,141,54,232,69,92,22,415,140,712,270,217,12,171,496,86,220,27,17,549,294,209,1,355,609,494,425,285,50,665,148,403,560,36,216,400,75,275,307 }, +{ 77,202,13,23,128,318,33,269,141,102,403,117,4,342,153,134,51,40,64,196,32,0,49,177,22,494,291,137,11,21,59,65,95,475,98,165,210,90,18,115,48,217,197,15,275,105,10,120,127,86,28,180,9,453,352,36,39,317,31,125,5,194,402,57,450,349,372,14,116,44,16,300,6,2,284,96,24,20,30,457,12,859,678,299,289,242,114,110,109,61,7,1,216,214,100,326,304,260,237,3,82,789,752,579,576,490,347,344,293,207,205,144,139,93,37,615,373,351,285,421,401,961,391,203,817,923,922,901 }, +{ 2,14,1,46,29,38,6,52,103,50,65,36,67,5,108,75,83,7,22,24,25,69,28,86,208,133,0,323,19,26,484,18,309,782,96,283,236,238,124,160,135,21,10,12,74,87,66,168,17,262,70,412,23,389,16,51,650,278,289,532,45,194,3,756,218,32,220,58,646,244,42,115,95,37,27,13,48,886,49,267,366,226,302,128,54,610,64,516,547,584,137,253,9,308,143,388,141,717,335,480,401,164,185,357,505,109,150,732,602,420,99,285,172,202,891,112,189,324,181,35,418,696,331,90,114,55,294,458 }, +{ 15,5,1,2,3,4,12,11,13,10,0,341,21,180,7,33,197,6,134,17,77,32,365,23,901,37,117,9,8,40,165,102,153,961,51,498,196,569,14,873,752,217,22,94,64,317,59,36,352,474,31,509,99,141,110,55,291,111,457,120,391,57,19,16,82,48,177,401,38,386,349,202,308,139,93,342,453,72,275,18,44,143,351,28,27,90,128,403,269,372,45,137,76,260,318,89,326,210,86,277,898,413,237,719,148,864,730,96,95,79,373,475,61,24,46,30,523,253,98,482,83,546,54,49,50,41,899,758 }, +{ 203,268,206,940,93,417,959,499,473,137,31,44,728,692,8,120,202,559,564,450,127,141,260,326,817,244,22,10,0,23,551,318,144,125,13,523,269,128,299,98,1,253,373,51,494,403,9,116,341,143,64,110,638,285,5,615,642,859,367,55,918,576,21,421,180,386,922,165,105,36,401,2,365,115,899,242,352,28,951,393,704,86,30,901,475,7,762,276,77,384,948,237,32,197,49,492,434,160,661,489,170,15,39,752,317,498,854,453,282,717,6,18,16,569,395,479,351,805,12,958,898,456,48,293,851,308,301,509 }, +{ 15,120,82,10,165,31,0,197,64,260,33,8,93,110,22,351,40,44,77,23,153,13,9,102,4,51,196,125,326,11,450,49,139,28,237,203,386,98,457,18,32,373,95,523,1,21,277,59,854,180,111,202,12,141,898,17,36,116,144,14,115,922,86,57,128,5,45,30,2,391,217,7,817,37,177,730,90,509,719,72,99,16,76,62,453,291,352,475,318,269,403,341,48,134,961,55,24,3,143,899,308,401,137,65,38,615,210,629,317,901,758,342,569,494,661,365,39,247,164,421,704,546,61,6,811,41,642,752 }, +{ 60,7,16,71,43,0,14,20,28,97,68,22,92,24,149,2,35,10,154,13,126,615,608,51,124,362,140,55,805,18,958,576,566,175,23,359,6,1,48,453,158,150,703,21,421,260,173,475,298,153,107,74,64,36,29,925,46,12,882,899,441,801,81,357,246,196,77,69,38,25,9,117,65,49,314,37,484,386,730,336,115,570,403,356,324,221,135,121,19,401,99,939,420,399,859,942,628,610,289,285,259,125,101,95,33,26,702,677,165,4,653,375,185,192,91,515,302,197,104,326,595,318,230,202,191,143,102,90 }, +{ 26,19,6,27,80,25,84,45,17,5,504,42,129,2,240,123,618,29,56,106,113,710,4,76,119,128,14,136,65,64,73,59,1,209,483,53,162,51,90,548,122,205,249,202,40,99,3,460,32,257,62,292,61,214,77,245,57,12,378,141,183,453,34,297,11,184,108,23,255,469,0,403,22,33,58,95,10,86,814,318,769,114,179,402,611,667,130,475,21,151,210,137,742,161,38,177,494,7,436,269,102,194,115,445,18,36,421,13,47,153,880,775,54,133,196,37,49,495,306,311,112,46,52,117,250,28,491,48 }, +{ 15,77,13,33,4,23,102,11,51,5,40,177,0,117,10,202,64,8,93,22,291,32,21,44,31,153,110,1,141,49,115,59,82,180,9,17,318,196,98,269,351,403,137,453,36,95,457,217,3,90,139,128,48,28,898,12,165,450,475,349,341,197,523,18,30,210,317,86,16,260,401,2,961,901,719,57,116,494,854,326,373,125,365,817,352,730,275,134,39,386,105,76,24,7,308,372,37,342,6,421,120,704,144,14,899,922,203,111,304,569,638,72,661,61,629,96,127,99,811,535,498,65,615,752,247,83,402,758 }, +{ 195,271,98,223,167,132,407,146,360,591,1,212,834,121,522,393,293,199,259,354,446,668,590,562,807,191,156,48,765,125,107,147,941,163,727,541,535,465,126,284,21,346,31,279,23,55,18,5,687,16,60,640,13,697,586,178,534,35,20,608,581,150,955,427,68,70,589,43,142,791,463,198,116,51,883,362,9,963,781,202,49,0,818,96,493,7,948,281,120,165,367,798,450,10,44,318,77,105,14,325,221,92,28,22,61,770,101,777,58,93,32,97,467,141,386,33,144,371,75,127,37,108,929,269,411,598,551,128 }, +{ 210,128,202,77,402,318,102,403,33,342,275,40,196,475,6,300,139,29,177,99,494,269,42,503,111,757,277,141,117,52,2,4,678,13,153,12,0,23,59,285,37,137,85,81,7,291,859,657,351,122,82,576,44,31,880,260,216,120,91,78,63,14,11,1,745,65,64,93,854,27,684,22,485,180,76,74,19,3,21,805,49,38,281,48,966,838,631,452,450,421,218,134,26,25,569,341,115,898,738,592,110,98,10,315,788,707,491,483,432,396,319,268,242,125,116,95,62,57,56,50,17,16,9,655,526,453,426,422 }, +{ 40,33,102,23,51,13,117,196,153,77,134,64,128,202,4,59,141,453,65,475,11,403,22,95,0,137,14,139,15,342,210,12,86,318,269,165,115,1,49,10,494,177,28,180,36,291,421,21,57,5,6,217,18,194,99,401,2,457,108,719,125,19,111,260,82,37,27,48,17,3,523,197,133,26,402,120,114,670,352,7,24,730,93,216,90,46,237,50,32,386,361,9,341,351,25,349,300,283,277,38,34,901,365,326,275,98,110,285,45,41,391,44,317,576,961,242,143,16,509,170,31,854,308,54,116,817,757,629 }, +{ 7,14,16,24,18,28,71,2,92,60,35,22,20,0,154,118,168,6,1,97,69,703,302,149,140,150,185,38,109,43,66,68,108,420,29,55,10,158,610,12,65,67,5,701,189,273,570,95,230,429,46,124,246,289,298,51,21,169,143,49,175,23,107,747,126,671,13,64,48,159,25,925,677,137,36,52,398,32,9,251,584,467,164,128,942,19,86,577,847,83,173,336,403,74,50,424,202,232,882,172,17,344,279,638,375,105,314,104,37,125,115,441,356,505,99,253,239,26,221,359,329,194,533,837,517,165,494,601 }, +{ 23,1,15,51,2,13,5,0,7,180,21,9,6,115,197,120,12,165,4,33,457,386,523,202,141,3,14,260,450,48,8,16,77,93,31,341,37,269,40,403,44,730,110,326,32,102,317,137,318,453,352,719,365,18,128,217,153,82,475,36,98,64,391,752,401,901,899,421,308,196,11,498,22,28,351,99,116,10,117,24,817,96,221,30,39,494,509,569,177,203,49,961,144,291,373,615,59,17,801,372,482,72,758,90,89,854,50,629,134,127,35,86,349,922,160,55,244,105,237,143,468,898,210,54,46,670,342,45 }, +{ 132,146,199,107,60,121,126,279,150,441,92,649,16,955,493,195,598,423,653,298,35,407,356,640,362,508,32,21,953,7,360,359,522,18,271,783,5,246,167,0,660,156,534,939,71,191,22,517,590,24,49,259,314,10,607,28,392,13,232,147,68,97,95,14,43,883,118,1,37,581,48,501,354,809,55,108,4,591,12,20,23,347,345,212,467,230,446,241,125,50,941,75,9,31,98,683,929,77,2,672,33,713,463,702,273,61,40,59,70,46,101,58,172,586,357,296,115,779,36,350,109,727,388,236,207,87,105,408 }, +{ 31,44,276,284,98,299,935,116,201,662,9,39,492,144,489,131,567,371,268,0,49,918,127,137,367,254,28,434,293,737,30,203,1,295,22,456,93,125,219,385,23,202,187,393,21,716,105,599,578,51,36,128,141,836,272,269,244,318,384,13,159,395,48,261,96,110,224,616,326,242,35,16,253,5,95,55,64,160,258,158,143,18,100,699,165,24,115,948,120,37,403,86,166,310,12,924,457,855,940,77,10,442,494,101,109,7,197,317,206,68,2,373,617,761,352,285,908,217,332,341,916,228,32,33,518,688,730,473 }, +{ 13,15,23,51,1,0,10,2,4,115,77,5,9,202,3,197,457,7,22,11,165,6,961,12,33,14,59,177,403,21,102,40,453,180,719,217,18,31,120,141,32,475,8,318,569,37,16,341,210,17,386,450,401,28,269,64,99,326,494,317,730,308,44,523,93,352,291,90,125,365,260,36,275,24,134,49,139,30,110,498,421,98,57,196,391,19,117,72,342,153,48,817,873,349,482,901,221,629,76,237,111,899,86,95,216,509,752,304,137,247,82,372,116,758,89,27,38,39,65,203,55,402,45,29,373,300,143,615 }, +{ 162,123,257,184,183,17,229,130,84,136,497,556,152,383,129,452,99,80,3,205,62,57,56,266,106,119,12,128,40,4,161,378,27,14,495,504,381,202,769,469,249,59,77,432,11,34,33,305,64,90,403,240,38,54,5,102,45,32,23,73,618,475,51,361,440,139,775,37,402,471,318,53,21,494,65,141,19,372,453,313,421,177,880,472,757,217,245,548,250,527,2,76,524,349,61,86,216,153,210,196,115,179,10,619,328,269,291,214,72,483,117,209,6,26,22,477,111,95,13,401,114,157,369,211,36,134,48,306 }, +{ 196,153,117,453,134,33,40,23,102,51,13,475,0,14,22,15,12,2,64,670,21,401,730,4,18,99,65,10,9,11,1,28,95,57,128,77,5,38,46,37,403,386,202,719,24,6,108,457,899,139,31,59,16,82,36,165,32,120,961,86,236,48,8,342,141,775,125,308,137,7,54,523,50,269,197,494,318,115,300,260,39,758,450,180,421,277,90,217,341,629,361,49,3,326,114,93,391,291,210,194,328,317,111,17,352,558,110,377,29,275,283,133,485,817,898,615,801,351,160,19,488,569,350,373,109,84,56,183 }, +{ 224,219,187,258,442,871,908,385,836,131,944,574,839,127,98,613,31,116,926,44,137,829,860,160,827,276,39,254,284,36,144,228,201,96,406,1,371,530,202,856,716,93,30,51,9,203,268,737,902,492,23,49,21,190,13,318,843,531,326,269,924,125,367,141,64,77,120,166,816,935,800,242,299,115,456,260,293,274,931,494,395,567,110,217,662,5,317,22,717,100,143,434,128,28,48,86,533,10,295,105,332,55,489,180,365,918,418,102,578,197,244,83,341,576,403,384,625,165,261,177,159,352,291,822,863,473,251,206 }, +{ 195,121,360,590,146,407,156,786,522,132,883,591,626,929,941,259,687,279,379,586,296,465,467,150,178,354,5,21,55,534,70,60,783,771,13,346,340,48,271,581,49,493,163,1,199,147,608,668,125,955,167,562,535,98,518,108,362,18,371,212,446,107,761,878,31,126,50,463,35,818,105,75,120,598,87,0,767,223,165,640,22,447,293,777,159,16,23,9,142,115,658,92,481,44,95,37,807,93,61,58,24,191,33,7,846,395,12,197,4,385,127,423,71,140,82,427,144,116,17,117,2,363,698,246,39,64,32,781 }, +{ 2,1,29,14,6,46,52,26,75,38,5,25,19,50,96,70,108,114,309,36,103,74,17,65,27,112,45,262,236,220,145,218,58,42,86,516,24,366,22,7,67,21,12,66,532,87,412,18,480,283,602,323,137,3,163,226,238,0,194,51,32,28,10,69,171,37,23,115,208,128,331,394,135,48,99,64,13,433,285,160,61,782,113,357,308,222,95,484,401,16,141,294,151,287,223,83,646,124,389,361,894,866,545,421,54,324,4,278,388,396,186,650,594,53,168,453,289,202,59,253,181,90,273,138,150,403,756,606 }, +{ 15,82,120,10,197,260,0,165,351,8,64,31,110,93,9,22,117,13,457,450,1,44,33,23,326,102,49,125,898,153,719,77,854,523,18,14,98,12,40,7,961,2,115,373,180,5,51,386,36,111,141,95,730,28,196,922,139,203,55,116,4,817,498,899,128,30,177,86,569,144,661,99,37,202,16,24,65,318,21,873,11,59,6,277,217,391,269,341,38,32,291,143,237,352,210,317,3,365,509,474,349,758,704,642,216,39,615,137,453,275,403,401,94,300,304,629,247,372,242,342,90,48,752,901,801,308,221,811 }, +{ 7,66,97,172,232,192,226,2,74,324,52,29,135,92,568,222,43,107,353,87,387,580,500,38,620,448,331,526,140,330,5,138,294,174,356,20,14,262,298,32,357,145,173,853,6,359,21,126,58,16,204,808,64,888,337,104,314,673,35,22,729,95,872,25,278,380,70,802,37,583,426,1,49,12,112,69,75,65,86,61,45,60,48,10,246,90,230,0,42,355,24,220,415,85,81,889,46,885,36,825,287,28,194,72,171,792,392,632,560,280,312,50,71,115,76,54,26,18,151,155,707,587,89,17,4,764,147,59 }, +{ 62,129,123,162,183,507,618,136,249,152,211,57,77,184,205,59,130,4,128,41,11,40,117,17,229,497,361,257,3,33,38,313,452,266,378,248,153,64,106,90,210,383,119,12,328,214,99,432,216,157,196,86,202,556,179,54,102,80,37,381,402,471,134,245,177,5,514,369,188,339,440,305,275,524,141,45,269,84,710,529,291,403,139,342,115,56,318,209,76,504,421,372,742,619,292,240,237,79,27,14,775,22,19,10,23,457,95,21,207,488,13,494,180,542,65,300,401,306,32,34,114,26,215,25,217,769,151,110 }, +{ 13,1,4,5,2,23,115,15,3,51,6,0,7,77,341,217,11,141,177,33,317,12,180,21,365,197,202,32,318,352,269,102,901,9,291,10,165,40,403,752,59,22,8,31,401,569,349,494,120,308,304,44,110,90,17,386,64,93,37,247,153,210,326,14,450,28,373,117,16,372,48,196,457,260,98,275,475,498,237,453,125,30,18,203,523,342,49,509,391,36,961,128,873,134,719,116,629,24,482,421,39,221,216,139,244,72,730,253,95,817,55,86,143,615,57,661,351,704,144,127,899,105,76,864,576,82,922,758 }, +{ 6,26,235,145,19,47,112,78,648,624,630,27,85,64,42,95,122,444,25,644,113,70,108,453,29,632,130,711,53,222,74,390,32,65,237,412,416,280,665,287,813,138,730,307,73,680,283,14,196,52,34,4,312,51,236,226,90,262,17,337,22,180,59,331,500,128,86,884,315,194,690,114,133,498,849,137,50,200,3,256,21,2,49,45,33,23,13,66,141,409,445,297,288,57,381,719,11,396,289,247,87,163,165,1,250,5,46,115,153,525,99,183,76,746,324,63,792,675,294,316,56,214,28,583,751,125,285,503 }, +{ 51,23,99,12,15,13,153,10,117,403,120,37,82,165,128,453,401,64,202,197,2,115,110,14,260,457,21,475,5,308,0,38,93,719,31,3,22,134,141,1,33,196,386,180,351,17,421,569,44,326,125,523,961,11,730,269,137,402,318,450,4,494,8,49,36,9,98,18,7,95,391,6,54,629,328,32,237,28,40,19,86,775,373,57,116,41,854,77,79,59,899,352,94,474,102,203,509,477,65,758,89,341,285,221,901,217,111,177,16,361,144,30,880,757,498,46,29,27,670,105,898,143,24,62,72,277,317,55 }, +{ 16,24,71,18,92,108,35,60,146,64,7,118,246,199,126,121,32,50,9,267,649,156,167,830,271,133,273,140,36,68,5,230,132,247,21,14,1,640,12,563,48,0,87,388,75,2,237,220,330,86,46,354,115,65,672,304,128,55,751,522,180,360,95,28,236,191,516,114,163,696,149,953,189,22,54,565,141,51,137,217,674,412,352,49,340,23,633,37,309,458,202,39,10,394,58,103,70,597,125,262,72,38,947,726,591,502,437,195,164,363,67,641,514,602,954,124,864,448,107,150,143,101,366,283,13,678,317,177 }, +{ 15,2,0,13,1,4,3,5,23,7,217,6,9,115,51,8,341,10,11,317,372,180,365,77,33,12,22,247,901,18,120,197,457,14,16,165,291,28,21,202,31,349,961,719,102,260,32,453,30,352,523,40,752,569,730,24,82,450,304,308,110,437,93,117,44,177,36,351,196,403,64,17,318,475,153,899,134,269,494,922,401,386,128,48,482,316,326,37,137,49,57,873,125,39,854,59,55,509,90,579,342,35,817,629,758,373,210,98,498,237,898,116,65,111,144,139,615,19,670,241,20,56,638,468,89,95,275,801 }, +{ 234,639,178,416,77,455,5,49,142,202,450,427,0,147,329,21,198,315,61,557,344,13,113,32,318,120,22,23,325,48,259,534,494,105,95,347,518,798,39,884,159,58,481,99,817,623,878,291,112,711,70,64,17,1,31,320,51,265,158,65,465,128,362,192,137,236,845,260,403,44,310,141,196,342,177,30,7,271,241,195,135,269,28,290,210,929,15,4,307,170,125,663,102,33,275,263,132,859,408,294,194,786,581,402,301,153,86,523,98,150,423,346,167,25,237,415,36,858,682,592,352,117,100,12,37,10,709,146 }, +{ 31,1,44,36,116,64,55,5,180,2,22,352,98,86,127,141,12,164,13,498,125,115,144,253,170,4,242,137,752,197,128,105,7,244,0,160,237,23,341,21,165,9,365,299,901,301,3,951,317,10,6,28,864,51,338,269,15,268,844,83,169,217,202,873,638,93,569,509,96,824,546,8,37,456,740,293,512,203,77,395,479,318,206,14,776,599,95,506,33,367,276,11,48,685,954,285,239,457,535,201,492,762,717,401,965,533,102,393,482,208,49,109,391,601,558,489,67,870,284,947,961,403,708,946,35,434,923,40 }, +{ 120,217,260,141,23,77,13,51,317,1,180,269,15,115,202,450,137,110,373,291,21,352,64,349,5,177,82,326,351,48,318,854,341,36,365,304,165,197,0,32,922,372,128,96,9,93,752,12,22,523,160,10,2,244,7,457,4,28,898,386,86,901,661,498,719,33,16,275,90,247,203,143,11,253,482,40,37,83,403,730,494,102,50,18,961,615,473,221,237,8,817,30,968,125,704,216,39,59,14,67,6,117,210,35,49,95,72,342,308,24,89,453,55,134,475,76,69,116,17,61,31,3,437,864,70,899,576,98 }, +{ 26,396,648,6,112,122,624,145,287,19,222,416,42,25,573,138,74,651,644,151,66,113,445,45,85,665,280,29,525,204,64,174,337,680,87,884,135,53,65,70,436,27,95,58,297,881,194,73,80,746,544,235,17,86,2,90,243,32,22,5,128,1,609,353,311,141,61,315,307,491,226,106,0,52,81,813,614,209,255,7,632,75,114,10,36,186,580,415,289,63,611,14,34,33,49,214,28,792,117,250,312,283,77,125,172,108,389,76,192,208,355,237,667,56,4,91,165,294,40,348,148,635,102,123,256,153,46,549 }, +{ 156,360,522,271,146,354,21,446,121,132,5,463,591,340,195,727,586,18,48,407,13,49,167,32,668,60,199,566,23,16,447,698,955,575,9,658,296,493,608,150,126,0,534,35,24,581,92,71,590,363,107,279,141,125,640,31,212,4,929,411,467,562,10,1,51,115,783,165,191,22,623,68,598,379,61,12,293,273,202,95,197,303,163,663,269,108,37,101,221,118,621,391,76,28,17,40,109,7,767,260,55,518,120,33,11,77,259,39,347,941,44,252,713,478,98,140,408,350,93,626,346,264,900,509,818,30,170,763 }, +{ 21,5,61,49,98,23,95,32,612,178,259,51,182,48,13,481,120,77,683,660,427,291,147,64,70,4,465,33,344,269,241,0,17,767,318,263,202,141,107,415,817,450,180,22,534,392,362,125,116,347,11,929,350,121,595,408,423,352,293,260,196,165,117,105,102,87,86,59,37,18,7,3,557,376,845,663,579,541,490,356,316,115,90,163,928,711,28,132,590,407,941,922,704,645,523,360,346,284,268,232,210,203,195,177,144,134,118,97,65,60,40,16,1,298,153,146,58,937,798,414,113,31,771,246,207,142,281,647 }, +{ 468,89,358,79,365,94,179,205,317,405,482,498,245,72,115,341,217,180,413,490,111,734,247,431,188,216,37,474,152,452,11,629,569,873,752,749,497,197,4,339,308,13,12,59,901,428,864,579,40,76,23,372,291,875,10,352,134,961,117,77,542,528,401,51,588,349,471,529,33,464,546,477,645,139,221,1,377,38,21,99,36,177,509,64,449,316,14,2,304,143,102,437,9,32,196,741,3,165,5,275,202,128,54,277,237,153,210,743,457,430,300,773,485,460,141,17,0,350,22,288,421,257,270,48,8,184,137,15 }, +{ 24,35,71,68,16,18,92,118,375,108,7,101,302,175,336,154,60,273,14,124,9,398,230,140,0,150,55,149,28,345,189,399,246,191,65,346,69,722,212,46,1,458,167,22,429,314,2,267,126,674,21,388,439,97,67,38,114,10,48,252,628,95,364,251,882,50,133,637,36,49,39,109,12,823,172,768,602,103,236,650,5,83,520,66,232,64,517,32,562,847,694,815,589,702,107,96,51,594,23,695,363,298,37,535,462,484,29,13,796,86,467,886,604,806,868,121,419,125,54,755,451,168,100,135,570,158,6,169 }, +{ 421,453,51,801,730,475,23,523,386,670,401,719,13,629,899,365,403,758,457,115,494,569,805,958,576,543,697,165,615,221,423,817,450,33,488,308,197,102,66,21,514,388,56,14,38,734,341,117,7,326,69,467,961,880,323,196,153,19,202,886,770,128,273,97,120,811,642,532,458,207,110,24,10,6,1,540,67,0,753,554,390,135,134,92,73,52,43,894,37,32,743,214,114,103,90,65,29,891,847,782,125,451,269,208,75,754,602,480,244,229,200,133,130,119,16,9,426,162,83,40,22,2,42,25,203,123,4,901 }, +{ 113,311,614,29,491,145,45,112,80,6,151,631,209,315,74,61,27,2,73,5,287,66,26,243,25,70,396,611,667,52,138,19,58,454,869,122,222,17,186,106,87,255,445,32,64,496,42,297,1,135,684,227,4,128,644,7,86,624,76,337,95,416,65,22,72,85,59,14,204,148,90,21,738,174,250,53,119,141,280,10,483,0,833,137,651,690,549,46,75,226,11,378,37,665,155,742,200,56,194,207,57,177,179,525,12,114,84,77,48,495,40,469,172,292,307,161,49,38,884,887,655,573,881,89,425,18,36,33 }, +{ 471,339,188,4,11,59,377,79,12,99,94,102,77,33,51,111,542,37,342,152,961,13,474,403,40,202,196,153,128,3,452,453,177,229,431,475,401,23,139,117,184,266,157,57,134,115,494,210,300,17,291,402,129,90,32,529,629,5,141,76,775,72,64,275,38,21,318,757,569,277,216,162,14,421,313,123,269,880,89,1,2,497,257,62,197,405,27,45,285,54,304,148,485,217,15,457,41,10,6,179,165,205,180,137,477,349,130,488,245,19,523,432,437,260,22,120,95,769,372,0,352,381,249,618,482,308,237,7 }, +{ 15,40,4,11,33,120,10,82,165,77,110,93,31,8,260,0,23,22,102,51,13,59,44,351,125,64,197,139,32,9,21,326,28,450,196,180,98,203,57,141,386,49,373,12,111,95,1,391,5,457,99,403,115,18,453,116,202,134,475,523,719,854,37,117,128,401,237,48,144,177,14,898,36,217,2,90,308,269,342,758,30,210,318,277,730,494,17,509,922,352,291,817,86,3,65,16,901,24,194,402,105,38,137,7,341,317,29,961,39,629,55,72,76,300,661,752,421,899,242,54,775,247,349,365,569,143,615,485 }, +{ 16,71,24,35,68,18,118,60,0,28,124,92,7,55,9,419,375,14,109,175,149,101,126,252,302,346,150,439,108,154,158,570,169,185,364,22,273,49,65,917,701,329,168,962,420,191,925,39,628,107,2,1,604,46,140,67,97,21,535,265,38,69,159,942,677,10,722,336,105,345,694,467,703,48,5,95,246,553,810,882,20,83,333,43,12,706,13,637,826,100,23,847,671,36,167,279,121,793,189,199,374,50,125,51,212,230,286,66,272,566,6,484,634,344,32,429,289,327,86,103,96,601,114,441,517,37,64,298 }, +{ 17,45,227,106,209,243,21,290,255,400,207,48,263,270,62,186,119,2,496,454,50,32,3,5,52,221,29,425,136,408,430,46,513,171,14,659,54,84,787,721,741,56,281,38,536,80,181,378,240,742,587,552,1,306,76,123,241,541,655,328,428,61,129,41,666,72,502,57,89,12,760,540,115,155,148,383,449,794,37,96,361,152,11,34,214,4,311,51,440,474,592,431,64,790,202,179,405,350,292,36,90,67,663,6,141,59,13,498,103,583,130,697,514,40,211,99,58,10,128,237,217,770,27,869,157,828,834,391 }, +{ 340,354,586,658,668,156,195,698,363,447,883,296,1,411,303,18,379,9,919,360,51,621,98,31,48,591,371,13,21,446,163,5,121,70,132,125,23,759,846,127,761,478,60,293,223,271,146,385,120,664,691,108,763,82,634,638,75,4,260,24,807,774,144,44,522,407,30,457,32,16,914,463,126,777,335,93,771,367,71,49,39,12,0,687,351,118,299,201,40,7,50,848,518,284,922,854,758,538,364,279,11,10,724,310,217,326,64,393,77,541,767,167,150,137,105,103,101,87,58,17,115,286,225,33,401,867,850,663 }, +{ 277,153,111,12,23,51,474,99,38,139,37,457,453,117,542,41,79,629,196,401,13,11,14,569,961,102,33,134,188,89,40,157,54,328,72,775,57,115,17,94,413,339,431,15,377,369,3,477,308,719,248,165,313,4,468,45,485,148,32,197,76,734,216,62,2,21,10,587,405,64,5,59,29,34,6,210,82,509,471,128,155,152,730,177,490,7,266,361,52,120,475,1,0,873,19,305,758,9,899,125,22,110,386,300,211,93,529,229,48,260,237,180,65,50,528,880,77,391,86,36,27,588,326,546,482,402,514,56 }, +{ 18,16,24,0,159,35,68,49,105,9,60,71,101,28,344,55,7,118,320,109,424,14,327,455,239,22,286,170,272,310,1,374,252,154,419,329,167,158,124,518,23,95,709,39,2,21,51,65,13,301,5,890,48,92,265,98,191,212,12,10,108,137,347,128,30,538,31,364,125,202,37,553,20,149,121,557,185,36,677,67,333,169,338,146,127,346,46,175,64,6,126,100,577,50,254,32,512,916,33,403,86,395,69,242,806,371,115,96,44,77,318,221,479,694,616,102,165,623,83,910,627,737,917,38,793,578,846,375 }, +{ 0,105,9,16,49,18,24,68,101,518,158,320,28,35,301,170,272,890,344,286,109,327,39,65,55,512,7,1,127,916,98,118,252,60,737,159,239,371,21,71,124,395,154,14,48,455,623,242,31,23,51,419,374,364,761,5,2,12,137,310,95,125,293,333,846,202,13,616,329,347,338,385,30,37,131,44,938,479,128,836,709,22,538,265,506,64,793,627,10,67,167,299,403,827,212,713,578,149,116,32,36,219,100,910,254,553,175,318,677,108,185,50,6,96,46,685,221,421,424,191,577,92,93,269,599,187,165,284 }, +{ 141,180,1,5,352,365,13,2,4,217,341,115,269,317,15,23,752,3,7,901,6,0,197,21,51,31,77,202,44,318,32,64,372,873,498,10,93,8,11,244,33,9,165,12,102,137,22,253,49,48,237,116,30,98,125,39,177,569,401,28,143,291,308,864,509,40,120,349,403,37,105,391,144,153,482,203,450,110,421,127,72,576,494,95,453,260,247,96,475,373,326,117,36,717,304,131,128,134,90,89,14,457,854,221,206,196,55,811,82,947,629,351,922,386,59,961,734,670,17,437,661,224,201,16,88,76,164,187 }, +{ 51,4,23,102,33,300,40,485,453,880,153,19,17,196,27,342,78,26,53,403,47,77,210,0,475,117,73,2,57,34,11,5,200,13,99,494,14,1,22,12,176,421,64,111,41,21,368,361,59,757,231,130,123,6,139,128,134,401,106,18,56,49,322,317,177,45,775,48,115,165,108,10,125,65,504,495,197,32,605,229,80,341,221,183,390,813,46,37,618,306,95,719,554,152,753,736,543,444,161,402,277,119,630,236,216,122,113,29,730,237,25,899,217,714,383,297,472,445,378,250,38,678,670,527,503,469,412,318 }, +{ 62,440,56,184,229,183,556,130,3,136,548,152,266,99,162,161,17,57,84,452,305,12,383,257,123,403,34,40,527,14,205,349,504,555,497,328,4,361,202,139,775,33,38,37,128,475,129,880,313,381,757,102,11,196,59,119,401,432,402,54,471,453,215,77,23,514,507,585,51,211,153,80,494,495,469,236,477,117,111,64,421,240,369,106,21,214,277,157,308,5,291,141,378,249,13,2,803,619,318,45,76,177,27,485,472,134,41,32,412,339,216,65,524,197,10,72,114,176,165,53,90,372,22,188,245,86,217,543 }, +{ 13,23,115,51,141,202,77,64,269,217,33,318,102,32,21,128,137,0,177,180,4,5,291,9,48,352,10,197,90,196,317,40,153,7,372,18,15,349,22,2,247,165,275,6,403,11,117,125,237,59,3,16,752,494,28,342,304,31,341,134,14,98,86,210,36,241,12,244,49,95,24,498,30,116,39,1,143,901,453,20,450,144,365,402,221,17,475,93,203,127,65,253,216,72,326,120,164,576,35,19,96,44,437,160,421,308,83,67,864,954,569,817,719,25,101,100,61,55,57,284,805,523,401,60,293,285,105,88 }, +{ 1,22,36,105,170,28,55,239,86,31,2,95,5,9,127,0,98,301,12,64,67,7,49,320,109,654,44,83,10,169,208,143,21,312,35,13,23,128,141,740,125,242,14,512,293,116,160,237,51,395,50,338,37,159,96,6,933,114,115,708,299,751,185,137,844,505,269,168,69,48,638,103,597,928,202,180,244,323,16,352,253,165,3,713,197,194,892,39,24,599,65,54,341,285,318,286,46,685,456,951,4,623,824,32,133,158,365,584,601,535,38,498,144,218,251,841,533,506,30,371,217,479,8,503,284,870,752,276 }, +{ 15,0,1,2,5,13,23,7,901,9,6,8,12,51,4,3,457,180,341,898,82,22,115,10,141,120,117,31,351,14,202,260,197,28,269,33,153,961,64,30,44,37,93,18,569,854,11,21,16,77,217,137,365,386,719,237,134,391,110,128,523,49,450,36,629,498,352,102,125,326,317,40,401,509,730,318,196,24,203,546,308,39,116,922,55,752,32,86,817,17,403,90,98,35,453,95,373,59,99,873,177,758,111,143,38,291,242,139,65,48,244,899,372,285,642,349,247,144,734,638,615,482,277,72,253,421,89,475 }, +{ 274,98,100,39,1,265,438,190,310,625,382,30,530,223,909,96,264,593,166,778,88,812,410,960,731,557,252,31,125,21,539,23,254,9,48,51,116,639,44,478,393,131,127,13,101,0,293,284,329,795,404,840,144,656,435,333,724,616,5,219,228,321,272,421,834,165,921,957,224,105,32,36,93,128,697,367,541,669,765,242,221,49,676,137,418,455,160,748,83,897,163,202,682,578,475,453,553,386,807,217,213,551,613,406,335,403,141,466,197,827,180,948,37,158,187,115,905,821,791,203,352,286,258,371,120,863,730,2 }, +{ 212,18,118,447,167,781,363,411,271,647,146,121,60,562,664,621,463,815,478,1,16,446,9,777,354,628,21,936,48,101,932,132,68,191,98,520,522,223,71,264,5,24,759,55,199,575,727,566,126,13,23,724,156,360,691,125,0,407,591,608,49,293,604,221,51,35,92,195,346,31,107,252,108,375,586,96,154,914,682,541,807,150,39,589,124,848,265,69,22,105,50,834,32,364,302,393,329,12,4,10,382,535,404,28,166,765,100,668,273,33,149,88,706,806,163,279,95,410,598,44,335,955,7,30,67,284,159,467 }, +{ 24,28,14,7,35,16,22,65,0,1,2,158,49,109,55,159,124,95,18,67,149,105,71,239,169,68,108,185,424,289,9,12,69,46,577,10,175,154,320,5,164,347,6,747,344,21,835,36,286,50,38,83,170,654,892,23,420,92,114,51,64,143,60,13,374,189,302,429,140,48,103,570,118,32,37,29,336,610,133,327,312,398,39,484,128,638,168,301,272,125,202,137,86,150,30,194,230,671,345,584,703,96,54,115,273,251,310,283,141,518,98,308,20,538,401,101,127,52,31,221,346,455,535,236,403,389,458,267 }, +{ 105,272,131,22,286,239,320,327,98,127,219,9,185,224,374,187,55,28,578,86,31,258,36,913,616,535,1,95,538,64,164,837,634,109,116,601,44,654,312,170,910,49,708,623,115,23,5,0,713,371,201,159,13,505,2,197,83,892,7,35,67,180,533,928,51,933,317,737,301,751,10,716,352,284,6,160,776,237,509,916,165,242,498,251,531,12,395,39,924,137,638,385,293,269,30,276,168,141,217,254,752,208,144,569,896,836,143,346,24,824,21,4,546,468,169,308,14,761,128,345,244,318,482,365,3,597,391,384 }, +{ 28,301,22,31,127,512,105,98,395,109,44,299,293,95,9,320,1,599,685,900,55,0,239,36,763,338,242,49,116,125,841,168,870,456,286,23,276,506,86,35,284,51,185,65,492,159,2,24,374,479,13,393,928,5,144,7,64,169,713,141,12,137,327,21,202,14,268,237,16,935,10,201,558,272,538,67,401,367,37,143,83,308,424,164,312,128,158,115,48,165,918,654,194,269,923,740,39,347,244,623,18,6,489,253,318,114,963,160,352,289,124,180,68,96,420,170,203,175,131,421,219,434,197,391,221,762,453,93 }, +{ 2,58,29,52,1,186,334,46,5,14,151,75,45,400,155,50,61,227,163,262,502,69,549,70,220,223,48,38,311,112,67,409,113,780,7,243,675,767,27,17,209,24,103,80,394,96,21,315,54,307,513,536,83,287,787,309,37,281,6,16,106,807,171,587,496,267,19,32,631,474,270,491,12,35,181,26,583,454,74,869,820,852,36,324,721,255,845,278,64,73,335,357,89,87,140,72,537,25,760,290,18,659,614,430,189,66,97,133,663,114,108,592,552,128,331,666,764,141,606,3,86,380,486,366,76,65,148,10 }, +{ 7,107,232,135,97,387,92,356,298,66,14,2,192,359,278,172,52,357,126,324,448,35,60,526,314,16,38,29,46,330,380,43,362,259,392,230,20,226,74,140,262,246,279,147,24,825,64,58,5,121,568,87,294,32,220,501,132,21,612,465,95,150,86,104,620,18,12,71,173,581,54,534,0,893,37,22,808,583,178,353,90,10,580,550,146,707,802,600,606,331,194,112,222,28,69,660,853,65,70,199,171,68,560,1,174,36,50,77,49,48,779,500,204,683,493,414,6,61,25,764,55,138,33,102,750,888,307,45 }, +{ 20,43,104,414,560,426,707,784,173,319,861,7,819,81,715,422,879,0,376,330,74,95,97,52,32,192,64,38,22,49,526,356,66,204,681,397,207,140,232,5,470,65,693,61,359,636,387,500,107,324,28,91,712,18,10,29,147,77,21,595,392,113,577,172,729,2,325,24,138,92,33,347,125,86,945,102,90,306,40,115,4,198,370,583,230,174,12,904,180,11,858,312,153,124,17,587,750,76,59,69,344,298,3,135,13,37,46,825,718,357,165,348,315,214,72,887,117,289,353,246,196,141,84,36,427,51,237,48 }, +{ 104,74,636,204,355,222,66,81,0,145,25,319,784,746,192,750,29,20,173,174,792,64,90,138,280,715,65,194,91,879,525,32,22,4,348,287,43,95,18,422,7,214,415,86,77,42,40,6,681,289,59,524,151,33,10,52,712,2,28,544,945,13,597,102,45,177,97,21,57,11,243,49,85,36,312,500,426,693,141,5,673,624,396,16,1,122,87,115,648,24,26,186,632,233,67,17,172,414,304,416,123,12,353,389,117,651,580,69,588,751,153,113,874,112,14,23,227,216,128,58,217,197,27,875,114,635,137,707 }, +{ 101,0,9,35,68,154,252,39,65,28,364,124,166,100,149,336,158,289,346,439,429,419,67,55,114,570,694,30,16,265,24,109,484,88,1,329,69,194,404,637,49,189,389,547,133,398,703,826,671,283,22,168,610,175,212,190,7,83,345,103,64,14,768,399,71,274,333,344,230,95,799,50,21,706,36,722,677,420,10,185,962,51,23,18,553,140,254,159,125,967,535,676,46,48,310,2,86,96,952,264,13,12,169,382,5,596,108,323,705,782,251,118,208,857,702,756,562,865,438,218,60,267,131,237,37,755,105,272 }, +{ 57,14,4,236,231,59,369,361,300,585,342,51,23,719,401,13,38,176,46,202,62,402,3,56,210,54,12,2,412,803,381,457,215,11,507,128,40,757,555,77,514,961,32,730,629,322,161,328,37,162,33,133,283,108,129,21,211,34,102,494,17,152,130,29,99,139,153,52,527,472,181,79,123,475,229,6,497,899,453,240,758,194,205,184,188,183,177,0,136,377,305,134,155,248,165,157,425,290,440,714,495,209,739,196,277,269,80,50,556,10,805,64,65,523,503,386,257,141,16,7,5,179,403,84,600,469,318,180 }, +{ 2,1,3,5,141,6,7,15,0,269,180,64,33,77,10,352,102,13,202,128,318,11,197,17,14,4,291,12,134,117,9,120,752,165,153,196,137,217,498,341,349,260,110,21,342,93,40,31,237,23,317,44,82,115,22,16,59,19,86,51,351,678,32,57,36,961,37,450,111,125,365,457,27,143,901,569,95,79,203,49,403,873,326,8,139,494,864,372,28,98,402,116,18,244,56,854,509,48,719,386,947,29,922,954,76,94,373,38,45,523,25,391,285,99,730,546,144,475,275,72,90,164,210,26,24,453,817,253 }, +{ 5,21,61,48,32,202,1,70,259,13,58,115,22,23,178,90,0,494,51,12,475,453,182,18,465,403,49,17,76,450,120,142,318,141,137,147,534,455,402,146,132,128,64,31,11,522,260,771,953,922,859,523,420,269,241,221,207,175,121,72,37,6,4,3,2,768,205,179,415,376,362,28,10,237,30,937,702,113,68,16,9,798,247,590,326,197,165,626,344,279,347,878,125,195,955,933,928,868,854,851,821,815,795,780,765,727,678,668,657,576,562,540,503,502,493,488,482,430,423,411,365,288,285,281,273,118,87,65 }, +{ 205,141,59,152,245,4,352,72,497,76,216,247,79,452,11,588,94,21,188,217,89,875,77,339,864,954,464,269,115,148,64,12,37,529,38,111,32,358,180,5,316,405,471,490,45,474,365,317,349,752,177,288,437,498,51,99,90,468,102,372,128,318,48,1,237,17,542,23,947,241,431,221,54,3,33,291,2,734,197,413,377,270,460,13,401,961,873,341,14,196,579,482,244,569,629,40,308,645,184,257,304,678,720,449,36,10,202,749,275,134,117,428,15,290,153,143,453,57,139,7,430,6,350,769,281,210,476,403 }, +{ 15,1,0,2,5,13,4,3,7,6,23,365,341,115,8,51,9,77,31,10,457,93,44,569,120,197,202,141,12,961,165,33,317,450,901,11,260,55,110,352,22,719,730,453,217,98,82,64,401,18,102,40,21,269,523,403,70,386,49,351,752,105,498,28,16,629,308,318,111,61,898,30,117,475,509,391,817,95,922,342,36,59,494,210,134,899,137,421,873,116,854,14,32,177,196,139,153,734,128,143,291,24,20,349,37,758,372,247,90,326,690,801,113,216,48,275,670,27,125,25,39,57,638,17,304,35,237,244 }, +{ 5,2,50,58,171,186,46,29,14,52,45,38,61,155,17,218,1,281,334,151,227,400,48,487,502,36,54,760,67,133,103,114,21,96,328,236,366,243,80,552,209,83,311,828,27,745,160,920,19,24,766,32,6,108,832,65,12,223,106,514,3,433,70,787,842,99,28,549,113,112,834,540,361,163,26,37,536,516,496,137,765,75,480,454,754,532,780,0,430,966,739,22,238,86,474,51,767,283,18,25,583,115,285,587,309,7,73,287,72,255,23,600,412,128,822,807,13,323,315,208,76,119,545,10,956,270,64,16 }, +{ 457,795,70,120,846,48,23,761,125,318,64,751,415,291,758,221,763,202,391,269,87,401,77,352,237,21,165,128,96,163,558,13,1,260,137,494,5,51,851,767,141,197,546,557,867,597,49,518,102,349,450,623,509,33,98,177,678,922,900,743,498,293,31,223,342,403,371,32,180,873,854,0,217,884,196,153,95,4,44,308,7,2,541,17,30,127,765,58,351,372,386,117,807,6,903,704,182,845,22,134,663,210,105,39,27,402,326,859,307,159,285,281,82,817,719,455,353,312,192,61,931,523,203,947,247,207,385,45 }, +{ 23,51,13,40,453,102,33,4,196,77,117,475,64,153,0,22,10,11,65,59,15,12,134,21,1,403,139,202,95,14,210,28,177,108,18,115,141,165,57,137,128,457,86,2,31,99,6,37,36,401,269,49,9,318,82,7,275,180,5,24,217,342,485,16,111,277,125,386,291,44,32,300,197,19,421,352,46,48,194,730,341,237,17,120,93,494,283,133,90,25,523,260,3,285,114,89,216,8,317,899,38,961,719,498,312,50,26,20,365,351,67,391,54,110,373,326,289,208,168,546,752,569,308,304,98,377,402,361 }, +{ 229,152,266,57,381,452,432,313,471,12,99,17,184,339,369,62,4,129,59,157,128,77,497,3,11,38,123,202,162,37,291,188,257,40,403,136,5,216,21,205,102,111,775,79,51,475,14,33,32,45,402,453,54,619,90,119,115,64,76,177,2,106,361,377,72,880,139,23,494,130,277,757,183,349,84,401,27,56,308,89,80,19,318,141,117,1,94,10,328,529,474,210,245,249,41,36,217,269,421,86,618,6,148,556,342,305,588,179,383,0,134,34,514,477,304,769,542,22,214,378,275,137,13,65,300,95,285,82 }, +{ 49,109,159,28,272,22,327,9,105,95,254,168,131,374,286,347,39,35,424,158,55,175,169,420,627,578,312,67,346,194,0,912,1,224,219,65,86,239,892,36,83,518,114,688,320,538,127,634,584,913,439,371,64,616,30,69,24,10,535,187,336,2,228,7,716,289,68,98,601,23,16,31,164,654,208,505,51,258,14,13,170,874,128,44,12,837,103,890,160,115,385,137,345,50,185,143,116,756,251,141,101,96,190,747,333,125,202,5,166,708,133,21,637,570,242,237,46,835,189,165,638,149,610,706,100,751,48,577 }, +{ 5,61,29,58,311,2,45,209,80,52,454,227,243,106,17,496,592,869,1,151,255,334,75,667,287,70,738,549,171,48,46,483,663,74,155,14,50,163,207,666,290,6,186,119,32,27,378,21,220,659,263,112,136,84,742,307,833,7,400,780,469,767,62,64,72,513,113,148,262,56,409,270,161,331,315,635,425,89,19,38,495,631,3,76,73,383,887,502,69,66,34,223,128,26,240,54,86,306,87,491,96,67,845,408,135,814,37,4,25,445,181,95,294,24,90,226,103,10,405,237,12,297,141,22,655,129,11,16 }, +{ 260,244,373,276,44,269,141,228,110,131,574,219,137,30,839,717,224,217,661,36,352,567,406,120,1,116,253,93,318,160,202,341,31,21,88,201,39,190,187,77,372,752,96,180,64,851,268,55,98,317,82,22,258,613,326,86,351,48,51,23,115,295,213,100,539,291,144,860,35,922,864,9,450,261,489,28,365,105,13,177,442,840,515,321,83,166,856,854,49,935,203,530,284,0,274,349,816,69,197,954,251,165,67,10,811,434,247,778,143,385,531,254,968,957,237,498,843,127,384,669,466,824,102,223,32,5,871,800 }, +{ 99,12,139,79,453,11,277,196,474,51,37,542,111,401,23,94,188,775,33,13,40,102,313,339,961,629,153,431,477,4,38,485,475,300,115,569,134,117,377,157,210,59,14,471,266,15,89,328,880,148,369,57,457,54,413,405,308,403,197,152,128,72,64,165,734,10,3,2,21,17,670,488,719,202,229,82,757,0,5,402,32,41,77,275,177,45,76,29,305,22,342,36,498,1,730,46,494,184,28,318,93,31,529,162,514,6,269,27,482,62,110,873,528,452,179,120,141,137,86,421,317,7,237,129,8,18,361,428 }, +{ 68,35,16,149,24,71,9,69,0,18,67,118,101,212,65,191,114,230,167,133,124,103,399,140,154,1,50,283,246,398,60,7,64,565,55,695,39,218,336,723,375,302,796,28,108,238,267,705,83,189,330,100,23,486,589,702,562,458,21,443,175,13,361,48,75,345,92,96,22,51,181,237,532,755,46,30,2,115,88,14,10,86,363,650,732,335,12,36,49,54,891,520,217,411,516,141,166,95,412,394,641,180,37,5,906,478,447,223,251,247,264,433,252,389,146,480,98,596,602,545,830,346,664,309,271,121,364,628 }, +{ 88,274,435,30,613,100,131,829,166,530,382,93,960,795,190,127,856,466,957,187,521,264,473,778,31,254,228,716,406,1,686,39,863,203,410,224,800,457,137,9,160,704,219,438,28,924,944,120,450,144,836,492,299,326,260,44,116,840,98,202,83,96,321,36,0,21,921,55,141,367,341,827,639,385,265,158,128,772,201,49,539,64,258,317,180,110,86,269,851,442,295,125,908,77,662,276,23,669,744,272,373,242,105,82,931,48,35,968,351,168,318,859,115,578,822,33,557,418,371,616,32,625,67,213,217,109,523,365 }, +{ 167,118,212,18,16,71,60,24,191,375,411,68,363,146,447,101,199,589,271,562,126,302,647,121,815,9,628,566,273,35,575,664,21,92,55,781,621,1,48,777,478,0,149,936,818,108,520,150,354,154,446,847,463,124,346,132,264,98,107,7,65,522,156,28,759,13,22,23,39,49,932,727,882,5,14,223,32,640,608,467,100,51,604,125,695,10,69,724,806,246,221,50,140,195,517,67,252,96,768,279,293,31,360,848,88,166,105,691,95,682,674,46,535,103,2,175,649,109,159,598,458,265,329,364,914,12,653,33 }, +{ 141,64,86,177,128,597,77,269,95,180,237,352,304,291,275,147,588,372,102,194,125,947,13,678,49,875,202,349,120,437,115,4,59,137,153,751,197,40,208,342,465,954,67,23,5,318,135,32,216,660,470,534,196,182,98,31,22,612,90,38,312,448,107,7,10,581,165,353,192,172,707,51,262,498,494,117,33,21,17,1,389,76,203,14,46,565,563,65,569,546,391,330,285,284,277,210,205,139,134,26,25,20,19,12,9,6,3,620,217,11,326,97,853,629,259,278,232,789,380,365,294,289,244,55,43,0,247,111 }, +{ 18,289,701,65,240,194,90,403,523,214,421,283,475,389,217,102,862,202,847,437,494,453,650,64,697,874,51,352,751,401,817,33,84,0,899,13,597,5,196,805,32,23,11,954,95,3,27,22,24,757,875,141,671,312,108,21,153,19,306,114,118,247,450,288,77,17,730,56,4,12,177,942,273,958,814,117,519,86,61,128,859,207,835,193,880,123,249,838,28,120,342,49,134,300,2,6,801,216,719,57,457,576,864,304,125,302,124,14,615,372,16,210,40,237,60,577,504,119,770,608,752,62,10,9,643,48,1,15 }, +{ 51,23,13,202,115,21,12,5,32,128,403,453,141,401,165,308,120,318,457,15,475,4,269,64,1,0,197,10,719,494,99,2,450,37,48,730,3,180,523,77,7,18,386,14,9,6,16,59,22,217,260,125,11,17,90,177,341,237,117,421,153,24,221,899,33,352,317,102,28,326,137,247,82,817,961,19,40,365,402,391,569,27,629,57,95,49,25,31,8,216,210,110,758,304,72,29,93,54,351,26,38,291,98,20,134,61,196,76,56,45,44,241,275,139,372,349,509,36,65,342,350,615,678,285,89,214,50,670 }, +{ 98,223,393,125,31,791,834,116,271,1,93,202,697,44,64,167,127,293,5,541,51,446,801,77,450,212,48,284,807,234,120,23,765,21,318,13,421,18,195,948,639,551,132,360,730,770,141,142,562,146,163,96,386,49,963,144,668,354,591,197,346,128,260,221,0,198,82,165,416,269,494,670,367,949,817,147,33,178,522,70,32,535,455,719,137,453,326,203,842,22,102,687,61,156,55,121,407,4,315,2,325,105,403,883,110,329,217,342,95,692,10,781,615,463,798,12,958,291,351,7,17,39,878,586,113,196,28,68 }, +{ 15,4,40,11,23,165,13,51,110,93,120,32,82,77,125,33,197,141,260,202,59,10,0,21,102,64,180,5,57,31,453,22,386,12,269,8,318,128,403,134,3,139,95,475,457,730,44,9,49,719,326,117,203,1,115,351,98,450,17,352,28,99,116,217,494,523,210,373,391,961,37,177,90,144,137,2,342,18,153,317,401,56,854,14,48,65,237,341,36,758,421,291,111,277,402,365,509,30,7,922,242,105,275,372,349,24,86,901,801,899,898,300,39,308,817,72,6,670,16,45,752,498,27,38,76,304,170,615 }, +{ 15,1,0,13,2,5,901,7,23,6,9,341,8,3,4,51,961,12,115,10,898,180,82,569,134,31,22,141,351,202,457,120,44,269,165,77,260,365,403,401,318,317,14,33,217,11,854,93,28,30,453,18,137,37,386,21,110,64,719,36,498,352,128,450,16,117,922,475,509,49,177,55,111,391,40,730,523,99,326,98,102,629,758,153,32,39,642,372,24,873,752,116,291,308,421,125,373,95,143,482,203,817,494,35,899,196,86,304,349,17,244,38,237,65,275,247,242,59,105,402,210,638,48,615,144,342,148,127 }, +{ 234,416,77,315,5,639,202,325,147,113,198,49,450,455,61,142,329,178,21,0,427,470,494,342,58,32,557,22,318,230,725,43,858,112,120,70,102,13,48,263,20,344,260,884,425,817,128,798,115,290,105,518,291,137,95,904,23,403,64,141,491,159,241,845,523,31,121,33,319,97,320,392,246,196,153,17,173,104,10,39,663,269,117,592,501,125,51,347,72,30,163,28,253,170,408,194,376,107,18,265,544,326,352,270,341,4,738,615,534,475,595,98,259,66,631,449,301,922,158,76,134,720,512,312,9,180,859,690 }, +{ 146,60,271,126,16,156,360,199,18,121,167,640,522,132,24,446,354,591,71,463,118,340,150,92,598,273,441,195,107,212,22,0,407,10,5,21,108,467,28,279,586,575,423,566,727,191,101,493,649,534,68,95,608,517,55,35,32,783,653,9,408,48,49,508,363,296,607,698,246,13,668,33,64,117,109,356,98,314,39,658,140,105,7,362,660,447,809,411,955,93,31,621,50,303,298,153,347,12,158,125,65,953,713,264,163,350,359,141,124,14,143,51,939,478,149,115,36,75,4,781,581,225,207,102,77,30,562,44 }, +{ 4,11,361,300,77,56,377,368,14,59,40,161,51,128,33,200,210,23,378,27,54,495,102,554,38,494,453,17,21,12,202,80,444,108,53,757,37,46,78,13,245,457,342,390,139,133,123,386,84,523,32,605,196,862,527,736,803,99,73,57,19,630,561,475,3,542,509,421,317,250,181,136,111,277,269,450,197,472,183,48,26,485,730,297,62,714,402,381,403,391,880,122,719,341,318,306,145,25,5,514,256,61,899,546,106,90,50,34,383,833,236,775,497,165,129,85,401,366,184,469,436,838,358,328,316,291,275,241 }, +{ 141,351,217,82,352,120,854,260,64,15,180,372,1,922,269,317,36,137,752,373,13,21,349,51,898,450,77,202,110,23,318,160,93,115,291,128,365,48,197,177,901,96,5,165,244,864,10,86,457,4,40,237,523,247,33,579,341,32,304,11,661,125,83,143,498,326,817,2,203,275,437,69,102,954,717,16,59,961,35,386,851,730,22,475,28,253,31,7,57,98,95,134,144,391,12,6,719,117,210,116,17,196,139,0,3,37,494,473,403,114,49,30,29,164,899,704,559,153,90,72,55,9,45,509,482,453,393,230 }, +{ 15,77,13,1,0,102,23,33,2,4,51,5,10,291,7,9,3,450,6,217,117,120,177,317,341,260,11,12,817,342,365,115,8,134,21,153,180,16,18,22,349,40,165,82,14,64,141,453,372,31,457,523,901,196,197,326,275,352,210,961,719,730,28,304,475,403,569,44,128,32,899,93,59,269,401,24,17,49,752,98,318,110,36,629,216,30,386,125,95,494,90,137,48,351,308,116,111,57,139,37,86,99,498,65,615,247,482,144,39,237,221,20,96,19,244,421,300,758,203,670,194,509,38,854,402,873,437,391 }, +{ 15,23,13,77,141,260,137,120,51,202,180,115,1,365,217,0,341,450,269,291,317,318,5,197,128,21,9,177,373,351,110,352,165,2,12,523,7,64,349,326,10,48,36,901,32,33,22,403,854,82,90,96,4,922,16,37,28,18,752,457,40,386,244,372,102,494,6,308,8,160,86,24,30,14,401,49,661,817,453,719,421,402,11,704,961,475,237,873,342,55,3,39,730,391,59,196,93,221,117,143,216,615,898,44,498,275,253,210,304,35,285,95,125,31,98,17,139,509,72,153,811,482,89,83,144,899,99,576 }, +{ 348,174,85,138,6,280,74,355,233,192,204,636,580,353,343,289,673,66,65,91,81,104,712,750,25,42,214,792,95,64,681,173,194,32,715,135,90,312,29,879,172,825,63,235,835,86,22,87,145,370,5,256,7,222,415,52,0,874,426,97,500,519,17,26,237,43,4,288,36,18,10,389,20,33,560,76,21,61,40,544,45,59,249,888,77,577,746,232,28,102,2,72,49,568,123,287,56,525,448,751,180,11,337,526,707,671,226,27,19,247,24,319,117,48,422,114,12,69,108,84,208,597,37,1,861,67,889,58 }, +{ 152,452,497,59,216,4,269,188,77,11,79,339,128,529,94,471,318,141,202,588,32,76,377,177,678,33,111,291,349,90,5,102,21,137,51,72,12,179,542,38,99,64,257,184,37,1,245,217,460,196,48,372,115,947,148,45,402,474,40,89,342,210,17,352,205,288,3,153,769,13,117,10,2,134,292,54,275,304,139,875,498,431,23,405,241,403,247,317,569,229,401,453,494,285,358,180,197,86,300,57,0,476,961,6,954,308,36,143,524,165,277,475,316,597,14,95,237,15,7,221,341,58,157,735,129,266,214,421 }, +{ 417,253,244,499,141,10,559,564,110,8,260,352,728,143,180,951,120,642,638,661,752,922,341,373,811,137,269,901,717,197,93,206,498,44,165,31,116,203,160,854,384,351,36,873,762,326,704,268,217,1,64,115,282,317,959,55,144,15,365,82,898,968,509,164,86,479,318,13,851,22,21,332,473,434,51,202,77,940,911,569,23,125,450,864,824,237,261,9,242,615,372,617,127,48,201,102,128,4,33,5,96,506,0,391,117,98,28,401,386,40,295,105,32,758,531,276,338,37,83,35,349,59,533,153,177,629,11,12 }, +{ 77,13,23,51,177,102,33,115,59,4,40,0,141,128,90,210,202,137,64,22,269,291,217,10,196,28,153,117,318,7,49,2,14,216,134,18,180,16,6,11,9,24,403,1,275,197,3,95,36,86,125,165,247,342,31,352,494,19,32,402,12,139,453,317,20,237,98,65,15,349,44,475,26,285,17,30,120,111,57,93,21,372,105,27,25,719,194,457,341,203,304,386,260,391,730,116,421,365,35,29,5,99,326,123,498,208,39,569,509,244,288,752,450,143,678,144,110,523,401,308,82,214,300,351,43,37,48,164 }, +{ 1,2,22,50,67,14,36,5,46,28,103,12,0,38,83,114,65,168,194,7,69,133,86,95,24,49,218,29,54,21,55,96,109,160,51,10,584,16,128,6,9,23,48,158,18,75,756,37,35,105,169,32,389,289,159,323,238,181,52,244,202,13,503,646,164,137,532,141,239,115,361,480,185,64,17,143,789,283,124,708,253,505,946,597,3,717,45,776,58,170,108,269,70,735,312,874,285,610,68,782,189,318,424,71,531,31,308,237,320,171,4,892,72,402,99,328,220,418,251,420,394,19,180,577,487,401,347,344 }, +{ 17,106,119,378,742,255,306,136,207,240,84,383,62,790,80,655,61,45,5,56,311,3,209,504,440,667,76,151,32,58,263,483,179,29,2,833,292,548,214,123,129,425,186,454,128,243,205,738,445,64,21,245,148,52,38,14,26,249,57,27,328,19,90,6,4,555,11,34,59,54,162,202,184,507,469,141,592,229,48,215,887,710,152,73,86,40,161,72,618,460,266,257,491,77,358,211,227,287,25,869,183,318,297,50,290,495,10,216,130,614,155,1,476,177,137,361,12,315,37,112,33,22,46,95,113,269,194,171 }, +{ 141,128,202,3,5,2,1,269,64,318,6,33,17,102,15,180,0,196,137,10,153,7,120,352,197,13,77,117,51,134,403,14,165,12,23,402,11,93,9,342,260,82,110,21,494,19,22,237,31,351,4,125,86,475,457,450,326,27,44,32,203,40,36,95,291,961,453,115,25,285,143,111,678,98,341,37,139,99,523,817,49,57,752,498,45,386,18,38,217,8,116,65,16,244,28,144,317,365,56,719,373,901,194,854,79,898,277,59,29,576,26,421,30,569,401,730,947,177,164,864,48,72,391,922,24,873,94,349 }, +{ 38,514,328,377,11,57,266,248,41,880,313,471,556,152,361,4,485,99,305,757,403,157,12,102,339,54,369,40,211,130,542,457,3,37,188,14,139,23,13,153,494,117,33,475,775,229,507,381,300,196,77,453,452,59,555,162,488,184,277,585,236,477,440,629,62,128,51,111,421,134,402,183,215,961,670,79,548,123,342,210,202,89,401,527,474,94,65,587,719,543,432,181,45,17,257,291,80,64,72,21,619,108,32,413,828,46,5,29,0,95,2,275,129,308,10,56,504,205,86,618,155,76,177,49,431,569,90,497 }, +{ 68,520,167,101,264,118,478,562,0,9,664,777,936,604,212,100,191,252,154,18,124,806,265,16,759,21,48,1,71,375,88,724,628,24,329,35,682,411,410,363,166,302,815,447,65,419,589,55,23,39,647,149,917,909,821,382,125,942,98,346,925,772,13,701,146,96,223,51,108,31,10,221,271,22,60,28,321,44,92,905,69,30,676,639,49,593,364,882,274,694,521,907,64,105,5,158,67,621,781,812,126,133,553,189,848,897,213,686,95,32,121,404,522,199,103,116,156,50,140,83,429,706,763,273,115,165,127,77 }, +{ 230,689,699,213,466,831,352,217,30,443,418,854,144,201,840,855,251,203,957,822,96,530,1,539,93,36,137,800,317,83,190,128,326,669,752,351,748,494,88,206,160,31,473,859,335,202,141,44,321,253,735,498,269,435,116,367,187,86,35,927,332,180,237,69,98,778,244,285,219,82,131,576,228,617,208,922,866,930,372,100,120,64,2,934,403,349,531,702,48,21,731,817,274,23,5,10,13,406,365,224,947,824,795,345,898,291,382,295,223,692,318,286,238,39,341,276,863,77,615,166,260,625,678,51,28,67,410,125 }, +{ 77,33,102,117,134,153,196,291,13,51,15,23,82,64,40,202,128,120,0,141,260,351,450,22,4,95,59,137,269,342,177,386,318,326,11,115,10,210,65,373,86,180,217,49,403,1,165,275,28,18,453,57,36,125,98,352,494,523,139,197,14,31,854,475,2,194,216,9,7,817,21,719,93,6,730,24,457,237,90,12,116,16,44,349,110,3,5,402,99,961,317,114,372,19,48,32,285,105,96,17,365,341,300,143,8,203,401,247,164,108,421,25,752,144,26,30,20,391,39,111,304,109,758,50,221,208,37,289 }, +{ 11,40,33,51,542,328,13,117,38,14,23,139,153,134,54,102,111,12,485,377,775,37,99,403,231,277,401,719,369,3,475,453,514,4,629,308,211,880,457,291,305,89,266,152,130,2,57,196,961,6,488,215,5,176,59,555,471,313,72,300,477,183,62,184,79,0,507,248,115,21,670,339,249,548,361,1,165,292,29,197,229,77,556,474,236,386,421,157,413,188,452,17,758,41,162,391,449,587,32,46,123,428,509,52,247,569,136,899,757,523,210,753,155,129,76,50,237,64,730,720,476,383,312,440,275,90,45,342 }, +{ 77,64,141,202,33,102,128,153,2,117,269,6,23,318,1,13,0,134,137,51,5,180,3,196,10,7,40,165,15,14,352,197,86,291,237,115,403,95,17,177,22,217,36,139,210,9,120,19,125,342,494,275,82,475,457,49,453,16,12,31,4,65,21,402,11,143,260,194,110,93,450,25,28,351,317,111,498,326,27,18,349,752,44,341,59,164,386,26,285,116,365,98,719,208,32,391,300,509,401,523,99,20,24,38,114,244,373,730,55,48,901,372,203,312,961,8,277,160,678,304,34,37,57,854,56,546,29,144 }, +{ 15,4,93,5,13,21,11,1,23,32,110,141,64,82,180,2,33,40,3,98,352,120,6,10,0,12,77,7,341,197,37,51,102,351,269,854,95,17,202,31,9,752,901,44,165,117,48,318,153,291,134,137,115,365,237,196,372,59,498,260,349,450,14,128,90,36,317,244,899,22,342,922,437,125,143,873,453,16,160,72,105,482,179,817,19,89,401,457,57,661,403,523,177,164,45,308,864,373,253,245,8,898,49,421,569,223,76,719,28,61,18,247,139,56,96,27,490,210,468,111,494,475,144,38,961,391,304,25 }, +{ 1,2,50,46,14,67,103,38,24,83,133,29,52,96,75,28,114,5,36,238,65,54,108,171,70,0,58,18,69,181,160,532,236,7,22,412,433,754,283,55,163,220,480,361,487,124,12,223,545,45,328,394,21,398,16,155,9,366,48,35,956,6,154,87,323,646,309,514,112,186,822,137,17,109,149,418,745,175,547,86,37,285,68,800,866,10,429,732,516,25,208,302,51,32,118,334,281,99,531,71,61,194,537,458,23,891,389,19,151,675,409,336,128,168,13,400,307,95,262,832,227,49,273,420,3,115,484,64 }, +{ 258,276,860,201,843,295,137,160,567,116,261,144,268,943,284,202,187,935,662,203,699,131,219,224,44,141,127,36,326,96,98,31,318,269,244,964,23,93,384,489,55,21,434,332,13,403,51,120,1,617,918,352,908,367,110,206,180,365,254,197,492,473,871,442,48,82,10,143,800,260,39,574,28,944,299,940,282,253,9,115,494,418,341,125,855,77,8,49,128,836,535,165,100,576,30,523,385,105,822,217,395,959,564,22,531,911,37,83,393,851,717,64,817,752,421,716,5,317,901,551,373,859,0,170,291,599,533,251 }, +{ 7,2,14,87,46,52,278,92,16,140,75,29,135,294,262,70,38,35,172,171,409,24,60,58,69,5,97,112,66,314,1,163,571,71,50,461,155,267,232,330,18,220,54,334,357,126,380,307,21,74,12,230,48,309,186,675,32,37,298,331,226,246,151,45,774,107,64,337,25,324,6,67,606,189,622,20,448,150,43,61,0,400,22,849,287,10,68,9,86,563,28,95,227,583,192,394,620,103,36,764,572,108,128,335,83,55,587,550,13,145,600,549,536,65,141,852,366,49,415,90,26,236,526,950,17,4,181,72 }, +{ 13,23,51,115,2,33,77,0,1,10,4,7,217,5,9,141,102,6,3,180,11,40,317,22,15,64,14,197,352,202,16,12,341,269,365,165,18,318,177,17,59,153,453,196,752,291,32,117,308,237,125,403,31,128,8,36,401,24,28,349,372,49,498,137,475,247,95,304,143,569,901,21,98,468,457,134,44,139,19,90,421,86,210,120,386,629,93,37,116,482,494,57,110,873,342,55,961,275,111,391,546,35,509,244,203,30,27,99,29,437,48,20,260,65,719,216,734,25,38,54,450,326,50,351,46,864,105,253 }, +{ 74,145,204,66,138,42,25,6,337,174,525,222,192,29,26,544,746,287,415,632,85,609,135,7,353,280,112,580,87,396,712,355,64,122,81,104,2,45,173,673,416,648,172,43,792,226,52,22,97,651,151,194,70,65,0,19,32,20,715,95,825,348,5,624,233,113,86,635,91,879,294,10,644,235,1,90,500,36,573,560,49,636,243,58,63,21,331,426,61,75,389,27,445,14,69,750,28,289,46,38,17,888,311,312,18,24,140,186,76,283,141,12,874,16,232,278,72,461,707,53,4,526,597,255,665,180,114,881 }, +{ 77,217,33,102,317,291,202,23,13,51,141,15,275,269,318,115,342,180,21,4,196,352,64,128,11,494,32,5,137,403,40,2,0,125,3,177,10,98,365,349,12,7,372,6,341,453,165,197,153,475,48,752,22,237,31,117,9,95,59,93,16,90,120,247,308,450,49,221,244,36,28,18,144,86,14,134,326,421,203,44,17,304,24,116,523,260,110,864,210,498,401,901,1,35,241,386,82,127,817,39,72,437,160,105,65,143,730,719,351,216,457,402,30,350,293,164,37,859,96,367,139,284,19,579,576,391,285,373 }, +{ 187,258,871,219,442,574,908,839,926,160,116,44,137,224,276,201,36,860,131,202,613,228,144,531,406,93,31,902,567,385,843,318,203,268,800,98,662,856,494,23,190,120,127,418,30,295,533,836,1,284,96,822,244,64,944,717,86,851,55,13,21,51,22,840,434,269,251,367,489,326,48,110,141,699,816,260,39,384,261,371,473,83,206,254,859,10,669,492,28,317,373,128,49,365,450,77,180,863,559,530,82,829,341,918,253,32,9,197,125,351,332,165,143,943,728,535,67,831,576,855,940,403,217,716,105,274,69,959 }, +{ 1,372,23,5,141,21,744,13,291,77,225,349,144,48,459,96,839,260,304,269,442,335,691,352,32,30,622,203,303,284,330,64,45,521,180,317,217,160,88,752,418,120,93,190,948,166,954,340,919,686,342,244,228,202,201,189,137,110,321,914,411,447,781,772,254,51,765,31,656,264,237,167,724,478,842,437,274,127,125,69,44,662,466,435,351,326,82,9,897,410,253,716,286,678,479,299,238,197,68,7,2,934,223,100,70,58,154,98,682,393,967,960,922,856,805,762,475,402,365,341,336,285,247,221,177,169,163,131 }, +{ 66,222,74,135,87,29,145,2,337,294,52,172,138,331,6,7,25,226,42,97,632,70,192,112,353,609,525,415,461,204,287,500,26,174,75,43,324,1,58,32,220,5,232,544,580,85,620,635,278,45,746,64,426,568,448,396,262,122,173,151,69,20,95,19,61,526,140,65,889,86,307,14,104,280,21,46,81,355,92,38,872,22,387,651,186,648,113,194,416,72,163,673,90,24,35,155,49,16,330,37,792,0,888,243,17,36,76,861,10,48,849,67,644,227,50,4,89,853,334,63,27,314,312,12,825,712,91,298 }, +{ 45,17,106,209,255,119,186,5,263,207,454,29,2,48,425,243,21,496,62,290,3,270,400,408,52,155,136,659,171,14,56,513,50,80,84,32,378,46,61,655,666,742,221,58,383,38,1,54,311,306,240,721,181,440,502,430,334,129,72,536,227,123,76,790,592,214,89,328,281,555,34,787,241,148,64,215,663,287,587,37,11,405,162,27,237,504,57,90,130,152,541,179,12,211,548,41,36,552,184,4,741,115,431,292,449,869,229,96,77,507,223,128,183,40,7,86,51,249,205,245,161,6,738,236,103,151,59,10 }, +{ 213,88,260,120,93,373,141,30,100,450,166,36,372,321,459,473,264,180,110,326,1,225,144,704,717,351,82,855,530,269,466,352,217,265,190,22,274,131,96,539,165,86,457,291,28,244,64,187,318,752,39,349,661,330,9,160,202,219,77,410,197,44,908,254,116,49,137,968,276,317,125,406,31,521,523,435,15,98,228,128,32,299,35,662,293,143,115,21,934,778,224,33,856,923,876,854,689,403,371,341,10,935,851,498,437,8,922,48,642,776,903,839,836,730,613,365,268,242,55,0,944,899,492,898,615,385,304,285 }, +{ 266,57,152,381,471,313,369,99,229,12,339,62,157,77,4,37,188,40,475,162,38,3,452,775,129,184,403,17,59,128,11,432,202,111,453,33,56,183,291,401,216,361,328,102,136,402,79,54,440,14,139,880,123,494,51,23,514,277,507,130,757,474,377,21,94,529,177,548,421,349,64,76,211,90,117,89,72,308,13,318,383,5,141,210,134,556,342,153,305,497,257,555,119,485,84,269,275,106,670,2,115,477,488,45,196,41,300,205,32,542,10,543,304,431,86,180,504,19,27,82,179,36,629,249,46,50,237,245 }, +{ 352,217,317,141,752,372,365,180,341,15,349,77,864,21,291,244,115,13,120,269,1,498,5,901,579,98,144,64,23,48,482,351,51,237,102,201,82,437,125,137,318,4,2,247,202,0,31,854,203,143,253,260,32,177,160,490,10,96,110,44,22,494,251,33,128,403,9,3,898,717,164,28,197,72,93,11,873,450,36,165,386,95,373,6,223,661,30,12,49,922,342,206,523,16,457,127,109,468,308,391,116,531,332,473,187,39,88,35,730,453,326,221,7,719,69,86,105,100,83,851,475,90,350,230,678,421,101,304 }, +{ 14,514,369,880,102,403,377,51,719,485,153,23,453,401,457,11,40,13,328,12,57,4,33,38,629,236,670,54,730,117,111,361,961,59,37,134,99,758,757,165,2,46,386,412,421,391,277,29,308,543,477,494,188,600,6,196,21,542,197,139,300,739,475,52,899,79,509,523,488,775,583,283,231,7,803,552,32,5,402,89,828,3,507,176,77,50,15,155,211,64,17,569,215,115,18,381,237,305,266,202,90,34,342,194,128,62,555,389,242,125,108,65,0,471,338,136,56,548,506,221,181,133,10,440,114,248,95,1 }, +{ 107,172,7,359,126,92,14,135,314,278,60,150,298,46,392,279,232,192,2,441,356,330,357,16,32,380,97,534,423,581,35,526,38,448,493,132,52,288,246,660,5,121,66,24,230,324,572,21,387,226,462,259,247,95,220,146,140,77,18,508,147,262,90,48,312,707,20,71,603,54,33,195,550,362,86,236,108,739,199,830,23,64,43,29,895,802,451,22,36,28,929,294,606,50,87,70,501,13,808,177,55,1,194,178,711,316,4,0,102,590,366,49,893,600,37,582,309,872,267,117,388,241,620,115,786,939,571,360 }, +{ 59,4,90,45,216,13,25,0,177,23,77,51,27,95,72,26,115,65,12,91,210,123,275,37,389,19,102,33,11,49,22,18,194,5,214,21,85,53,48,202,343,291,31,57,6,128,42,746,93,73,58,44,32,196,283,81,319,61,141,249,153,64,138,117,422,355,40,348,139,403,789,145,788,300,247,240,134,74,34,804,619,370,104,86,38,1,304,180,524,494,217,20,597,544,318,352,323,233,174,114,76,17,475,280,204,588,342,285,14,3,43,725,287,7,888,636,235,52,29,63,337,232,122,784,125,397,817,678 }, +{ 5,315,416,49,455,639,202,350,77,408,21,197,137,450,402,13,61,509,329,113,713,344,569,347,401,48,0,841,494,318,734,749,64,70,308,95,903,120,342,884,237,557,32,194,546,115,845,1,23,165,663,128,873,102,291,518,22,58,817,391,498,263,17,33,221,269,403,923,260,105,241,125,51,196,312,39,10,31,153,234,112,523,158,18,4,265,12,141,9,159,928,623,326,207,25,177,310,3,2,859,98,28,65,30,163,11,19,275,491,290,6,117,541,16,15,124,76,72,27,922,210,44,341,349,134,773,147,24 }, +{ 130,47,381,390,59,90,200,472,214,714,65,289,6,122,874,85,64,29,751,648,52,50,74,875,247,194,624,249,288,26,312,437,32,217,630,881,4,123,95,78,835,352,196,665,519,33,57,711,524,954,108,42,19,77,22,153,597,416,412,0,145,444,27,243,25,14,183,304,117,453,51,256,141,730,102,177,134,236,315,680,86,112,34,11,115,235,287,237,113,45,283,151,317,588,579,99,3,752,216,342,813,128,445,864,180,56,46,28,18,73,12,23,114,13,389,53,644,719,21,396,58,17,372,947,137,316,10,250 }, +{ 51,453,23,719,13,730,165,457,37,197,99,12,386,401,475,961,4,17,11,64,59,308,54,115,57,38,328,758,391,6,5,2,899,3,753,403,32,523,670,10,509,40,33,421,629,120,21,65,19,22,0,202,125,95,569,117,90,558,29,26,514,494,1,775,128,134,14,450,361,34,9,86,194,177,350,269,72,27,25,7,880,757,111,62,153,316,236,141,50,56,260,217,196,867,546,161,130,106,77,48,41,31,16,8,317,152,468,242,221,136,734,80,89,968,949,862,814,801,717,704,495,469,440,428,412,383,378,326 }, +{ 15,13,1,5,23,2,7,0,6,3,12,51,4,180,115,21,9,165,457,197,141,352,11,14,202,217,10,37,8,32,341,120,22,117,752,269,16,77,134,961,901,719,318,48,317,365,33,110,93,31,569,18,102,28,40,260,17,509,128,44,153,137,82,498,59,629,351,72,386,403,36,49,401,453,730,308,196,450,89,873,30,523,125,99,391,326,177,98,64,247,494,854,24,291,95,475,38,86,482,39,203,922,372,349,116,90,144,57,35,758,55,864,221,468,244,210,421,139,817,76,373,899,54,105,19,304,342,96 }, +{ 0,9,35,28,68,24,67,65,1,16,18,114,69,103,50,101,149,22,175,124,55,39,12,218,133,96,154,189,5,83,21,71,23,7,336,433,251,13,49,75,118,335,2,51,48,486,30,14,10,283,181,745,115,223,137,54,230,109,418,37,345,64,128,141,108,281,99,36,60,100,202,956,537,163,532,480,221,766,302,596,487,95,252,346,58,158,46,180,217,361,237,70,398,86,269,160,31,32,105,657,443,140,285,125,87,212,166,167,502,6,88,375,165,323,831,317,754,29,318,439,77,98,364,38,197,429,44,765 }, +{ 28,105,22,320,109,49,301,0,9,170,95,1,35,55,127,65,24,168,98,159,713,16,512,31,395,36,623,2,7,293,740,14,338,347,424,185,286,12,44,23,21,169,124,5,158,68,67,928,13,371,18,299,242,125,51,289,175,916,64,761,479,374,39,737,685,154,48,272,6,114,599,116,10,86,141,37,194,83,149,506,327,312,841,202,763,237,50,900,870,538,284,115,131,420,346,137,654,385,456,165,187,762,30,276,239,32,308,69,143,269,429,96,570,71,128,180,933,160,164,46,197,221,401,634,965,219,101,318 }, +{ 77,13,51,102,4,59,23,33,202,210,115,40,403,177,128,196,141,153,117,64,494,269,318,134,475,217,342,0,291,11,90,453,22,402,137,15,216,139,10,180,275,86,95,65,57,6,36,18,523,1,125,21,317,365,197,49,28,421,32,14,352,25,19,7,2,899,730,719,391,341,152,24,401,48,27,17,12,901,247,123,114,16,9,111,99,26,349,285,576,50,20,3,386,372,326,260,194,120,110,351,283,482,293,242,165,96,29,37,30,817,801,498,457,450,393,373,299,237,214,203,615,569,116,93,34,82,958,805 }, +{ 13,23,77,51,33,141,115,102,0,217,10,2,6,4,7,64,22,1,11,177,180,40,3,14,202,269,9,291,352,15,165,197,17,128,317,153,196,5,318,18,372,117,16,28,59,36,19,137,95,210,237,247,125,49,86,349,341,304,134,365,24,275,12,31,27,453,403,752,25,143,308,26,57,20,901,120,475,44,401,457,29,498,90,139,65,8,391,93,342,494,216,509,164,98,99,35,55,386,244,111,32,45,37,21,260,38,110,56,450,105,39,730,402,523,46,437,82,30,719,569,203,961,873,326,285,194,116,421 }, +{ 456,116,492,268,949,867,203,51,719,8,391,918,791,457,730,499,13,386,23,206,685,125,31,551,170,870,93,417,401,10,22,479,506,338,940,44,509,143,64,692,670,558,453,0,473,197,523,758,629,341,165,1,564,55,899,762,308,961,421,959,36,638,115,569,801,180,642,86,299,164,28,37,141,965,498,559,9,5,120,237,95,546,260,873,599,137,253,12,244,903,901,2,817,752,728,128,352,450,105,202,7,615,127,365,958,21,301,49,951,403,740,326,239,898,373,922,475,6,854,242,15,3,318,4,269,98,65,99 }, +{ 17,237,106,62,180,45,136,32,498,64,115,41,255,129,546,752,864,197,21,227,241,490,352,482,57,123,292,350,247,509,449,162,365,468,214,38,23,13,4,476,148,76,437,155,507,141,165,77,3,720,317,290,270,873,211,164,72,153,209,117,5,659,54,48,249,217,341,186,710,33,40,14,12,496,119,11,519,90,183,618,139,179,37,328,579,221,431,248,372,405,130,59,99,400,263,205,425,2,358,349,901,51,177,171,89,428,50,454,304,207,542,1,457,308,734,721,391,316,29,569,94,243,143,339,196,313,128,111 }, +{ 1,5,180,15,2,4,0,352,3,13,901,341,7,752,6,141,217,23,365,317,115,197,21,51,165,269,12,11,9,202,31,33,77,569,44,93,318,137,110,120,36,10,22,372,260,498,116,64,32,873,125,48,102,308,8,349,98,482,291,386,401,14,373,326,28,237,37,55,86,134,203,457,391,16,117,177,253,144,509,421,127,153,40,206,403,961,864,468,221,494,437,453,18,17,196,304,143,128,96,854,450,72,247,35,579,89,642,758,82,342,160,629,719,282,811,59,351,922,730,95,638,523,210,546,332,661,49,734 }, +{ 141,205,497,216,269,152,588,59,452,128,4,76,352,875,188,77,79,11,94,678,947,339,954,217,471,372,90,529,72,318,247,64,32,89,177,12,288,37,1,304,349,202,5,51,291,33,474,102,111,23,115,21,99,179,864,38,148,377,13,460,431,498,437,316,180,2,961,40,358,542,137,490,405,241,464,317,3,45,54,7,184,196,17,10,402,752,245,342,210,14,873,48,143,285,153,257,197,308,292,0,36,569,769,237,134,482,139,275,165,468,341,629,413,734,86,476,365,6,300,851,117,751,494,401,270,229,453,29 }, +{ 247,64,317,217,237,437,180,498,349,115,752,579,304,372,864,482,291,141,352,468,197,177,13,391,164,490,23,546,33,365,77,153,72,216,954,597,277,10,708,269,134,569,275,51,695,509,318,179,0,678,244,140,312,640,117,76,629,196,751,128,36,953,674,260,102,901,32,21,22,225,401,86,717,143,92,873,351,16,133,114,191,386,253,165,69,59,9,458,341,202,149,82,649,35,453,398,166,100,95,93,90,125,15,373,330,923,316,48,854,586,213,486,230,922,5,411,823,734,189,139,103,65,50,24,18,12,830,167 }, +{ 2,1,5,7,6,3,77,0,141,13,180,15,217,64,33,23,102,10,352,14,117,9,11,197,165,40,269,17,291,4,115,134,12,153,341,317,752,372,237,51,349,22,498,202,318,16,457,128,196,36,177,365,32,21,59,19,125,901,342,8,86,120,139,93,45,961,57,95,38,143,509,28,29,391,111,210,27,260,873,244,82,137,18,386,164,37,719,25,48,31,304,49,864,98,402,275,569,110,351,629,253,546,26,41,90,494,50,730,56,523,453,44,54,144,24,99,30,403,678,76,898,450,437,55,954,482,308,277 }, +{ 11,40,38,328,542,33,339,313,12,377,157,775,485,99,51,471,41,369,23,37,475,57,54,457,14,117,13,188,305,629,102,403,79,4,139,514,880,961,111,266,719,152,248,474,134,453,401,153,211,342,477,21,2,59,361,89,587,277,94,29,308,196,64,32,413,77,229,210,184,215,402,128,431,381,46,670,5,247,197,165,556,757,386,115,300,50,17,391,803,494,875,490,730,177,162,450,421,275,202,0,6,1,257,76,72,65,48,19,10,129,569,236,130,15,7,555,901,898,588,86,9,543,488,90,452,749,619,507 }, +{ 77,102,33,196,40,51,202,23,153,128,117,13,342,64,134,141,4,59,0,269,210,177,22,318,137,403,65,86,10,475,139,453,95,115,28,99,11,165,217,36,180,197,49,402,291,125,1,352,18,15,494,421,194,275,31,300,110,120,57,90,93,12,237,401,317,6,457,14,2,82,24,114,260,9,386,44,285,108,361,98,16,7,21,349,326,216,5,116,730,341,372,19,133,719,203,48,32,391,39,25,143,111,105,67,576,752,670,961,899,308,498,37,351,304,20,30,283,901,523,365,27,244,208,55,3,164,485,8 }, +{ 472,34,80,495,250,469,176,389,527,17,14,161,53,231,4,128,56,283,216,106,84,57,27,483,177,59,119,503,436,45,585,297,194,29,378,3,42,62,269,136,26,40,11,2,862,678,619,33,73,236,255,514,719,200,25,6,64,240,179,141,52,102,789,361,618,129,597,5,153,611,349,205,65,245,803,402,210,457,123,383,90,304,667,318,814,504,328,133,130,51,32,48,684,108,714,412,215,13,139,19,95,77,47,275,99,12,311,211,165,1,432,207,322,196,117,61,23,524,152,115,369,183,134,114,573,54,162,46 }, +{ 752,352,317,180,141,217,365,269,244,341,498,115,253,901,1,372,202,21,318,4,5,23,700,717,13,137,2,349,197,864,10,873,143,7,482,0,51,3,64,11,67,308,531,12,8,6,28,237,22,31,468,579,533,291,160,221,35,30,96,48,55,33,9,961,120,49,44,39,32,247,83,69,450,326,260,77,36,98,851,509,165,37,16,457,437,110,93,88,82,14,345,822,765,638,421,219,190,131,128,114,203,144,99,800,401,844,453,125,494,475,373,351,241,206,170,164,148,134,127,116,105,100,86,40,38,18,811,19 }, +{ 9,39,101,265,18,333,520,329,100,593,553,364,310,252,167,16,68,604,0,363,404,118,166,121,254,158,272,24,60,271,212,286,639,327,159,35,146,1,109,30,455,411,793,105,557,49,132,647,31,71,621,21,656,354,438,627,676,688,98,48,682,28,156,538,190,22,5,55,23,195,65,44,848,264,51,446,857,709,127,374,781,447,191,344,865,625,821,13,124,96,93,7,578,125,419,907,360,463,32,116,126,37,274,777,88,731,108,67,154,92,95,36,199,12,137,131,10,562,128,326,664,203,382,50,806,242,921,435 }, +{ 15,1,901,180,2,4,5,3,0,752,7,6,341,13,115,317,365,23,217,141,9,197,352,165,51,21,137,11,36,202,12,33,22,873,308,77,269,10,64,86,498,569,31,372,55,93,40,120,160,44,509,318,349,482,638,8,117,37,116,164,102,32,28,401,110,128,196,144,206,244,153,457,468,951,17,291,16,203,96,260,811,143,373,642,125,134,437,386,546,268,247,177,14,48,758,717,719,391,579,72,421,89,661,629,111,730,559,961,221,59,403,523,304,851,35,30,169,854,494,127,82,326,450,251,898,417,475,734 }, +{ 131,39,829,166,613,827,716,578,9,100,224,310,406,30,98,616,228,1,254,856,219,438,938,190,31,846,265,931,0,127,96,625,274,924,44,371,101,385,116,252,272,863,51,137,88,21,333,731,557,36,23,404,48,223,125,49,264,160,187,737,242,105,28,258,144,93,13,530,158,944,382,284,395,203,812,593,518,442,5,456,836,128,326,293,68,329,403,35,202,816,77,115,83,909,688,86,890,478,120,64,22,639,16,165,276,393,455,871,410,201,286,299,33,576,960,656,24,217,95,159,110,32,367,421,492,221,55,102 }, +{ 17,64,62,141,106,136,751,292,129,352,41,38,954,86,476,214,217,128,177,32,237,947,180,162,123,209,710,269,519,247,90,864,157,77,318,99,57,76,59,4,437,40,179,358,11,45,618,119,313,5,304,211,597,148,875,23,249,197,130,95,507,37,54,3,216,457,248,58,210,115,288,12,425,678,186,164,328,72,454,339,245,102,80,498,312,165,372,139,155,2,152,56,33,21,655,243,720,183,207,14,29,241,117,266,290,471,137,84,51,188,305,275,205,449,7,263,48,542,13,270,742,171,752,509,61,317,349,369 }, +{ 42,25,235,650,736,65,605,630,233,343,123,256,122,85,26,416,6,389,63,141,249,368,444,194,511,174,544,19,108,597,348,524,138,90,27,183,32,370,304,525,84,56,53,283,789,445,18,78,280,91,130,947,588,619,45,355,17,680,73,4,0,222,112,3,813,119,145,64,678,297,250,80,21,573,523,86,214,216,102,76,11,746,40,33,269,240,29,289,624,177,323,450,57,24,561,77,152,114,184,74,287,28,396,665,648,378,372,137,37,5,390,106,70,22,847,51,49,349,874,47,881,23,817,432,337,243,153,124 }, +{ 184,229,152,432,266,452,57,497,381,619,17,313,471,257,59,129,369,339,4,12,205,99,157,77,3,5,45,128,123,106,62,202,162,32,21,216,40,136,11,188,119,249,402,349,2,403,14,37,38,54,64,291,618,524,102,475,76,177,80,90,214,33,79,141,453,23,210,111,245,372,137,27,318,269,217,494,775,86,94,139,10,1,19,115,51,401,56,769,183,361,84,529,474,277,757,72,48,880,6,247,29,209,710,179,377,89,41,292,285,328,13,588,180,342,130,556,36,477,308,275,460,378,61,197,196,0,165,421 }, +{ 137,202,160,131,860,567,318,36,295,141,843,39,800,93,30,105,187,224,276,201,261,943,964,98,49,269,219,851,116,9,22,473,1,352,203,127,822,100,206,699,144,268,662,244,253,10,717,535,332,120,44,258,143,31,64,450,704,96,859,190,180,115,55,365,531,326,95,217,28,110,564,752,86,373,284,228,317,21,77,341,13,48,434,221,197,51,911,940,159,23,384,533,260,494,559,935,291,901,88,109,574,282,128,442,367,308,959,617,5,83,489,299,251,728,170,164,165,177,492,406,926,417,293,401,349,871,125,968 }, +{ 125,64,493,0,49,165,35,293,55,93,18,98,9,325,68,10,279,137,31,48,453,389,194,197,147,13,788,14,132,95,107,77,391,371,342,174,61,43,581,105,28,22,5,259,319,104,758,671,590,128,44,32,20,767,761,457,146,121,725,465,198,126,757,289,116,100,23,21,110,763,612,12,663,199,142,120,102,101,7,427,4,356,37,941,963,867,848,498,156,88,86,58,51,33,690,558,376,180,2,864,163,955,70,623,804,422,657,202,1,40,534,90,649,470,798,522,392,352,945,502,386,359,237,221,203,177,159,144 }, +{ 15,33,4,77,13,102,23,40,32,5,11,349,51,177,21,141,117,93,202,59,110,153,64,291,318,196,31,217,269,115,10,180,82,137,1,8,44,3,98,125,134,0,165,22,12,128,351,17,95,116,2,304,120,139,403,457,210,197,49,341,453,144,372,450,494,86,48,352,386,523,36,90,719,898,6,275,30,365,57,730,28,9,317,247,326,854,76,373,475,7,72,37,421,817,638,901,401,111,961,342,899,216,402,277,260,18,237,758,569,203,99,629,437,535,39,61,16,96,752,14,300,208,922,704,105,83,308,143 }, +{ 110,854,253,811,352,141,951,661,244,642,180,752,143,498,384,269,559,317,922,10,911,160,351,533,365,373,206,902,638,873,332,261,345,260,137,717,116,843,31,417,120,44,959,531,217,341,535,201,125,36,699,860,82,55,564,434,926,318,268,940,197,901,164,574,864,276,251,824,295,876,127,450,64,617,943,93,258,776,15,282,326,165,546,817,855,851,203,96,187,144,728,83,1,372,601,219,224,762,237,284,509,21,170,169,499,98,391,479,115,768,482,473,406,86,898,662,349,48,202,131,51,5,0,896,871,401,238,228 }, +{ 2,29,70,75,220,52,112,331,145,1,163,26,74,324,6,69,58,307,19,409,278,287,38,226,46,223,394,87,25,294,606,50,66,14,632,135,583,151,42,67,45,27,396,357,171,380,675,138,5,21,113,267,262,97,24,7,309,764,635,53,537,222,103,48,335,500,83,32,122,16,550,186,96,17,73,334,849,155,189,644,852,802,37,61,54,64,571,140,35,12,582,651,461,486,853,108,36,80,18,133,451,315,624,65,337,785,573,366,445,72,86,774,92,89,297,400,10,114,95,416,587,648,462,172,71,600,820,238 }, +{ 23,13,77,51,115,177,64,141,33,102,4,217,202,40,128,269,291,59,0,180,10,2,6,7,318,11,196,14,137,90,32,22,95,153,352,210,9,1,117,15,165,16,197,49,3,125,86,237,349,18,403,216,19,17,247,21,317,304,342,24,341,134,475,453,12,275,25,36,28,20,98,494,372,57,31,93,65,44,110,27,26,120,139,365,752,111,37,35,326,164,116,402,901,260,457,30,288,421,105,56,498,29,144,391,82,203,5,401,308,285,719,386,99,143,48,39,351,55,194,437,312,450,96,523,76,509,244,316 }, +{ 340,897,691,478,914,658,724,382,363,812,698,682,100,156,166,521,264,447,909,411,1,404,88,354,303,438,96,296,274,772,39,656,418,921,9,30,410,144,23,744,848,639,664,435,21,586,919,13,271,31,335,254,51,5,850,98,367,225,960,77,203,759,621,190,48,127,676,120,32,668,33,83,4,67,44,36,272,217,69,291,131,284,286,360,821,101,285,116,242,18,223,686,102,265,905,842,317,329,93,68,82,822,195,128,125,576,692,351,103,64,238,115,652,475,349,765,326,141,966,578,110,118,0,260,403,201,163,105 }, +{ 310,18,9,326,265,252,121,51,159,77,195,105,101,120,363,33,24,354,271,102,272,709,167,98,16,13,132,639,23,158,260,55,333,327,704,450,35,170,60,202,49,557,125,93,411,254,1,364,95,455,146,817,28,520,338,494,7,286,137,156,109,39,21,14,48,128,116,64,40,31,859,320,118,212,141,360,890,318,293,4,523,615,269,576,169,20,165,153,75,196,329,373,210,177,92,69,0,301,291,781,479,371,217,627,407,403,342,127,71,44,36,910,374,5,50,668,647,351,936,682,578,538,593,553,203,185,180,110 }, +{ 57,313,471,99,369,12,339,157,266,152,38,475,328,453,775,37,403,401,514,40,188,342,139,77,11,59,377,361,229,474,33,4,51,880,277,54,3,211,14,102,62,381,23,111,162,196,130,529,556,305,757,275,128,79,115,629,13,402,421,507,308,485,210,129,123,184,177,494,41,477,153,488,117,542,452,248,300,216,202,236,134,94,670,431,89,64,17,183,440,569,136,555,72,76,734,432,457,543,961,257,215,291,304,10,413,15,205,548,90,56,2,21,46,165,318,247,349,197,86,32,0,283,412,141,36,828,386,82 }, +{ 16,24,35,60,140,71,92,7,14,230,189,108,18,69,46,150,388,246,278,2,172,68,1,451,38,330,135,126,236,309,149,443,50,267,121,565,67,582,21,75,48,462,52,563,594,314,29,399,66,191,877,103,572,55,232,366,97,696,357,380,733,167,83,220,517,5,28,674,74,12,65,0,54,9,118,262,335,146,516,32,37,653,915,22,36,133,251,96,324,87,273,394,64,199,723,223,107,10,486,458,711,13,6,95,163,571,86,726,448,212,641,192,345,70,181,132,23,49,114,550,98,20,271,606,298,633,99,603 }, +{ 206,417,940,959,93,473,728,559,499,203,137,202,8,450,564,31,44,120,269,260,318,141,116,326,244,253,10,127,268,143,22,125,98,817,434,1,373,144,128,615,51,638,23,384,692,523,341,64,922,0,851,13,489,110,352,105,9,951,717,86,28,386,367,403,299,704,642,242,55,551,901,36,958,282,859,160,77,49,291,494,801,968,30,180,492,365,911,661,498,276,854,165,164,873,37,762,456,197,21,317,12,15,5,7,569,285,918,421,752,629,576,479,115,95,2,349,96,401,758,899,393,170,898,217,457,391,48,39 }, +{ 15,1,2,13,0,5,23,7,4,3,6,51,9,217,77,341,115,10,8,11,33,349,12,317,291,180,177,14,102,16,365,120,22,901,304,165,457,21,372,260,197,32,18,40,403,453,31,202,82,961,719,475,28,247,523,117,450,64,93,141,730,110,30,352,351,752,569,44,401,153,17,386,196,59,308,24,134,36,854,494,318,899,48,137,342,326,125,629,90,49,128,437,37,817,95,275,922,421,498,758,98,210,111,203,99,237,391,116,216,57,482,144,373,39,35,20,19,468,139,615,55,221,288,898,873,509,244,86 }, +{ 2,1,14,5,50,48,46,290,21,45,29,32,263,155,207,52,38,425,171,76,408,72,449,17,292,227,89,58,513,400,428,119,476,7,61,54,67,720,75,241,243,186,502,655,69,221,36,148,24,96,6,306,3,214,106,37,0,103,223,10,179,281,16,83,350,64,90,129,430,464,181,663,666,22,413,405,18,240,710,84,262,790,123,431,28,12,741,163,115,460,255,536,56,128,35,334,141,9,220,65,205,51,86,659,99,249,23,541,95,245,618,209,519,202,70,592,270,237,528,62,27,217,133,4,394,137,114,675 }, +{ 132,121,354,167,146,463,271,195,668,446,98,223,407,360,18,212,522,447,1,781,60,48,591,411,156,562,586,363,664,21,777,393,199,293,125,807,936,9,23,590,163,16,13,658,5,126,541,55,101,941,107,259,621,191,51,834,786,68,647,765,31,150,929,759,284,35,71,49,24,465,221,118,520,7,92,883,116,608,279,727,535,70,96,44,687,478,105,50,346,697,467,963,281,33,10,566,22,581,626,93,77,296,493,4,102,108,115,640,147,75,39,770,87,32,914,127,144,598,178,0,117,273,12,2,37,783,691,932 }, +{ 131,578,371,219,224,716,105,616,737,385,187,761,258,916,254,159,49,910,9,623,98,127,836,924,272,286,442,39,28,688,890,320,627,518,293,0,944,871,574,242,31,395,22,201,327,44,116,158,276,109,908,301,860,284,36,51,23,137,95,926,713,512,30,160,125,333,170,239,96,21,456,347,228,13,86,64,128,1,68,101,839,202,506,424,295,662,938,144,55,168,24,567,48,843,406,83,65,185,613,438,203,268,35,165,492,244,299,902,221,384,16,141,115,166,827,93,10,829,37,364,763,261,169,269,5,656,434,685 }, +{ 302,467,484,97,453,6,398,124,69,36,51,14,421,298,1,66,273,87,74,523,81,426,2,403,67,168,18,386,886,756,701,801,158,13,65,0,26,23,475,24,758,86,22,108,719,25,547,615,401,159,117,423,150,730,847,563,154,75,19,646,29,7,505,817,189,278,457,165,42,458,717,572,267,138,782,531,429,153,135,450,134,49,700,208,958,671,715,622,336,244,172,143,114,95,85,63,60,52,650,197,46,899,140,92,160,28,10,791,885,718,327,314,12,5,610,109,43,703,673,670,370,341,326,289,38,35,805,879 }, +{ 190,30,778,530,539,88,100,625,382,1,960,410,731,274,96,840,957,435,321,795,748,264,669,438,466,39,166,131,36,418,144,265,9,44,228,213,23,219,224,921,223,160,406,251,31,863,83,98,310,116,21,613,13,557,203,51,128,0,252,367,335,137,254,5,924,521,284,352,829,48,765,856,258,475,827,69,201,93,49,909,404,716,238,101,616,822,33,421,32,456,593,217,125,127,944,752,67,286,276,679,931,831,329,403,816,158,77,812,221,772,317,639,37,35,28,242,120,268,578,50,230,966,686,494,187,551,478,285 }, +{ 141,202,128,2,269,1,64,5,180,3,318,33,197,6,352,15,7,102,77,0,134,13,137,10,196,153,165,120,117,14,17,11,12,82,110,4,9,237,23,291,260,494,93,51,403,217,21,498,752,450,342,341,31,32,351,22,326,125,402,457,317,143,86,36,115,19,203,40,111,961,285,16,48,475,98,116,44,678,365,95,8,37,57,28,244,27,18,901,49,45,864,569,349,373,386,391,453,59,523,144,719,139,509,25,854,372,38,24,56,898,194,164,947,79,29,817,94,253,99,873,954,730,55,65,597,61,26,546 }, +{ 44,201,567,489,116,662,224,268,131,219,434,295,187,144,31,276,699,384,137,110,442,258,36,160,228,261,367,613,93,30,64,120,908,244,284,203,260,141,202,385,1,9,269,940,39,855,253,918,98,96,373,22,406,856,326,105,206,143,318,10,217,127,55,523,115,77,125,717,617,86,49,13,836,51,28,23,574,332,128,83,190,180,871,839,371,393,944,352,317,21,164,473,935,197,450,341,959,0,177,911,840,254,531,165,291,716,418,899,800,88,829,282,827,494,48,492,661,854,5,351,100,82,299,851,822,15,860,365 }, +{ 51,12,23,15,99,37,13,10,453,165,117,38,0,197,629,569,82,115,401,120,64,134,22,474,457,11,110,719,31,93,260,9,14,475,111,32,153,79,1,94,21,40,308,128,125,8,28,5,18,54,403,351,3,44,180,202,89,450,328,33,961,2,139,36,4,49,77,45,141,523,326,386,730,48,17,217,30,431,148,405,317,237,29,98,16,341,775,413,196,365,352,55,318,72,758,116,6,59,24,7,477,898,734,509,58,39,203,95,372,482,86,854,19,391,421,402,269,144,494,90,102,291,922,817,57,373,899,27 }, +{ 135,7,172,2,92,140,66,52,97,74,324,14,226,29,232,46,38,380,87,262,314,357,278,330,16,298,571,448,35,126,60,220,620,606,75,6,24,192,107,69,246,230,331,1,5,267,294,70,622,353,222,853,550,563,71,572,583,138,150,600,189,500,25,43,50,309,21,526,32,872,12,174,54,58,37,20,893,802,22,48,145,86,64,580,462,95,67,18,888,42,950,65,387,36,171,108,451,112,10,501,26,726,335,441,103,785,83,359,204,582,279,337,461,49,0,72,423,45,443,409,61,68,356,28,392,163,673,114 }, +{ 125,386,391,23,963,60,51,949,165,221,21,13,541,393,118,719,150,197,326,758,421,791,517,801,566,558,308,48,362,7,834,670,453,551,173,401,203,102,43,20,5,71,36,457,251,77,697,279,140,867,33,126,115,32,92,149,422,743,367,232,193,0,770,4,98,509,40,674,81,18,341,11,783,192,577,546,397,16,494,510,508,217,189,104,705,610,330,2,596,105,823,293,202,117,10,314,796,64,862,629,475,468,281,246,242,38,403,29,474,298,31,27,14,858,414,95,942,779,773,569,523,430,317,301,172,134,116,91 }, +{ 85,6,42,138,235,25,222,174,525,256,746,26,289,64,280,214,544,65,32,337,90,204,355,348,74,312,95,415,145,122,233,609,416,66,194,19,29,630,835,63,22,87,45,237,874,5,605,353,112,86,519,343,135,17,648,76,736,70,287,249,180,681,0,288,21,580,27,123,444,4,226,192,52,247,81,1,56,751,216,78,58,75,49,91,792,23,48,53,183,396,108,59,61,28,3,636,624,2,208,84,370,368,12,207,37,114,10,151,153,389,197,172,51,673,72,33,671,597,18,115,125,31,730,573,179,141,73,250 }, +{ 1,14,50,24,5,67,46,2,69,103,18,54,58,48,16,38,83,96,0,35,28,502,21,36,45,181,155,12,61,7,223,513,430,22,270,65,108,9,75,10,37,133,540,29,32,290,71,52,17,171,238,114,218,72,236,55,99,309,807,70,149,328,89,433,163,227,834,68,64,23,189,51,666,6,766,765,221,115,474,86,60,366,552,541,488,13,487,243,425,95,592,536,76,780,141,477,767,186,128,663,267,251,3,794,49,741,137,418,263,202,160,207,545,27,543,920,335,428,388,408,400,150,760,148,124,106,19,269 }, +{ 811,351,642,951,180,752,110,638,253,10,352,854,197,82,365,564,873,341,499,55,143,244,535,36,498,559,901,417,22,661,141,9,28,282,165,30,206,898,373,35,509,260,115,922,728,8,39,332,49,317,31,44,762,964,93,15,911,533,203,160,1,217,120,391,137,24,116,33,105,102,479,943,77,569,717,338,134,117,968,125,965,164,64,473,127,196,153,531,384,269,601,308,98,37,959,23,177,13,867,109,617,291,864,482,169,21,372,242,546,843,434,824,692,40,32,4,551,299,758,5,468,11,629,903,12,89,949,791 }, +{ 51,23,13,453,403,12,475,115,4,15,165,202,523,719,21,457,386,99,308,37,401,2,730,5,197,32,494,0,33,1,120,450,10,59,102,128,17,14,117,3,961,77,153,9,217,569,11,196,134,899,421,40,318,391,247,90,6,629,64,7,16,57,210,48,341,177,141,269,38,260,402,180,22,468,29,27,758,317,291,817,25,19,72,18,56,361,326,133,304,54,221,757,236,34,26,288,300,275,670,365,514,509,125,237,36,65,139,137,108,45,76,52,28,498,351,801,82,805,576,114,89,61,352,41,615,24,8,854 }, +{ 120,13,23,141,77,51,260,217,202,326,1,137,291,269,93,102,82,21,203,450,351,15,180,110,165,318,352,5,128,177,373,48,854,197,64,922,523,317,96,36,349,32,0,386,372,144,33,2,9,125,752,115,59,12,210,7,457,16,817,90,403,244,367,14,898,237,160,28,35,342,719,6,275,10,18,494,304,37,30,341,50,39,24,498,730,365,391,22,473,901,4,86,223,285,247,116,31,704,453,678,83,221,661,216,44,69,61,615,201,421,65,402,8,475,268,401,11,468,49,29,98,88,899,300,242,68,3,418 }, +{ 1,5,22,12,0,2,36,21,28,48,10,14,86,32,54,50,37,7,49,9,23,65,51,55,202,96,95,13,24,208,128,3,137,323,67,17,6,99,4,16,168,18,38,194,83,115,114,281,58,45,64,318,141,745,402,46,269,31,160,480,77,103,503,105,11,403,164,69,35,218,181,72,133,29,822,433,735,430,75,180,223,89,109,584,244,341,197,361,289,165,657,169,401,239,757,177,389,98,124,217,851,238,159,33,158,956,30,59,143,70,102,39,308,44,494,754,40,253,90,155,61,766,163,540,221,76,270,756 }, +{ 219,127,98,258,23,308,301,51,910,401,276,616,284,170,395,293,105,165,22,13,327,242,512,125,201,944,115,900,374,320,109,924,964,763,272,239,567,28,860,629,391,131,116,386,55,197,935,453,843,341,9,943,743,365,506,206,457,261,758,295,36,180,670,31,95,237,538,535,662,44,224,456,185,558,332,64,911,479,0,143,169,569,492,634,10,841,317,421,867,338,282,253,49,144,685,740,384,110,901,468,187,734,719,86,417,844,509,268,12,564,141,286,713,762,918,475,299,120,1,578,164,137,159,403,870,168,551,498 }, +{ 64,180,237,5,80,165,61,58,498,250,297,197,186,2,752,864,34,445,95,869,881,17,311,391,231,115,29,151,546,549,3,312,247,483,153,469,45,27,22,365,334,73,26,136,933,62,56,495,32,52,243,215,21,90,472,592,396,509,288,651,48,738,106,53,490,482,161,211,11,316,287,177,13,369,635,378,141,86,262,667,119,663,216,128,179,40,19,209,50,164,719,457,145,23,10,352,176,241,196,527,666,171,833,269,383,125,758,14,46,70,341,507,317,873,579,464,255,400,37,875,405,210,117,33,143,331,84,6 }, +{ 77,13,102,23,64,33,141,2,115,51,1,0,10,7,177,217,6,269,180,40,3,14,202,128,22,15,318,291,196,5,197,165,153,9,117,352,210,4,237,95,17,134,16,11,125,137,86,18,317,139,12,31,93,59,28,36,49,341,24,275,304,19,349,365,110,44,342,143,403,27,120,29,719,752,457,498,164,372,111,45,203,453,82,25,569,391,8,494,216,144,98,21,247,386,475,26,401,402,30,312,37,99,57,65,20,901,38,678,244,116,326,260,56,308,450,32,35,509,300,34,961,597,194,730,48,629,55,351 }, +{ 15,120,450,51,23,13,202,523,153,196,260,457,719,386,326,4,730,128,318,141,21,11,64,403,453,33,77,32,475,269,95,5,494,125,102,165,48,12,98,1,115,2,401,117,116,10,0,180,40,197,817,7,9,308,99,899,3,352,137,90,16,37,6,18,14,237,22,17,59,217,421,402,134,8,291,49,31,961,82,28,177,670,341,351,342,247,61,758,221,194,19,391,30,317,44,93,214,365,24,57,27,20,275,25,241,216,312,285,29,288,65,576,349,629,70,72,26,39,304,498,36,96,569,123,210,46,289,854 }, +{ 2,5,1,29,45,14,58,207,263,425,52,32,46,76,186,72,292,50,7,61,476,655,408,227,48,38,155,21,720,89,17,75,243,119,6,449,171,148,306,70,241,163,106,334,214,592,54,400,36,179,90,64,790,663,24,666,84,3,16,67,69,240,10,464,56,405,496,128,0,710,74,86,270,129,22,290,460,519,255,209,220,18,141,428,223,123,659,205,96,65,19,513,83,37,4,12,27,25,112,103,115,358,66,23,9,202,95,262,249,221,51,151,28,26,767,59,35,77,350,87,137,780,245,40,409,99,237,618 }, +{ 15,4,13,11,5,23,21,32,1,40,141,3,33,2,180,17,197,64,12,269,31,352,44,117,165,102,202,120,37,318,153,0,6,137,51,10,196,341,134,22,7,49,77,752,59,217,128,48,260,203,125,237,901,82,93,317,115,365,498,90,14,110,342,111,450,95,177,873,72,326,61,8,494,244,457,57,45,19,36,210,139,386,96,144,56,89,509,143,98,76,372,275,719,27,9,160,403,94,351,349,546,854,704,678,817,391,947,253,453,304,99,569,864,308,401,247,29,70,216,758,717,670,475,474,961,116,523,221 }, +{ 214,76,5,129,710,249,618,123,45,460,2,257,17,179,769,205,245,519,207,61,184,106,162,29,209,90,119,32,1,58,6,14,255,292,183,128,77,229,64,21,141,3,655,48,27,38,52,19,263,57,59,4,40,497,151,25,202,152,464,72,7,26,80,99,102,306,311,269,33,115,288,130,318,524,86,54,177,790,62,358,50,243,452,137,46,10,84,186,454,0,425,51,12,148,619,11,210,56,23,556,22,240,112,266,402,36,217,216,37,432,476,136,227,65,70,403,738,155,237,95,111,194,381,139,241,317,180,13 }, +{ 51,23,141,1,202,13,120,21,326,15,269,137,180,165,318,197,352,5,128,48,260,2,77,0,217,12,33,102,7,403,450,203,93,9,32,110,386,115,16,14,64,6,36,4,82,523,373,457,125,18,494,237,291,351,24,342,196,96,37,453,475,817,3,752,391,177,244,615,50,144,28,854,730,210,35,719,99,30,8,317,341,401,160,31,10,44,901,86,221,98,421,22,349,402,576,365,116,922,153,308,39,704,40,72,20,285,17,59,498,372,367,393,661,49,899,67,95,551,70,55,90,275,29,247,89,83,65,69 }, +{ 7,135,2,172,97,87,66,29,52,14,112,70,58,232,337,92,46,226,74,43,262,192,331,461,20,409,16,307,5,294,107,155,334,186,171,278,140,38,151,32,849,324,415,35,75,21,25,45,609,24,227,6,145,1,298,222,60,448,61,64,620,353,387,126,37,86,69,287,549,356,173,48,400,330,644,10,314,583,18,12,22,113,220,526,54,128,359,49,853,36,95,0,76,525,163,67,568,50,65,872,28,104,72,243,138,90,137,587,71,357,4,141,17,246,544,26,230,888,177,194,380,122,474,311,315,635,83,631 }, +{ 574,187,926,860,384,258,531,434,269,110,160,244,141,253,116,871,533,940,261,717,699,959,902,219,661,224,201,373,143,36,911,352,137,260,268,31,851,617,817,728,10,318,523,180,206,295,332,144,811,489,44,843,922,64,251,662,816,120,450,55,127,93,276,855,535,839,98,131,752,951,284,559,824,164,406,642,854,203,1,864,202,317,237,228,365,345,776,498,326,567,442,197,125,282,341,367,96,21,217,417,13,943,86,351,83,679,115,908,564,954,473,82,800,896,165,968,23,128,35,669,48,5,22,170,901,873,169,385 }, +{ 354,156,296,586,1,98,850,163,223,698,379,291,64,44,31,937,48,340,96,349,182,125,237,120,658,77,284,39,30,367,23,385,221,195,293,18,202,144,105,774,128,771,687,541,342,197,116,49,371,121,118,93,5,626,360,16,692,591,453,421,386,352,318,242,153,117,36,10,795,759,260,141,463,372,326,102,87,33,697,11,4,931,446,13,35,948,103,958,899,827,781,752,664,638,558,551,497,475,457,415,304,269,217,205,201,179,166,140,131,127,126,113,107,100,92,89,88,72,55,37,24,883,203,70,21,7,914,846 }, +{ 250,34,472,80,495,483,194,469,141,176,17,64,53,161,33,814,4,667,527,73,27,231,84,56,833,269,14,102,297,585,283,503,59,597,119,106,77,862,255,128,789,11,90,153,209,389,95,887,57,76,3,240,58,23,678,61,236,40,202,134,113,117,151,51,123,304,342,136,369,137,22,237,86,65,45,32,62,445,412,311,200,573,129,436,803,328,19,10,611,196,177,133,491,214,514,72,36,947,26,5,52,139,402,378,318,504,114,0,216,180,287,322,13,381,396,358,629,475,285,46,28,2,383,179,130,112,738,247 }, +{ 97,140,66,172,7,192,314,81,92,426,74,232,298,462,501,43,508,330,69,135,279,6,230,189,24,48,21,885,104,14,448,861,38,712,126,173,715,572,60,563,246,267,16,91,633,423,71,526,52,36,357,29,362,387,18,204,63,443,32,568,324,681,879,441,138,107,67,22,20,0,150,517,85,636,607,12,825,5,893,2,174,278,65,37,149,28,726,86,87,35,779,380,560,42,125,565,95,750,83,124,46,1,49,370,251,653,77,64,877,25,4,10,221,54,33,61,672,40,23,451,98,102,31,9,51,120,348,915 }, +{ 44,31,116,144,268,434,489,367,918,492,384,948,393,127,98,9,940,22,206,105,203,0,855,332,49,93,395,959,28,456,617,911,293,137,1,299,39,125,282,202,51,141,473,55,963,120,417,326,23,95,159,21,599,109,276,730,242,13,36,10,131,8,272,284,253,551,219,30,318,523,2,352,165,512,403,269,18,64,5,7,899,244,16,301,616,450,158,115,12,170,559,935,160,924,143,260,791,386,86,692,201,421,128,457,24,719,728,499,373,37,261,916,110,197,578,48,951,239,286,576,811,494,308,35,96,564,14,498 }, +{ 202,128,141,3,51,64,15,269,5,120,33,2,102,13,403,318,1,23,196,197,153,10,165,17,0,137,82,6,110,117,180,260,453,93,352,12,475,14,125,7,351,450,494,22,11,523,457,402,31,326,77,342,86,99,386,237,9,32,36,21,19,4,134,44,27,203,18,730,37,115,8,373,95,817,421,719,40,291,139,401,49,143,341,98,285,57,111,116,25,961,498,28,391,38,854,317,144,26,752,56,16,217,308,65,277,899,365,898,509,59,45,79,922,48,72,30,41,678,757,569,24,177,615,34,29,864,873,244 }, +{ 127,98,31,105,395,293,28,9,512,299,22,44,0,49,763,599,242,685,109,284,320,159,276,95,1,116,456,870,55,286,900,125,492,36,713,506,158,51,23,935,393,24,327,16,35,239,623,144,201,13,2,65,272,21,39,7,338,12,168,141,18,202,424,5,268,14,170,347,137,86,489,841,68,367,918,301,269,64,374,185,124,165,48,567,479,221,403,662,37,963,308,131,30,401,6,318,67,616,948,115,83,197,237,434,160,295,96,169,128,421,203,154,10,143,538,194,916,352,101,371,253,928,910,261,180,164,244,737 }, +{ 7,24,97,124,2,69,66,1,14,92,18,6,140,67,20,71,43,16,21,118,29,23,149,52,150,51,32,81,5,429,189,48,36,484,172,273,38,60,65,28,22,25,74,83,46,91,13,0,298,314,12,420,610,154,42,108,398,701,467,135,35,505,173,221,576,104,421,168,336,423,37,232,403,302,143,185,847,267,137,115,10,703,158,251,308,508,330,75,68,49,86,61,26,253,202,230,107,401,64,799,388,128,99,443,102,72,607,165,426,453,570,387,547,326,105,125,85,109,517,386,95,138,615,87,126,192,671,63 }, +{ 1,349,64,303,284,442,622,202,691,141,652,447,682,67,717,917,340,237,154,352,269,919,752,547,429,411,197,293,260,44,335,180,160,863,238,201,191,167,318,9,751,730,98,453,33,418,956,475,360,69,77,765,217,140,96,31,153,120,87,724,664,389,323,268,206,116,922,888,102,948,854,842,805,196,127,399,586,658,873,851,850,796,781,695,657,385,330,244,149,137,0,662,367,351,82,947,774,551,498,363,225,146,125,35,212,909,899,848,807,776,772,770,759,726,604,576,541,523,480,450,403,393,372,332,329,327,301,296 }, +{ 1,2,141,15,7,10,180,3,5,6,64,0,13,197,77,269,33,11,352,165,9,102,14,4,349,12,291,341,23,237,134,752,22,120,117,202,498,128,36,217,318,115,51,17,317,40,153,93,365,110,342,961,82,31,864,901,125,137,59,143,196,372,16,86,21,260,111,44,873,32,49,947,457,95,57,244,326,8,351,37,28,569,954,203,19,373,678,18,139,450,29,30,48,386,72,27,79,45,854,391,144,509,55,546,94,99,164,98,116,20,76,83,253,56,898,922,38,25,24,39,61,177,210,719,58,523,494,402 }, +{ 139,99,12,453,196,277,775,475,40,401,215,33,51,111,130,23,11,77,313,211,14,38,266,129,474,477,37,328,339,369,153,719,162,152,54,231,3,308,57,700,117,403,305,188,629,102,880,457,569,183,62,514,229,961,157,556,555,115,176,41,177,471,248,13,507,670,342,4,542,257,17,123,291,236,72,79,304,134,452,89,34,184,90,2,59,76,383,488,94,421,80,32,361,128,440,377,202,119,56,316,21,494,46,165,757,288,45,543,197,528,730,585,249,431,136,5,0,432,52,210,402,120,485,29,1,529,548,536 }, +{ 77,102,33,141,128,202,23,64,51,59,269,153,4,40,115,177,13,196,318,117,291,210,0,137,134,403,22,90,217,342,10,95,180,494,18,49,11,1,475,65,32,6,16,352,216,36,15,139,86,7,194,24,402,275,14,28,9,2,21,19,125,453,165,20,25,120,57,237,3,260,26,341,317,114,349,197,326,214,48,31,12,5,82,17,300,111,421,308,365,523,450,98,110,93,247,304,351,457,285,498,39,27,312,123,44,152,99,401,37,615,373,108,372,170,143,116,68,67,56,96,719,678,361,29,961,205,35,133 }, +{ 7,14,2,58,46,16,70,112,87,38,52,135,5,155,29,66,97,294,172,32,20,334,24,92,186,35,128,227,18,171,6,21,107,37,64,75,780,666,400,61,140,43,60,48,220,232,0,23,141,177,1,54,202,307,13,592,461,115,22,77,12,45,50,51,126,163,137,409,10,315,192,270,25,845,69,90,269,76,72,513,28,113,74,210,49,262,243,9,95,89,71,318,402,631,86,884,298,40,431,102,4,275,290,278,17,324,217,19,180,151,36,237,230,67,356,291,352,314,65,403,59,33,26,609,387,226,285,330 }, +{ 15,13,1,5,2,23,3,0,365,10,7,4,341,51,11,6,9,115,180,317,14,33,77,12,217,102,40,197,21,153,165,17,64,8,117,141,569,22,901,134,32,16,202,352,37,36,457,291,128,31,269,125,237,18,139,196,44,453,391,177,372,28,349,59,719,93,482,629,86,509,961,318,873,498,99,55,752,386,19,401,210,143,48,137,120,342,50,38,49,29,27,403,24,30,758,111,110,57,475,95,203,730,46,98,260,116,90,45,523,494,326,54,105,72,96,65,546,402,351,242,277,164,67,450,734,253,83,26 }, +{ 2,1,75,29,52,14,69,220,67,262,46,38,70,87,103,6,74,24,83,267,25,226,357,366,394,96,26,189,451,112,606,486,135,145,388,324,294,108,335,278,50,66,42,238,163,58,16,331,150,71,19,149,545,222,35,236,18,418,696,133,114,7,218,582,5,36,45,433,516,537,60,380,171,138,641,600,65,550,251,151,21,54,309,603,853,223,181,12,27,287,28,0,739,565,22,17,785,48,86,68,140,461,594,37,97,10,32,423,458,95,160,733,337,583,61,122,307,281,336,64,571,186,273,85,55,774,632,172 }, +{ 9,105,18,101,39,557,16,890,1,252,272,100,265,938,21,326,23,51,301,49,0,68,24,60,264,13,35,310,254,159,737,363,167,593,137,127,121,166,98,846,118,827,156,48,202,30,333,31,55,5,411,395,128,329,132,146,578,44,520,512,242,77,731,682,170,320,371,120,71,916,518,450,373,28,821,286,33,447,327,212,354,271,32,195,639,125,7,364,95,704,102,65,616,385,615,36,688,203,64,12,110,523,455,96,141,197,75,910,4,116,836,506,88,165,93,604,404,22,318,931,2,10,260,86,338,817,40,905 }, +{ 80,209,667,61,45,17,106,483,255,6,833,151,454,29,5,738,378,2,887,742,112,311,27,119,26,469,495,113,491,161,58,145,396,19,25,84,73,70,315,207,814,76,56,136,42,64,32,1,243,62,52,306,614,287,128,445,14,74,383,53,631,3,90,240,34,141,179,86,297,4,72,148,496,65,250,611,214,138,527,46,194,123,137,186,21,655,95,66,549,50,881,292,592,790,48,87,7,11,129,263,205,122,22,440,361,59,222,573,307,38,202,227,75,269,57,85,10,358,245,644,684,54,200,862,651,425,36,171 }, +{ 23,13,51,5,21,115,12,202,15,1,141,32,2,165,0,269,180,197,401,7,4,3,9,457,308,403,453,6,341,37,318,10,719,14,48,120,128,352,33,77,22,137,11,730,217,523,40,386,99,16,260,31,475,569,18,64,450,317,28,237,391,90,17,494,421,365,177,59,125,8,102,72,93,30,629,247,961,24,221,117,44,153,498,758,351,291,196,509,899,402,817,49,304,134,76,89,210,98,326,82,482,45,57,110,38,36,752,901,39,139,468,20,96,372,116,216,19,349,203,288,29,61,95,275,546,670,27,576 }, +{ 99,12,453,277,139,474,369,339,51,38,157,196,37,775,401,188,23,475,111,328,11,313,471,153,40,629,266,79,54,4,477,33,102,59,14,57,248,880,152,342,13,457,305,542,117,377,403,514,94,308,115,229,569,488,961,210,300,211,431,41,89,734,361,485,275,670,134,128,184,381,3,72,529,2,556,719,402,64,413,21,15,215,46,177,757,165,77,197,236,32,62,421,155,50,130,17,162,202,5,45,587,52,507,10,183,555,82,247,490,129,588,123,405,148,216,76,141,7,0,1,181,29,432,6,730,90,468,452 }, +{ 7,92,232,126,97,298,387,140,107,16,60,246,14,66,35,462,150,441,104,357,314,359,279,330,633,423,380,172,230,199,24,121,38,392,356,71,132,192,451,324,22,146,893,508,493,568,289,65,560,95,0,5,32,74,189,830,2,135,21,173,18,603,64,46,43,707,20,501,52,362,90,214,712,550,877,86,607,28,426,312,48,36,29,10,69,278,12,194,49,37,1,6,672,874,204,81,267,68,526,108,835,259,733,895,388,500,779,649,448,915,61,125,149,443,50,55,54,399,590,70,72,939,572,191,124,711,517,167 }, +{ 13,23,318,202,77,177,51,269,33,102,141,115,494,4,90,403,210,153,40,196,117,32,59,128,11,137,342,134,217,291,64,21,12,475,352,31,341,14,2,98,93,10,7,95,700,49,317,275,216,20,19,16,44,22,365,116,6,453,110,247,144,180,96,9,24,17,5,82,0,498,402,349,300,127,48,3,125,57,111,87,28,86,1,203,197,35,25,401,18,165,351,326,139,60,43,26,36,450,873,864,551,473,393,373,372,288,678,576,285,37,523,120,70,65,817,67,752,697,529,421,415,386,304,214,164,152,123,61 }, +{ 13,77,2,23,141,6,0,1,217,115,7,3,180,5,10,51,33,14,197,269,352,64,102,177,9,291,15,4,317,40,17,165,22,11,341,202,318,237,196,117,153,349,16,365,28,210,372,752,134,19,18,128,304,498,36,247,12,125,59,139,95,8,275,457,86,31,27,49,21,24,29,391,342,569,509,25,93,45,143,901,137,32,244,403,26,57,38,719,111,873,386,120,44,864,20,37,546,56,494,39,961,164,758,401,30,453,110,475,35,730,98,65,216,437,34,203,41,326,99,402,300,253,82,90,55,46,194,351 }, +{ 15,4,110,13,120,93,11,23,5,21,32,1,40,180,10,82,165,2,3,33,197,141,51,37,0,48,901,102,12,31,6,7,125,117,352,77,752,341,144,153,351,196,9,237,64,134,498,143,17,269,457,342,59,202,115,98,365,450,318,22,44,401,291,873,8,244,217,475,57,391,253,14,72,349,854,372,55,36,509,89,898,403,90,719,19,386,697,139,111,546,661,27,260,61,56,177,96,569,482,961,16,638,332,758,494,28,864,148,76,45,241,79,350,308,247,437,421,70,474,559,221,210,41,164,160,137,453,275 }, +{ 15,1,13,2,0,23,4,5,3,7,6,51,77,9,341,10,115,291,11,33,217,8,12,102,177,365,317,14,165,16,22,120,349,40,180,197,31,457,901,453,18,21,450,153,961,141,719,260,523,32,44,82,64,117,304,28,93,372,110,17,210,475,59,30,134,352,386,342,569,401,752,275,196,730,90,403,326,202,36,49,629,125,817,216,98,308,247,269,351,24,203,494,899,37,95,111,139,482,758,373,391,19,48,116,237,922,509,57,144,86,137,498,854,421,128,670,873,898,253,437,65,300,55,99,20,704,244,615 }, +{ 20,43,198,325,904,104,173,147,234,422,66,97,426,319,861,77,376,712,416,725,427,945,0,7,5,470,450,192,142,32,707,178,330,819,639,38,202,22,500,21,784,583,414,2,324,230,858,61,889,49,315,113,671,81,455,18,392,74,207,232,52,24,329,107,342,33,560,13,140,595,10,715,291,120,102,95,48,1,64,359,693,600,879,16,798,58,312,494,4,14,344,835,764,306,105,526,290,40,23,289,259,90,159,158,884,577,246,37,9,170,204,681,138,29,239,194,112,12,17,31,318,72,28,226,70,397,153,65 }, +{ 105,28,0,9,49,301,159,127,512,16,170,623,24,109,35,395,158,98,68,1,65,22,55,31,761,371,713,916,18,293,286,7,242,124,347,95,338,737,327,44,39,21,2,14,272,424,154,36,506,685,23,168,101,125,299,51,12,479,48,13,870,763,374,284,64,385,320,599,5,67,202,71,276,116,131,141,185,137,836,456,175,344,239,37,900,187,30,10,577,538,6,616,289,740,60,518,118,86,169,83,364,912,219,96,50,115,928,128,935,165,318,149,747,221,346,32,114,69,810,254,237,627,269,308,144,393,201,492 }, +{ 213,466,689,88,230,435,321,30,699,795,201,831,352,144,217,443,854,530,840,190,539,957,418,1,96,203,382,855,251,100,669,410,335,93,83,748,367,44,662,960,31,934,36,206,141,752,351,317,822,128,731,116,778,120,284,274,131,98,228,927,332,187,69,438,521,800,21,242,160,224,48,372,219,310,137,735,686,557,326,285,237,576,127,922,5,859,269,260,13,494,264,82,523,403,35,244,202,615,166,39,616,551,345,930,180,67,253,921,299,966,2,10,9,258,23,473,365,692,265,197,866,32,276,51,816,617,856,223 }, +{ 130,453,196,47,57,4,236,14,59,412,51,711,381,730,153,77,202,108,128,361,23,46,283,402,210,11,300,719,13,117,38,803,200,401,183,78,34,475,54,342,318,27,369,432,275,2,757,133,503,472,12,3,585,134,62,862,161,529,37,328,457,56,390,494,102,73,176,50,440,322,111,670,65,52,377,899,181,629,17,114,33,136,99,266,123,40,514,339,291,53,250,84,177,21,188,216,389,217,386,139,32,961,155,480,162,754,832,714,894,739,383,657,269,152,106,603,543,527,523,497,277,485,838,137,86,64,48,758 }, +{ 1,2,14,50,5,38,46,36,22,67,103,0,114,65,29,12,28,218,54,7,133,83,86,96,69,21,75,208,24,6,52,168,194,389,10,128,51,160,55,17,23,48,18,181,238,45,37,32,95,16,283,532,58,137,503,202,49,124,171,361,480,13,735,9,289,109,782,285,3,115,70,756,141,169,484,584,35,244,717,64,402,4,328,220,163,646,158,803,505,19,253,789,164,185,108,25,155,487,236,269,143,946,394,318,610,401,335,105,433,403,851,87,800,223,866,309,420,61,27,180,597,99,366,308,59,754,514,418 }, +{ 6,74,26,19,165,453,391,197,14,138,730,29,357,70,51,457,1,50,108,125,500,509,27,145,386,112,366,52,46,235,133,262,13,308,47,331,2,719,78,42,309,75,603,25,223,394,785,23,600,218,38,163,115,114,17,558,34,53,220,128,487,58,236,122,65,130,766,401,498,96,409,307,45,85,66,444,899,73,516,632,64,711,54,630,412,196,5,103,21,180,894,113,221,137,624,241,237,594,773,758,867,629,433,390,475,820,546,12,648,67,32,153,285,606,99,545,745,569,644,324,583,3,181,675,903,283,802,670 }, +{ 10,9,30,376,0,68,55,39,18,43,20,49,174,104,88,501,120,342,450,858,28,2,137,523,470,453,197,757,603,37,414,165,31,325,730,729,671,595,455,222,204,77,64,58,22,21,17,13,5,194,110,95,389,318,260,177,128,102,97,33,1,904,725,7,788,568,387,90,54,46,32,14,11,259,98,147,61,817,392,518,961,958,922,899,873,854,819,805,785,739,719,713,661,615,550,534,528,494,477,475,474,465,457,449,386,372,355,351,349,337,298,294,291,289,269,262,247,196,173,171,155,135,122,105,94,56,48,36 }, +{ 15,23,13,1,202,0,51,77,5,9,341,141,137,120,4,260,115,2,351,21,128,7,12,450,82,901,197,180,269,318,10,8,11,22,403,32,854,33,165,40,6,18,3,16,37,494,523,457,102,177,28,64,352,453,14,317,922,217,36,365,48,421,401,719,475,291,30,110,59,24,569,817,93,961,308,730,17,752,125,117,898,31,342,153,196,285,44,99,576,86,275,642,49,90,402,39,143,139,98,221,386,244,116,373,55,57,629,144,210,661,349,134,203,859,899,95,498,35,482,372,72,237,253,105,216,96,111,758 }, +{ 272,131,105,578,219,159,371,49,616,716,9,224,320,286,737,623,187,910,385,28,258,98,761,127,254,347,916,424,22,109,627,158,713,95,924,239,0,242,836,39,201,31,301,912,944,442,125,170,116,327,293,44,538,688,374,23,55,168,276,36,64,284,51,871,185,395,35,506,221,512,518,202,890,137,101,13,860,169,68,574,479,86,908,10,24,333,16,1,65,144,269,364,308,228,344,141,21,338,567,30,577,165,128,160,747,18,763,124,839,926,299,261,456,268,295,164,662,406,197,67,312,175,7,928,421,685,154,810 }, +{ 457,51,13,23,961,719,12,453,629,99,401,57,515,3,4,165,11,45,62,41,14,17,2,569,37,77,33,32,5,0,40,59,1,308,76,115,197,38,72,79,730,21,474,102,7,475,6,54,9,64,403,10,56,775,237,477,202,29,152,188,341,328,90,509,111,89,215,128,880,734,339,247,757,134,386,84,421,317,313,217,129,94,22,318,157,34,130,117,361,210,670,555,369,365,285,269,266,186,181,153,123,24,899,485,120,542,25,391,758,514,468,277,270,243,227,196,180,179,155,148,137,52,50,49,46,18,471,236 }, +{ 202,318,5,120,450,494,77,33,102,260,21,1,128,13,165,403,523,23,269,342,12,326,817,32,475,141,402,64,2,17,82,457,180,137,48,730,125,291,197,453,37,93,51,615,922,719,386,210,15,177,854,351,98,7,899,576,275,6,44,25,961,4,196,117,115,285,95,14,217,31,391,352,237,65,38,29,27,20,758,40,10,9,153,194,367,116,110,105,50,49,35,16,139,498,341,61,163,39,30,11,349,293,216,144,108,99,76,58,54,43,0,203,3,134,113,70,959,958,940,933,923,900,855,841,750,725,704,639 }, +{ 141,269,352,180,217,202,349,64,160,137,317,318,372,494,752,418,244,954,864,437,753,237,13,341,291,165,717,22,197,115,304,260,365,93,36,21,351,247,120,922,77,82,48,498,32,5,579,144,10,23,342,373,33,854,95,128,0,391,164,143,125,98,482,51,403,28,326,898,386,76,72,1,951,678,638,453,201,86,110,30,457,312,253,221,163,96,44,475,102,901,719,421,196,134,190,166,131,100,88,474,401,203,859,597,521,509,466,459,435,393,321,284,228,108,65,59,40,4,851,576,350,308,288,241,216,214,179,99 }, +{ 70,145,29,112,74,26,2,75,287,52,220,226,632,87,294,6,135,307,163,66,19,396,278,138,635,409,25,151,58,113,1,122,331,222,5,97,42,644,324,461,27,849,500,69,651,45,7,172,262,853,337,53,648,573,624,64,334,445,171,21,67,186,32,73,297,46,38,416,14,17,140,80,61,50,436,155,884,223,24,22,85,48,380,852,92,65,86,889,12,315,36,774,571,103,549,37,10,95,243,491,83,16,72,665,227,89,357,394,76,309,35,106,54,675,808,267,474,250,609,71,881,209,133,606,96,232,583,400 }, +{ 351,82,317,141,752,898,901,180,352,341,15,365,1,498,217,115,33,253,32,143,10,160,55,0,854,5,197,269,21,77,4,36,137,23,64,2,110,482,169,873,120,244,96,7,89,13,349,86,164,102,72,93,372,148,922,40,16,345,90,531,9,83,437,22,251,35,260,457,546,165,202,6,864,48,291,51,28,719,59,238,18,308,247,128,11,304,117,12,569,237,65,961,817,509,450,535,196,95,125,730,523,468,144,98,49,3,899,661,342,318,373,153,579,734,14,490,116,203,134,559,391,312,130,123,109,84,76,326 }, +{ 39,9,310,254,101,252,265,272,100,0,557,30,49,688,455,105,190,166,329,627,159,286,625,709,327,333,18,68,158,404,520,578,131,438,731,16,553,24,98,364,31,676,35,118,593,1,518,48,344,44,167,23,65,21,93,604,829,795,127,60,890,96,88,616,22,363,656,51,242,28,419,716,125,124,846,71,274,36,264,613,13,137,95,116,212,938,109,224,827,411,154,910,160,639,371,664,128,5,374,144,530,395,228,170,219,865,67,10,203,759,121,83,301,382,320,737,55,682,37,647,86,271,793,7,821,115,916,285 }, +{ 2,58,29,151,52,186,70,5,549,75,1,112,45,400,61,163,113,227,307,315,311,287,220,46,7,14,155,74,243,87,409,66,27,80,209,631,845,780,69,262,135,48,50,171,767,496,869,454,331,6,491,17,334,278,106,32,19,64,614,21,73,145,26,294,24,592,67,25,38,659,223,16,97,255,172,72,226,502,852,128,37,140,721,666,89,667,290,36,103,324,86,849,267,76,141,54,35,83,65,207,22,148,96,270,12,635,10,4,95,92,738,474,18,445,119,611,425,137,431,263,114,396,663,0,122,133,90,237 }, +{ 141,217,269,352,317,180,23,13,21,115,77,349,202,137,51,64,372,165,197,32,365,752,318,291,341,5,237,102,15,177,498,33,48,244,304,128,125,4,864,253,39,30,44,11,247,22,9,0,31,901,308,403,1,717,201,342,49,10,98,16,7,221,95,144,40,88,494,90,12,28,117,93,678,120,68,326,100,203,86,96,468,2,475,206,116,546,457,160,437,8,873,210,127,110,76,65,55,37,418,386,350,72,18,453,223,143,24,153,851,351,260,284,579,576,401,268,482,196,35,421,82,3,402,166,101,954,391,83 }, +{ 68,35,101,149,124,154,0,65,9,175,24,398,28,16,189,71,118,429,55,346,67,114,7,345,18,230,399,364,133,14,252,722,39,283,1,547,140,69,108,484,302,694,100,439,103,46,705,83,166,267,389,2,458,92,336,50,212,289,21,246,375,251,419,109,36,520,329,158,22,637,12,60,952,48,265,236,702,596,96,806,30,732,5,826,604,64,194,49,412,701,388,88,420,191,535,264,562,167,886,23,796,168,51,650,238,570,782,868,86,13,218,95,917,768,38,10,32,309,404,706,37,54,221,602,125,159,105,676 }, +{ 66,7,29,112,52,97,2,192,20,151,74,43,145,173,222,135,226,525,415,172,644,104,337,544,353,580,32,45,25,287,186,609,58,70,204,155,14,5,21,712,122,87,227,174,331,61,849,549,426,90,355,81,334,560,6,232,416,243,632,46,0,92,324,177,64,113,673,624,107,59,37,635,216,140,396,138,22,500,16,38,4,707,128,65,48,307,17,77,86,137,91,35,12,721,665,789,141,24,294,884,85,42,414,194,115,526,72,311,825,11,126,262,597,76,89,10,28,75,1,387,209,60,304,171,51,147,651,40 }, +{ 31,125,44,299,116,22,170,55,242,456,599,685,36,870,64,393,492,268,1,479,164,86,558,28,506,144,10,740,23,206,903,9,2,0,963,237,127,546,301,13,434,143,367,5,169,93,51,239,12,654,965,384,137,498,7,923,203,918,489,98,180,95,391,141,762,115,352,417,638,841,253,6,105,14,951,8,940,165,197,844,128,948,457,401,959,244,160,308,282,185,21,312,109,286,202,332,341,49,168,386,752,269,538,365,4,37,24,512,911,473,873,509,551,634,499,318,719,3,208,395,708,217,450,421,96,901,35,16 }, +{ 188,11,79,12,99,377,542,94,339,33,474,37,40,139,111,102,453,4,51,775,475,277,13,471,23,196,629,153,431,401,117,210,134,266,152,403,115,38,961,128,59,300,57,77,880,342,157,485,313,569,3,229,202,14,177,275,477,54,308,184,488,328,21,89,72,670,32,64,2,76,10,402,15,90,165,369,148,141,5,162,318,405,17,269,257,205,216,413,757,129,62,361,0,494,734,41,421,179,45,452,197,217,291,120,82,22,130,386,123,1,50,237,358,125,381,497,498,6,137,285,36,180,48,482,372,29,46,341 }, +{ 658,698,340,303,31,363,919,98,44,156,296,850,586,668,354,87,914,120,163,30,127,70,23,385,51,48,1,478,125,49,77,774,223,260,291,82,691,349,13,854,225,217,116,39,10,724,21,922,898,447,351,293,201,75,9,269,284,759,541,480,281,268,221,165,621,141,442,622,411,371,318,187,153,115,105,102,65,777,93,64,33,652,352,202,744,453,433,398,330,191,180,177,137,128,117,95,69,35,931,638,40,5,888,372,146,140,100,88,565,457,450,335,326,317,219,190,159,158,134,36,32,22,518,482,401,394,276,947 }, +{ 15,82,4,197,13,33,1,23,901,21,5,11,10,165,32,0,120,180,2,40,3,51,110,77,642,7,8,351,326,115,6,386,365,217,457,9,117,401,12,37,102,260,523,134,719,758,569,22,36,59,317,730,31,57,342,898,961,450,453,153,17,141,291,137,475,55,177,48,116,90,196,93,44,403,14,64,352,629,54,372,817,28,143,18,269,139,16,958,96,202,494,349,421,275,752,210,128,49,94,86,244,29,498,899,801,318,851,38,670,72,615,474,304,148,41,89,25,19,308,482,160,373,253,70,61,30,277,125 }, +{ 15,4,11,13,23,5,21,180,141,32,202,269,3,40,1,352,318,17,12,64,33,165,197,31,2,0,51,120,128,117,326,59,457,10,153,196,134,817,6,48,125,93,237,82,44,137,450,77,494,403,49,203,386,752,37,523,7,22,961,217,260,8,115,110,341,57,177,498,719,90,615,9,317,102,365,139,14,730,901,210,98,402,72,453,19,116,65,18,111,391,45,56,95,351,27,899,99,349,293,291,300,475,86,28,36,401,25,55,144,244,275,509,247,216,38,678,76,61,393,29,372,576,485,373,30,253,551,468 }, +{ 91,233,370,718,85,6,81,25,65,256,63,235,42,138,511,397,123,26,343,74,650,87,249,643,355,544,18,64,194,510,636,45,174,280,90,22,108,348,681,104,1,84,86,114,389,4,415,66,19,0,673,283,214,141,122,524,289,56,53,193,208,95,240,750,297,29,119,605,27,573,445,436,736,10,14,11,287,597,32,630,49,24,192,57,5,17,204,475,103,61,102,70,947,152,51,588,23,16,183,77,619,730,523,453,444,59,561,2,813,701,874,323,75,33,28,804,129,96,403,368,21,9,145,177,396,255,693,611 }, +{ 23,51,202,13,21,128,318,141,403,120,450,137,48,494,269,5,1,165,125,64,32,260,15,523,102,475,115,12,14,457,31,33,197,77,817,44,98,453,2,308,95,49,99,16,7,180,18,93,326,37,221,6,4,352,421,401,116,291,61,386,203,82,59,36,9,40,30,24,719,117,110,20,217,153,730,17,615,351,402,237,50,854,10,196,39,19,342,3,922,25,96,341,35,28,22,11,105,177,90,72,58,38,317,247,54,961,805,576,207,68,57,46,349,210,134,86,704,75,70,899,373,65,901,670,0,391,89,45 }, +{ 365,180,317,752,341,217,115,352,901,482,372,498,291,15,253,244,141,13,1,579,269,349,7,5,21,4,318,23,351,82,0,2,51,77,93,125,10,898,89,160,202,44,6,854,165,3,96,308,221,148,137,332,247,197,33,11,304,851,531,144,120,494,40,717,468,251,403,72,177,391,32,12,546,453,421,260,203,437,961,574,258,533,48,509,55,922,880,661,615,373,261,134,241,873,475,345,949,867,734,728,699,569,490,473,434,295,288,237,219,143,127,110,27,17,16,187,153,117,876,350,99,36,9,947,128,86,64,679 }, +{ 115,498,901,197,341,468,13,352,482,546,165,509,237,569,9,873,64,23,365,22,180,95,391,317,758,558,49,752,734,109,28,105,217,51,844,86,837,143,629,131,254,876,12,159,158,719,39,406,308,148,141,679,117,490,83,637,457,10,708,166,36,706,102,89,35,505,168,442,372,125,55,530,221,37,208,669,535,332,272,153,103,98,903,401,261,219,768,114,33,864,364,187,169,345,4,574,434,258,242,224,67,30,3,253,961,868,164,228,816,77,843,120,438,384,351,230,134,128,100,99,2,753,50,44,31,584,347,170 }, +{ 23,13,51,202,141,21,180,165,5,197,125,1,115,269,32,120,12,128,318,64,15,137,523,2,33,386,7,260,14,403,48,457,102,4,450,6,93,99,82,401,719,9,730,110,16,31,237,77,326,308,421,98,453,352,37,221,475,90,44,117,30,0,18,144,899,153,10,817,3,203,391,196,11,59,351,40,217,494,177,17,341,134,116,39,8,24,95,752,498,402,20,509,615,61,28,96,19,247,961,291,36,72,365,86,317,285,241,76,49,373,38,29,25,342,70,304,58,57,242,546,576,22,350,349,901,629,801,551 }, +{ 214,289,90,174,874,280,355,138,65,85,64,6,751,81,312,194,249,835,233,348,636,875,91,343,288,519,32,0,524,247,415,104,29,370,681,750,74,204,22,95,217,437,235,86,123,25,352,192,42,597,954,256,77,18,673,63,864,752,671,306,87,353,746,287,712,237,397,389,115,588,222,177,180,145,580,715,21,141,59,474,317,207,66,56,49,52,33,28,579,45,544,10,84,61,784,102,4,511,202,693,57,40,24,117,173,76,319,240,208,5,216,26,304,134,291,205,114,525,72,17,153,577,168,11,108,422,718,498 }, +{ 15,4,11,5,13,23,21,32,33,1,37,31,2,141,3,180,64,102,44,12,49,196,120,77,40,6,17,7,153,117,352,134,0,82,269,197,51,22,93,10,752,260,165,217,98,317,137,365,342,202,901,318,95,341,450,115,48,30,9,128,177,351,125,523,110,498,854,291,237,89,90,8,817,45,59,14,57,86,457,873,326,373,372,72,61,922,139,56,275,482,36,864,391,96,898,386,111,403,144,27,38,509,899,453,70,116,29,58,253,244,76,160,475,437,304,316,210,16,41,569,308,300,203,719,288,221,546,148 }, +{ 217,33,15,13,117,196,153,134,23,51,77,141,0,2,64,115,102,10,4,5,7,6,180,3,1,11,165,352,202,22,9,40,317,269,197,14,128,318,341,237,17,177,16,291,12,95,365,120,125,18,752,28,453,372,59,137,498,349,49,36,31,24,403,32,110,98,210,386,19,457,401,8,391,901,475,44,86,260,342,304,143,247,308,730,93,275,244,494,164,719,21,864,65,509,57,961,523,25,90,105,546,139,216,55,38,482,569,402,35,29,450,99,873,421,48,670,45,27,26,326,20,37,437,39,629,579,56,203 }, +{ 15,33,77,4,102,117,40,115,134,11,153,13,217,21,1,5,317,196,32,23,3,2,59,12,291,341,365,51,139,120,177,180,372,349,48,141,260,17,342,110,6,197,37,7,72,482,90,165,111,57,352,93,898,752,0,326,457,210,82,854,901,450,269,202,14,36,64,125,523,373,10,137,89,922,203,719,498,730,961,569,244,817,143,44,318,8,76,304,277,403,253,275,31,494,128,386,308,160,22,247,56,899,148,144,453,468,61,638,579,300,96,437,221,116,873,38,485,94,99,237,216,9,27,49,45,19,86,401 }, +{ 15,13,217,2,1,23,0,4,5,3,317,51,341,7,6,8,115,9,365,202,10,11,291,12,22,33,269,31,82,180,308,457,120,28,901,30,18,77,197,450,351,21,961,40,221,372,523,165,730,318,719,854,141,102,64,453,110,260,14,569,93,117,44,16,817,36,32,349,386,498,482,98,403,898,196,134,35,153,24,49,125,752,579,326,899,237,128,137,247,401,342,48,475,177,39,304,661,20,922,509,494,19,116,59,37,203,468,95,329,144,758,55,17,65,244,86,111,391,90,118,437,113,91,122,143,629,864,99 }, +{ 135,66,2,7,192,337,97,52,14,20,673,355,29,43,104,107,331,226,415,376,353,222,294,147,74,112,46,172,560,155,32,5,324,470,609,173,174,21,825,16,64,58,298,145,92,707,38,232,77,35,356,4,10,414,22,525,287,262,171,888,86,632,544,595,635,568,178,90,48,580,87,334,202,597,72,37,60,587,474,6,151,95,307,49,177,59,25,0,142,17,858,61,409,128,1,12,721,583,318,259,362,500,141,126,269,198,102,319,70,494,13,230,853,45,227,54,849,612,400,65,204,186,33,819,246,76,644,279 }, +{ 195,132,146,142,271,167,121,363,878,178,687,647,786,212,354,77,522,929,60,202,411,120,35,234,147,259,101,360,520,626,446,781,98,0,450,668,5,199,55,771,562,23,16,706,777,49,936,18,621,604,48,158,318,639,13,31,407,22,125,21,591,590,465,481,159,534,51,109,198,419,24,126,364,191,141,7,682,551,252,28,20,806,634,156,105,14,447,948,494,107,10,116,39,43,1,269,260,33,265,128,640,291,93,403,137,326,95,203,421,379,102,765,9,82,692,110,416,32,883,664,553,455,694,475,842,523,293,239 }, +{ 202,77,20,318,104,66,269,291,494,102,177,43,128,0,342,216,173,5,2,97,52,450,234,22,7,33,750,29,112,319,74,275,90,337,32,49,198,210,678,544,147,204,58,24,141,789,21,349,64,18,304,16,10,14,580,120,192,194,325,13,416,142,403,500,196,61,59,137,115,86,712,153,113,12,226,174,415,40,23,81,4,28,70,525,402,1,9,230,72,35,178,214,636,46,11,6,904,37,95,87,48,217,355,597,334,31,798,39,17,38,427,186,222,65,376,117,426,285,36,135,51,208,353,673,155,792,172,260 }, +{ 107,362,612,359,414,356,97,392,683,259,819,43,298,660,465,147,581,729,173,779,7,595,501,20,319,470,534,858,246,376,279,126,230,14,5,178,427,232,493,35,92,49,132,426,885,60,32,104,387,0,786,423,66,38,21,121,878,22,16,146,861,441,95,357,560,830,142,2,199,61,172,10,81,707,150,70,590,422,207,48,140,46,58,929,52,324,603,192,18,508,202,314,77,37,347,33,263,13,135,28,241,12,23,29,125,1,345,124,102,98,74,64,568,24,198,380,40,4,687,626,105,607,117,72,177,577,65,318 }, +{ 6,19,13,26,453,165,457,386,51,23,14,74,401,391,308,758,719,603,47,867,29,138,2,70,730,27,366,309,108,324,197,1,25,196,743,711,78,235,262,130,42,475,133,50,115,145,670,487,112,58,53,52,85,17,421,894,96,34,412,5,331,516,509,163,33,500,569,629,482,388,45,468,153,218,128,64,341,283,594,545,444,65,125,600,217,114,236,754,75,899,73,785,630,137,95,38,801,328,895,103,961,550,901,200,11,350,558,117,4,220,180,54,46,745,3,134,433,766,287,739,122,223,317,226,390,241,21,357 }, +{ 179,205,72,245,317,247,152,180,216,490,76,752,352,79,217,59,864,94,498,4,497,452,89,148,11,875,111,141,405,115,188,21,468,588,873,197,579,471,365,464,37,645,372,482,5,77,64,431,341,237,10,529,349,1,12,23,2,474,128,134,316,339,291,36,32,51,38,413,437,629,954,734,308,99,13,143,48,102,165,14,288,90,377,749,184,401,460,542,33,428,3,7,269,449,961,569,901,350,86,40,257,244,202,0,270,117,6,304,54,241,402,45,137,221,457,403,177,9,164,139,318,17,528,15,453,421,358,769 }, +{ 107,359,7,314,392,298,387,279,581,32,232,60,14,126,97,493,534,150,356,121,423,362,172,92,259,147,441,95,35,501,22,357,21,16,246,590,0,380,786,49,18,465,13,64,132,5,28,146,178,10,508,779,23,140,48,24,71,939,929,660,51,135,607,324,603,115,230,43,46,241,38,192,683,462,247,66,612,568,941,835,819,77,12,550,90,36,560,199,4,312,33,9,893,347,330,65,59,52,20,519,2,278,50,626,58,202,37,345,771,294,117,40,102,70,470,451,156,448,350,316,707,236,173,288,31,75,54,672 }, +{ 156,354,586,271,195,591,668,132,48,360,340,121,23,21,463,446,1,5,13,18,658,296,407,51,146,70,698,883,626,55,60,221,777,163,32,363,379,621,590,562,58,49,167,467,647,522,150,61,118,24,411,781,115,447,223,767,786,7,16,31,87,308,687,22,9,259,279,212,44,17,518,346,71,10,371,45,98,2,919,241,761,33,12,93,64,39,541,37,608,35,774,341,759,75,664,941,663,125,846,581,401,120,638,566,303,68,936,260,0,932,199,105,634,270,706,30,453,141,102,95,86,110,107,771,273,535,82,727 }, +{ 23,51,13,453,4,475,0,12,457,10,2,115,401,15,11,403,5,1,40,99,14,59,961,165,569,629,9,32,77,37,308,3,719,7,57,421,21,64,17,494,6,22,33,102,18,202,177,128,730,197,523,670,139,141,899,16,90,341,8,56,217,210,402,48,24,386,125,27,318,95,45,86,72,775,269,758,50,65,19,76,111,38,317,180,89,62,342,54,46,734,28,247,41,29,36,509,237,26,880,196,352,365,275,468,120,216,153,152,108,25,137,757,349,221,181,117,277,49,477,123,34,291,304,801,82,31,678,597 }, +{ 48,125,221,21,165,23,197,13,763,423,508,193,98,293,457,391,5,867,693,386,441,81,18,16,51,92,60,314,64,683,401,49,237,96,104,43,430,35,397,558,241,180,126,523,105,32,758,140,38,72,510,308,474,356,230,95,279,102,501,784,517,663,858,541,31,633,498,718,149,127,91,422,97,418,118,900,159,144,115,511,44,203,246,366,24,223,480,453,361,350,218,160,71,22,701,719,393,40,173,124,963,421,370,148,69,643,93,743,598,493,298,253,191,170,164,116,7,1,939,730,120,74,33,20,326,63,192,269 }, +{ 202,5,450,77,120,132,494,195,523,318,326,1,260,146,121,271,167,576,203,0,817,615,18,21,403,692,234,604,354,142,49,13,35,23,7,212,20,14,2,682,158,252,147,137,101,102,128,32,33,265,48,16,291,551,342,419,364,60,22,966,475,43,107,9,178,363,51,639,17,10,687,12,821,6,859,159,520,416,97,848,805,31,360,269,109,93,198,329,259,39,55,64,562,786,402,92,82,553,878,70,61,647,455,180,411,95,315,191,4,842,105,239,165,822,638,115,98,634,24,453,346,126,99,156,899,199,522,28 }, +{ 39,166,9,101,404,158,333,30,0,190,310,252,274,68,438,419,100,344,88,49,254,124,694,865,154,530,656,364,577,28,810,265,65,35,131,455,159,857,16,21,24,793,22,539,212,777,105,625,747,677,709,562,95,48,286,1,329,703,18,228,71,125,272,36,109,13,10,289,538,219,676,706,778,51,31,98,557,86,23,175,60,553,406,55,962,671,840,118,224,168,221,83,669,67,213,382,96,570,64,960,346,327,170,613,731,191,242,44,115,759,439,957,627,748,114,634,264,688,593,37,251,137,320,127,7,520,167,518 }, +{ 15,110,4,120,11,5,1,23,13,165,93,21,10,2,32,40,180,82,197,3,51,6,33,0,7,125,351,901,391,352,77,9,115,260,102,341,117,217,317,134,31,141,752,453,12,48,8,153,386,509,873,203,457,401,196,37,898,372,59,326,475,55,14,202,569,57,498,403,719,17,64,308,237,494,318,269,94,90,253,961,22,44,730,36,697,342,177,482,56,474,137,89,72,421,349,38,670,576,139,111,30,28,210,148,96,758,143,734,629,16,244,160,128,18,291,41,805,247,25,19,84,373,245,61,488,579,205,523 }, +{ 2,29,52,1,26,58,6,262,5,50,14,70,103,46,19,38,309,27,96,112,67,516,75,163,133,331,218,145,108,223,17,236,45,83,287,25,487,74,114,171,785,409,357,113,238,24,433,151,53,36,388,220,739,34,545,396,307,532,583,675,61,600,181,42,65,73,764,160,635,21,603,394,12,80,594,186,150,324,894,632,3,7,334,99,54,137,315,891,69,445,37,412,323,48,745,537,18,283,87,458,138,822,308,243,155,765,86,711,895,398,285,28,651,366,226,115,766,849,294,23,480,820,51,221,297,22,732,209 }, +{ 120,77,260,141,51,13,217,23,317,1,450,291,15,202,137,326,373,110,177,180,21,269,165,318,128,197,352,115,349,64,32,203,5,342,351,0,102,48,341,96,854,93,82,523,2,244,457,9,160,752,386,36,922,12,365,372,37,898,7,304,275,403,86,10,210,253,817,16,704,28,83,90,901,498,143,6,615,4,719,22,494,237,18,661,730,473,30,24,308,50,221,40,76,72,55,8,59,35,11,33,391,285,67,89,144,125,61,961,99,39,401,14,3,475,247,402,44,31,70,453,49,509,642,153,208,482,17,216 }, +{ 444,183,291,297,358,196,554,255,56,59,123,269,33,605,108,122,153,177,275,210,77,57,341,117,130,128,475,467,730,152,134,42,403,4,50,880,19,381,315,401,51,65,421,216,630,349,283,21,503,235,133,85,23,11,719,368,46,202,99,494,432,229,40,14,29,561,665,339,670,480,736,588,523,453,102,15,899,650,326,256,249,236,197,125,318,2,775,757,711,624,396,365,328,524,78,64,3,529,194,18,13,0,250,12,6,690,25,445,266,48,511,402,285,26,789,678,260,242,165,120,343,769,543,488,409,390,369,366 }, +{ 23,13,51,12,165,14,120,153,38,197,117,37,99,401,134,386,453,54,128,202,141,196,64,5,328,40,237,21,137,139,15,260,33,2,719,475,125,133,180,457,0,46,403,242,77,236,6,208,1,36,96,32,4,391,361,115,308,52,194,629,17,569,730,477,86,102,546,817,450,19,10,758,961,933,923,903,558,160,11,29,22,18,3,188,111,79,114,108,65,48,95,291,56,349,157,94,41,338,221,89,82,34,59,277,177,49,317,304,506,372,351,50,26,421,217,44,31,7,72,341,352,350,316,312,207,183,143,130 }, +{ 95,64,348,33,467,56,343,174,554,511,77,312,233,120,108,180,0,561,6,355,543,13,306,370,197,165,859,25,18,29,297,256,91,63,899,237,49,770,650,412,235,85,138,19,42,523,65,671,90,102,22,9,32,368,194,40,23,10,4,576,426,416,204,161,153,117,116,86,26,27,104,448,775,721,719,457,450,344,326,275,217,72,24,605,730,283,245,196,98,76,17,5,577,519,123,835,475,192,81,78,353,588,386,365,269,260,222,220,214,179,170,141,134,125,119,105,97,66,51,28,11,881,273,242,172,746,707,643 }, +{ 120,51,260,326,450,202,23,77,141,203,93,13,137,318,21,1,82,165,110,15,523,5,351,269,197,64,102,373,128,33,217,48,180,386,32,291,457,125,817,352,922,854,403,115,144,116,177,349,210,12,96,196,615,475,7,719,372,367,237,40,730,2,342,704,4,98,391,10,275,59,494,453,401,0,37,6,421,317,50,16,90,36,44,752,308,9,498,160,31,393,18,899,221,341,14,3,30,45,402,8,898,304,83,692,99,24,551,72,947,247,216,28,961,153,864,678,661,473,88,509,70,22,285,17,576,300,76,35 }, +{ 15,4,11,13,5,23,21,32,1,64,93,33,2,3,180,40,22,37,141,10,82,0,6,77,102,44,31,352,7,120,117,165,341,12,48,49,110,197,98,134,9,752,17,901,153,498,342,269,196,115,291,365,128,260,36,317,137,51,95,59,217,318,237,202,349,72,8,509,391,14,873,177,16,523,86,922,386,247,143,304,210,125,29,45,57,457,453,90,854,482,403,111,579,160,244,18,569,437,89,61,253,76,27,899,351,864,275,450,719,56,758,116,875,961,277,468,164,148,25,817,401,316,84,139,546,490,216,46 }, +{ 15,341,13,141,23,33,217,4,77,180,1,102,40,317,5,137,0,365,202,51,11,21,351,854,9,269,82,10,115,372,32,64,922,901,22,117,260,352,197,153,128,36,28,196,752,134,165,59,318,349,3,120,2,12,30,177,244,37,450,143,291,31,86,457,48,139,160,16,8,7,898,247,253,125,523,18,49,44,57,403,304,55,730,569,961,6,111,110,14,96,498,494,17,308,719,72,39,237,642,386,475,90,98,24,342,45,401,817,326,873,661,76,277,95,509,105,421,210,93,811,453,717,144,391,99,89,164,221 }, +{ 272,105,131,327,286,320,578,109,22,28,219,239,224,159,9,616,371,49,95,623,538,98,716,910,713,127,258,737,187,185,385,761,424,168,170,64,55,254,347,169,928,916,0,158,164,1,924,31,23,301,51,293,39,627,374,125,913,201,44,836,242,912,36,395,13,116,65,479,634,24,442,654,86,346,284,276,35,312,137,308,512,160,115,194,420,10,175,2,154,7,338,21,128,16,944,83,143,456,12,67,601,202,762,141,908,124,535,289,574,871,688,165,401,900,5,763,96,892,933,384,317,506,237,860,14,37,68,30 }, +{ 16,68,35,71,18,60,118,24,191,126,92,101,150,212,167,154,302,175,9,346,55,0,273,7,124,108,107,375,628,28,566,14,722,149,815,199,589,439,882,575,520,363,121,562,246,467,21,364,1,806,279,140,22,49,48,252,517,695,65,230,345,847,647,97,621,411,69,146,336,46,653,39,10,109,694,604,535,5,189,768,399,446,637,50,441,2,755,419,125,298,608,43,314,423,13,649,158,706,463,398,100,67,818,508,38,12,37,20,826,95,354,264,23,32,169,166,132,271,105,96,221,674,83,51,932,429,356,54 }, +{ 20,470,147,43,904,376,798,427,325,595,142,178,858,198,319,0,97,173,414,22,259,61,422,819,77,945,202,14,32,5,362,49,426,392,107,207,887,725,660,481,263,465,234,104,450,534,494,861,95,28,18,33,120,10,612,64,344,105,13,30,324,39,21,455,817,115,46,146,102,31,132,4,246,356,318,65,158,66,260,40,7,48,523,581,9,35,159,315,113,729,23,230,125,347,112,38,128,878,359,59,36,60,12,239,600,182,237,192,153,342,196,683,98,16,170,2,306,298,357,54,279,101,70,560,501,345,199,845 }, +{ 51,23,13,12,99,453,33,202,128,64,153,102,15,196,37,403,141,40,120,117,475,38,134,115,14,137,77,401,139,10,0,165,318,269,82,308,21,2,5,197,54,3,180,260,22,31,494,421,17,86,93,1,95,44,111,457,277,237,402,450,9,110,18,6,98,719,125,65,351,32,28,217,49,342,326,328,730,629,352,386,285,361,221,11,50,36,19,341,48,569,177,41,59,8,194,27,24,89,79,523,29,775,4,961,365,7,670,291,144,373,210,114,46,317,391,34,116,509,757,203,236,108,30,26,817,55,498,57 }, +{ 383,136,17,62,84,440,119,504,56,240,548,3,80,129,378,123,106,128,555,205,4,184,507,59,162,11,14,183,57,361,249,38,202,469,152,64,179,495,483,90,32,215,257,214,245,130,209,54,161,27,250,503,328,229,618,211,5,73,402,45,12,306,318,40,34,514,790,76,452,77,65,26,86,297,141,19,53,497,742,269,814,283,37,33,255,311,194,436,667,114,177,61,217,803,95,207,266,2,50,432,769,757,236,102,21,527,36,556,46,22,10,72,115,41,494,611,381,29,196,99,216,210,151,248,445,137,0,576 }, +{ 70,652,48,5,638,846,1,21,163,340,888,767,221,125,761,349,260,562,269,303,518,658,591,586,13,698,623,23,883,774,98,87,49,31,9,371,296,223,51,763,850,863,120,541,360,346,141,634,64,535,39,30,2,0,845,7,622,156,318,195,44,95,202,900,32,12,931,379,6,558,768,293,271,115,777,401,58,354,818,61,55,17,132,668,450,308,385,914,351,919,93,922,391,457,128,22,590,212,291,77,807,762,758,252,159,101,663,352,538,372,100,446,75,520,18,937,284,96,146,626,326,127,118,116,771,522,463,69 }, +{ 66,135,74,97,278,6,172,69,324,140,267,808,462,14,550,204,7,357,138,87,189,85,298,75,29,2,500,52,335,38,25,92,582,802,174,571,572,841,563,42,380,1,24,314,21,350,16,451,67,46,280,220,501,443,150,620,230,103,486,423,35,353,622,60,83,226,71,192,32,889,26,22,65,903,95,5,48,232,330,64,108,86,565,792,641,580,36,448,70,509,197,222,43,348,558,426,241,726,125,262,0,877,893,50,872,81,309,63,233,950,37,749,18,388,149,145,12,91,237,10,355,606,294,246,114,508,673,28 }, +{ 51,23,33,102,13,128,40,196,202,77,64,65,139,141,99,12,14,453,117,153,475,403,134,10,86,137,4,11,22,0,318,269,115,401,36,114,108,21,38,5,120,180,421,165,402,59,28,15,95,82,210,37,2,177,494,31,342,9,260,217,277,523,386,3,308,450,6,457,730,17,133,18,719,24,46,361,1,352,49,194,143,111,54,50,351,197,300,94,44,237,341,164,365,221,389,67,32,48,283,19,485,291,29,236,27,854,93,961,758,110,326,817,317,285,125,275,90,52,45,34,26,7,391,181,57,8,752,670 }, +{ 88,264,435,100,166,274,772,921,478,382,909,639,438,960,521,96,897,410,30,1,466,190,530,778,265,321,682,691,418,144,724,329,686,39,9,447,93,31,914,131,404,203,44,411,765,335,593,475,795,367,98,625,254,664,120,213,613,403,0,744,539,116,83,656,363,238,5,48,13,576,77,812,805,110,23,21,141,966,69,125,616,829,326,286,201,82,35,520,225,51,821,127,604,285,284,165,260,217,33,36,310,731,340,842,223,102,137,101,615,64,831,252,228,49,10,67,224,557,219,443,692,406,272,268,910,506,197,351 }, +{ 440,62,136,548,504,56,84,3,555,383,507,129,361,128,123,162,4,17,59,119,184,14,202,183,152,161,402,495,328,57,205,514,527,229,215,80,27,257,38,11,378,73,757,497,472,240,211,130,503,452,803,54,469,77,53,34,494,381,318,283,266,250,106,12,102,33,245,210,432,64,403,19,40,880,236,90,5,556,179,21,412,377,2,32,200,300,99,196,269,585,153,488,141,51,275,216,45,37,117,249,529,194,214,372,76,862,177,134,133,313,26,421,0,139,471,41,305,65,248,483,13,114,678,23,217,50,72,775 }, +{ 911,617,959,332,206,253,244,141,282,120,110,384,951,143,352,260,269,10,160,373,717,531,811,417,940,55,728,661,533,36,434,450,93,137,559,203,473,498,642,116,180,326,855,261,295,44,268,638,922,854,752,31,499,164,318,351,201,535,144,851,564,341,365,64,8,115,165,197,843,1,217,317,704,864,187,699,567,127,202,873,237,251,22,258,574,824,98,86,968,860,489,23,817,82,125,21,169,509,926,219,13,276,15,601,28,345,9,372,51,901,349,943,871,5,367,291,776,308,131,391,96,523,105,83,386,902,338,35 }, +{ 120,260,450,141,77,15,1,23,351,326,137,64,373,82,203,33,13,291,51,21,217,523,269,177,102,128,922,165,110,197,854,5,318,457,180,352,48,32,125,144,93,817,160,202,386,96,50,7,372,36,349,304,210,2,342,730,115,898,719,16,0,196,247,494,59,10,9,473,22,4,403,116,18,86,67,341,752,615,95,75,901,317,275,704,24,475,453,437,367,208,164,163,143,37,90,899,244,98,12,3,365,968,551,300,237,206,55,35,29,6,801,805,678,576,401,393,391,288,268,216,194,99,72,61,58,49,39,242 }, +{ 15,0,1,2,5,51,9,23,7,13,6,901,12,3,8,180,4,141,82,115,14,10,341,120,197,33,93,730,21,40,125,401,260,202,102,22,110,64,569,719,457,165,37,961,898,453,18,351,31,28,203,16,137,352,217,450,77,326,11,403,629,308,128,196,32,30,36,269,44,98,116,318,365,99,475,373,670,237,24,899,854,523,391,95,421,49,48,317,177,221,817,153,55,117,752,39,86,144,38,642,105,922,242,291,54,143,17,134,494,498,244,59,661,96,473,65,160,509,139,170,247,89,114,90,638,20,704,482 }, +{ 104,7,97,107,356,560,66,232,707,359,298,568,289,324,712,392,192,38,14,426,173,65,95,64,172,387,22,43,92,32,357,362,214,20,259,526,90,0,330,380,126,246,501,49,5,86,893,74,500,194,874,147,135,612,230,10,204,779,52,2,729,808,550,140,35,835,21,28,314,77,312,60,16,465,132,715,279,423,414,600,885,802,683,18,81,24,36,13,636,861,150,33,178,29,819,519,288,4,31,58,660,448,121,534,12,168,61,48,581,278,70,37,146,102,441,23,46,199,142,6,751,249,76,319,202,189,124,237 }, +{ 6,66,1,2,74,67,29,14,24,69,83,135,25,458,52,150,108,451,75,38,103,380,267,278,7,87,26,42,324,36,357,138,602,114,97,19,85,238,594,273,65,149,398,335,172,46,160,204,70,18,711,550,50,16,309,220,71,732,96,696,5,133,603,12,891,86,174,28,280,500,418,467,226,32,22,21,222,189,27,60,124,423,486,547,895,302,733,236,532,388,208,64,63,0,35,17,13,298,95,262,45,906,336,646,582,37,145,194,571,20,531,112,23,58,140,516,484,48,886,218,118,115,99,81,462,429,54,55 }, +{ 51,23,13,453,12,115,202,21,403,401,5,4,457,719,475,37,99,165,32,308,64,3,1,2,523,10,730,0,128,17,197,15,141,494,11,22,14,6,269,961,7,318,120,386,77,899,18,247,421,450,817,48,16,117,59,90,33,125,217,9,19,153,180,629,26,758,237,24,177,28,221,95,57,341,40,25,38,54,569,391,27,260,402,29,216,317,36,72,291,102,56,134,365,45,76,49,468,304,352,137,210,203,34,89,196,342,82,275,194,349,437,46,123,326,50,20,8,670,482,86,509,139,498,316,53,61,576,288 }, +{ 57,59,77,4,381,11,412,369,210,291,421,283,803,53,250,542,377,14,339,471,275,108,99,670,19,300,453,403,23,27,401,368,40,51,202,361,775,21,12,554,236,183,402,813,33,342,38,378,475,503,46,37,444,10,736,390,152,90,56,561,285,153,134,123,117,650,73,899,523,281,0,529,494,63,386,880,432,162,514,128,58,605,102,817,743,690,495,349,277,256,196,137,111,79,18,9,801,235,139,78,867,391,200,119,758,130,42,26,480,389,719,714,467,341,80,269,122,730,221,216,188,177,161,133,114,45,29,22 }, +{ 15,1,5,2,0,7,23,6,13,51,9,3,180,115,12,457,4,8,14,141,901,719,197,33,21,82,961,365,22,120,165,341,31,351,64,202,308,77,37,401,260,117,16,153,28,11,110,93,730,10,32,44,269,137,217,569,18,98,102,49,128,317,453,125,30,36,99,523,48,352,134,752,450,196,386,318,326,40,95,24,758,898,17,116,629,39,373,203,86,72,899,237,403,59,177,35,873,90,89,55,291,105,817,144,734,475,244,139,421,65,498,76,247,494,20,38,854,349,160,253,509,372,96,482,221,216,615,143 }, +{ 64,237,180,95,312,197,146,32,90,247,316,929,352,126,165,288,317,132,304,13,60,490,77,217,955,522,92,314,86,22,4,23,7,5,21,626,40,14,11,12,652,498,291,177,232,164,199,33,10,482,51,141,115,150,633,462,579,340,216,16,529,128,37,360,638,388,121,359,143,468,275,59,107,49,534,125,202,658,509,269,888,267,1,598,581,588,76,17,467,451,296,28,751,660,441,349,194,29,31,71,698,279,253,45,640,57,48,546,134,117,18,2,830,647,372,102,65,437,493,351,98,733,622,423,273,241,309,137 }, +{ 1,23,13,51,120,15,202,180,5,21,260,141,0,326,2,137,7,77,197,450,269,33,165,318,9,128,352,110,48,6,102,523,12,115,342,317,4,32,16,386,14,403,373,203,817,36,18,217,64,96,37,457,93,752,82,28,341,494,24,615,3,22,291,351,401,8,475,10,308,453,421,391,901,221,196,59,719,730,854,30,177,90,160,365,402,125,35,498,31,285,99,39,11,244,237,72,20,899,44,40,704,86,17,922,83,50,210,349,569,576,98,95,67,76,372,509,49,143,116,253,961,65,144,89,55,551,661,29 }, +{ 14,46,2,16,24,75,69,35,220,52,7,38,1,140,267,29,71,50,309,60,87,92,70,171,189,67,18,54,163,262,335,108,230,58,366,103,150,5,675,236,181,112,223,278,409,537,21,83,294,48,155,68,12,486,149,37,571,582,96,0,246,334,135,186,443,45,328,28,32,133,641,97,451,151,388,64,307,126,25,9,502,760,394,281,357,22,65,66,774,36,10,55,172,114,61,6,565,462,461,251,552,563,400,74,820,330,238,218,600,72,99,86,606,314,17,380,430,227,587,433,20,13,23,583,516,545,121,115 }, +{ 127,98,31,44,299,276,284,9,935,293,116,599,49,0,456,201,492,28,39,105,144,371,662,22,159,125,272,137,242,131,737,489,393,567,268,395,51,1,761,578,367,202,158,30,109,918,916,685,219,254,203,616,23,434,36,21,55,141,295,95,13,24,16,187,93,385,128,18,96,948,48,269,763,160,318,64,5,165,86,101,244,326,115,518,35,286,836,261,37,512,7,10,68,327,623,716,384,120,403,253,688,110,333,224,2,890,457,143,100,197,12,386,252,301,320,258,963,310,65,14,237,924,221,170,494,421,83,699 }, +{ 53,573,445,297,436,73,6,27,151,113,26,250,491,19,614,145,611,200,315,112,45,396,881,138,80,42,58,70,34,684,25,631,17,222,287,106,74,472,122,61,624,714,690,235,5,90,32,65,255,64,648,29,14,66,95,85,322,2,209,243,549,311,644,22,1,52,665,280,47,161,86,76,87,194,312,651,3,667,133,416,75,307,114,128,78,62,137,51,57,4,226,141,50,136,46,862,483,49,289,884,119,440,632,814,12,21,176,237,148,56,236,283,174,36,135,63,123,337,10,453,33,412,84,469,214,59,108,23 }, +{ 90,788,804,32,115,693,494,81,84,56,725,403,119,21,18,681,99,77,202,526,65,240,214,576,64,817,91,0,784,350,241,102,397,342,690,848,104,61,24,945,318,69,825,453,421,365,210,134,123,117,33,20,707,402,60,293,263,207,194,114,49,888,370,97,43,673,671,523,726,326,125,510,879,773,718,715,701,462,283,249,180,153,59,57,51,11,887,835,814,111,23,4,889,468,289,247,221,95,89,86,66,120,719,325,192,899,730,683,595,422,757,486,484,426,351,312,306,302,285,275,260,238,216,203,196,177,174,165 }, +{ 24,7,14,149,71,16,18,108,65,69,28,154,189,35,2,140,124,92,68,150,118,175,429,302,0,66,273,67,336,38,55,97,251,46,60,36,547,1,267,484,22,458,230,29,83,6,9,388,86,650,172,314,95,109,114,52,135,21,64,886,602,74,505,10,232,5,20,12,596,823,705,398,32,103,49,101,420,570,236,531,289,533,50,298,168,169,48,133,462,451,380,23,882,246,283,51,799,13,43,25,185,797,194,847,75,357,278,158,37,439,346,594,330,733,399,423,610,164,192,535,701,104,345,467,143,443,125,137 }, +{ 68,0,101,9,212,65,124,35,154,189,69,149,114,230,67,39,252,100,1,336,702,329,16,265,520,83,103,191,264,755,64,589,88,562,28,705,30,24,283,167,118,18,777,732,478,12,21,723,71,166,23,49,175,133,398,335,596,55,664,218,237,429,389,532,818,604,364,50,158,115,484,289,246,352,238,410,141,22,759,419,323,375,345,36,13,10,806,128,51,547,796,269,213,5,48,722,140,247,321,96,565,480,164,180,251,330,803,553,37,676,95,143,202,86,75,490,443,7,936,724,99,2,190,694,346,197,782,917 }, +{ 5,2,186,61,29,52,45,58,311,17,171,80,227,209,48,155,1,106,243,496,50,46,334,454,592,21,513,14,502,255,869,666,151,663,262,223,75,207,38,32,270,667,62,6,27,738,659,119,56,3,807,54,780,287,378,84,721,74,220,290,96,163,483,72,263,89,67,549,103,281,136,37,583,76,430,181,536,7,70,408,12,394,64,787,73,675,148,495,469,69,161,400,409,36,833,86,65,34,19,474,587,742,767,221,26,324,128,4,309,115,24,83,306,425,25,22,431,10,241,760,90,141,764,541,133,240,95,383 }, +{ 15,1,13,2,5,3,4,0,341,7,6,23,10,11,14,12,9,33,51,17,165,153,197,365,569,180,77,40,102,8,115,117,21,901,64,217,317,22,141,16,128,32,457,196,31,134,28,36,291,18,177,139,137,237,719,961,37,57,120,111,352,349,386,59,202,453,629,86,752,19,758,38,93,873,269,44,391,372,49,143,95,342,730,30,475,498,509,260,210,318,125,110,304,55,27,29,403,82,48,24,277,72,482,275,116,494,25,450,98,54,351,76,90,326,41,79,373,99,899,546,523,194,144,474,94,56,203,39 }, +{ 13,23,15,51,0,1,10,4,2,5,77,9,115,3,12,14,22,102,11,197,33,457,7,961,165,453,403,40,59,6,21,475,494,18,719,64,180,28,202,569,32,401,31,37,17,16,8,210,177,341,523,120,141,386,730,49,217,99,44,318,57,450,308,36,629,421,125,93,153,342,90,117,24,111,498,139,269,237,30,196,817,291,134,317,326,98,95,365,899,260,352,19,48,82,275,391,72,38,89,110,509,482,86,873,216,76,349,148,247,79,27,54,116,474,65,203,901,615,55,45,351,128,758,137,304,752,94,50 }, +{ 15,13,0,1,23,2,10,102,5,9,33,7,4,3,6,77,51,115,12,11,341,217,196,40,14,117,153,18,317,569,22,8,457,365,523,16,342,180,197,165,120,719,453,961,59,134,730,21,64,141,260,31,901,401,291,899,386,28,139,82,210,177,349,269,49,24,482,202,17,372,817,44,450,93,629,111,32,125,351,326,300,352,37,318,110,128,30,36,475,95,579,468,57,308,19,137,38,98,247,437,304,275,20,65,752,237,498,391,494,758,615,90,86,373,854,116,277,99,72,96,48,421,203,45,216,105,898,89 }, +{ 166,39,274,190,438,310,30,333,530,100,252,656,625,539,265,404,778,88,101,455,9,0,557,960,593,329,254,131,1,676,382,709,724,158,553,840,731,228,98,31,669,23,68,219,264,48,21,748,957,812,406,49,224,127,44,13,344,36,272,286,51,159,520,419,105,96,116,125,613,83,5,93,795,124,213,28,863,22,223,35,478,10,816,352,293,69,67,829,16,827,258,616,364,160,64,242,639,578,120,167,128,65,518,217,321,154,32,137,187,604,180,4,679,435,118,18,251,212,144,86,95,759,141,77,410,793,371,37 }, +{ 734,94,148,308,79,111,413,468,89,37,431,115,179,401,569,197,629,341,188,13,873,474,490,365,72,12,11,134,205,245,317,117,4,247,358,59,40,152,509,51,482,901,961,76,99,528,23,180,10,498,217,216,477,339,77,139,102,38,165,55,749,421,196,743,579,143,33,542,428,391,875,752,471,497,54,1,452,153,177,21,488,588,3,64,141,349,453,485,670,277,291,457,2,128,5,57,403,36,475,529,184,540,202,48,377,90,7,372,14,50,405,210,437,15,758,402,464,386,352,300,157,6,17,0,45,794,9,342 }, +{ 539,228,224,219,190,816,840,871,30,669,530,957,258,748,863,406,93,187,778,531,160,213,88,466,839,36,131,137,116,202,728,679,926,120,44,352,39,321,373,318,274,276,851,31,574,442,860,144,730,217,201,203,613,251,856,55,559,533,944,141,435,1,450,242,230,22,127,86,110,64,800,28,49,326,473,9,260,752,268,254,166,418,717,567,456,98,96,776,902,385,968,100,719,180,269,616,317,918,351,83,827,492,10,625,931,494,125,831,908,197,21,365,372,82,371,35,77,716,854,795,689,295,704,5,332,345,244,291 }, +{ 801,393,125,791,386,730,23,551,51,21,144,116,48,203,98,221,175,165,949,93,575,31,126,13,958,326,697,601,421,345,788,293,367,22,963,541,110,455,60,608,670,273,197,44,463,4,32,677,623,127,33,259,834,35,948,453,375,133,107,120,77,40,223,0,407,342,68,719,121,43,14,446,621,590,523,373,308,102,49,16,899,1,815,475,199,770,725,191,493,804,96,36,319,141,101,92,39,146,690,71,55,7,118,38,805,352,649,735,615,602,533,531,480,323,279,246,208,201,196,180,160,105,64,743,589,132,117,598 }, +{ 13,23,77,51,115,141,33,217,4,2,177,6,0,102,40,7,64,11,10,14,3,1,180,269,22,202,9,59,16,15,17,352,318,128,18,197,291,247,32,165,196,153,117,134,317,19,5,90,341,125,237,24,137,210,36,28,304,349,403,49,12,95,86,25,275,372,453,365,31,308,752,216,342,20,143,139,475,93,27,98,498,57,26,401,21,120,386,391,457,326,8,569,316,494,421,111,65,901,203,110,105,30,260,468,288,164,437,241,45,48,29,35,99,44,482,719,82,402,509,144,37,244,55,546,56,285,961,293 }, +{ 2,52,29,70,112,7,58,14,75,87,46,220,307,186,171,163,151,1,334,262,135,278,66,5,69,155,294,97,38,140,74,16,45,172,287,549,92,227,400,24,849,226,331,113,61,324,25,48,852,32,35,21,37,6,461,67,409,50,19,12,644,315,267,145,26,243,223,311,27,64,60,54,232,122,71,18,83,17,571,583,189,86,36,853,103,72,65,394,380,587,780,774,309,20,43,22,357,42,96,314,298,128,209,335,536,95,474,767,675,270,337,76,80,89,330,222,0,90,721,290,230,10,137,108,246,126,28,448 }, +{ 321,88,213,435,466,100,689,382,30,230,1,795,352,410,96,217,699,921,190,831,521,530,443,36,83,686,144,418,840,264,438,854,128,251,539,957,44,141,180,557,752,335,69,223,317,960,31,265,48,137,855,116,934,201,98,748,160,669,909,93,166,203,812,731,403,21,274,351,593,39,778,206,367,772,219,64,228,131,455,202,9,284,5,372,35,326,238,127,23,735,82,51,656,285,67,158,310,125,639,800,221,187,224,332,269,120,365,576,494,13,897,252,345,822,478,765,349,702,77,268,421,242,208,110,318,859,101,237 }, +{ 363,520,411,354,101,195,668,132,447,9,156,905,364,664,121,146,360,23,18,759,781,1,98,765,271,51,96,676,252,264,13,777,5,31,586,621,591,478,223,167,848,21,100,691,936,551,33,393,857,70,48,883,125,682,77,16,39,687,948,821,44,367,116,93,24,407,265,127,593,834,110,49,842,522,296,4,293,166,626,812,865,64,404,203,333,102,538,212,36,638,40,55,446,50,647,118,291,656,897,144,639,60,771,697,128,403,379,67,35,692,541,165,463,163,604,453,658,199,590,69,791,286,7,32,12,317,217,724 }, +{ 51,23,13,453,401,475,12,403,457,730,719,21,5,308,99,115,2,32,11,4,3,421,961,1,165,15,629,899,0,670,37,10,6,17,197,9,33,18,22,523,569,64,40,202,28,19,56,26,7,57,27,16,72,48,196,194,24,59,494,177,153,45,36,139,77,386,210,128,117,65,758,34,8,391,341,817,468,217,54,29,450,275,221,76,757,509,350,141,134,90,89,46,38,291,180,880,237,102,304,216,775,734,546,402,361,277,137,130,129,67,41,25,14,247,148,62,901,498,477,283,262,260,133,123,74,42,801,365 }, +{ 72,76,89,205,179,90,245,528,148,54,184,37,12,431,38,308,152,4,257,59,474,316,428,401,464,57,460,749,32,288,358,405,229,23,477,645,743,99,11,21,51,115,497,77,217,1,794,79,157,14,5,629,540,488,453,216,45,449,17,155,2,221,770,165,48,670,421,569,328,266,50,452,197,341,181,3,128,769,10,365,137,64,13,317,587,241,36,457,734,188,247,0,339,313,7,536,350,471,9,202,94,468,524,509,721,482,141,46,129,102,773,697,33,432,40,180,270,237,281,619,741,381,86,361,961,543,476,720 }, +{ 376,43,147,470,20,595,173,107,858,97,319,142,427,819,414,178,729,362,426,325,5,259,7,32,861,392,61,798,246,14,104,198,230,560,885,324,77,21,422,904,356,0,465,126,357,48,707,234,38,330,22,49,207,35,501,612,66,13,202,95,534,359,298,2,10,577,76,28,16,70,113,64,132,4,17,481,318,715,102,60,58,18,784,59,120,40,72,11,90,29,500,23,74,279,879,146,33,1,112,660,387,56,52,45,315,81,232,9,494,177,140,36,115,37,153,600,57,199,217,31,12,121,347,51,403,878,671,164 }, +{ 202,269,141,494,318,51,137,403,128,77,453,4,475,217,678,349,421,5,342,352,96,102,291,177,372,317,64,341,180,21,2,40,10,59,32,752,33,11,61,3,515,120,304,36,0,365,6,31,13,22,7,39,30,95,125,260,237,12,717,275,115,28,23,98,244,482,268,105,947,851,402,805,203,194,144,954,576,450,351,48,859,289,283,86,65,55,49,44,795,197,82,90,498,312,285,241,201,182,165,160,57,56,14,411,293,284,9,822,326,899,523,931,921,897,863,854,839,821,735,691,668,579,569,442,360,251,214,207 }, +{ 77,202,318,102,494,33,128,269,23,13,51,196,153,177,117,342,134,291,403,0,141,137,64,15,349,82,22,351,203,49,275,615,373,21,551,393,473,217,95,859,31,210,4,475,402,1,352,65,11,10,450,120,32,180,86,285,36,18,2,7,3,453,12,260,6,28,98,14,125,39,421,48,90,5,372,40,194,59,44,165,817,16,9,115,326,341,523,17,678,216,30,197,317,116,576,96,127,237,61,93,300,24,365,124,70,401,105,730,214,160,114,304,289,110,329,20,704,163,35,386,208,899,144,692,244,67,50,719 }, +{ 253,951,352,110,811,499,244,854,10,638,642,752,559,180,143,141,564,8,260,341,498,417,922,661,901,197,206,165,911,373,137,93,282,873,351,384,15,959,55,332,120,762,717,728,269,160,898,82,473,64,326,164,36,365,203,22,704,533,317,509,31,479,86,968,116,318,115,202,531,1,268,217,33,535,28,44,9,617,237,13,434,4,261,23,77,940,125,5,372,11,851,864,40,32,338,21,102,0,569,386,30,96,482,349,169,824,421,51,105,546,12,401,242,758,576,391,35,127,98,170,2,117,144,844,295,457,24,291 }, +{ 141,23,13,5,4,269,352,51,180,21,317,115,202,217,1,197,2,11,365,318,32,341,3,6,372,137,7,498,77,64,349,291,48,752,33,0,165,237,9,15,22,12,901,221,253,16,144,244,143,403,93,28,401,30,453,110,475,24,304,40,39,120,125,494,421,203,308,72,160,569,18,386,177,35,102,482,31,14,260,44,10,36,391,95,473,373,153,49,468,864,247,117,873,128,100,88,59,134,69,546,241,76,717,89,86,196,17,37,437,166,509,351,164,98,854,67,8,90,704,629,82,851,450,96,811,367,326,284 }, +{ 252,101,18,9,0,68,16,118,329,35,364,24,39,167,60,333,265,109,158,553,71,212,677,28,419,604,100,793,49,286,159,154,191,105,55,310,272,694,327,455,124,810,806,48,22,166,254,593,21,7,374,344,1,520,146,121,538,5,917,65,346,706,857,634,92,562,23,627,404,126,925,13,98,185,51,108,30,14,907,271,169,709,31,942,95,10,446,96,149,628,557,125,375,170,175,67,688,199,363,132,639,239,190,656,107,621,320,12,912,638,137,36,150,44,815,302,50,463,865,221,438,32,37,578,671,439,848,131 }, +{ 28,109,158,9,168,49,39,175,35,159,22,0,55,185,65,289,272,67,95,194,114,346,333,577,169,584,254,1,344,327,105,570,286,336,24,83,86,30,747,68,627,374,124,505,69,101,810,610,837,439,36,154,535,133,103,7,310,16,14,835,131,756,601,345,2,429,874,149,389,50,799,793,252,347,283,208,547,51,251,46,23,100,128,10,189,312,164,424,912,21,484,5,64,688,637,12,190,578,455,96,239,420,13,518,143,634,137,419,533,364,18,398,166,48,709,320,218,538,127,160,170,230,323,125,71,37,98,202 }, +{ 15,13,1,5,2,3,4,341,11,23,0,33,6,51,7,12,40,115,10,9,77,102,14,197,365,180,17,901,21,217,317,196,165,32,569,22,141,8,64,961,453,457,16,153,59,117,18,352,31,202,125,37,139,291,401,629,28,386,269,134,719,177,475,237,93,318,120,730,44,48,372,110,24,111,899,36,210,403,19,57,523,391,349,752,326,49,38,128,308,342,260,494,498,116,95,509,98,137,30,873,304,758,90,801,27,56,86,203,615,144,275,45,482,450,65,670,373,54,41,50,734,94,143,25,82,244,105,79 }, +{ 173,693,18,104,422,20,102,0,77,49,61,5,784,560,526,707,226,32,13,33,397,64,193,344,134,117,91,66,21,153,715,263,81,95,172,232,207,196,31,11,6,65,510,42,25,414,197,180,135,48,29,22,10,43,72,819,4,470,324,40,85,546,86,887,347,51,319,204,57,28,27,426,359,283,498,376,138,128,90,194,392,643,879,701,474,87,74,59,52,38,9,387,681,7,862,597,192,814,847,718,713,334,291,270,217,165,125,83,37,56,23,202,177,141,122,825,241,509,330,942,817,523,370,269,115,98,84,44 }, +{ 23,51,386,308,13,401,221,421,801,949,719,165,468,730,21,770,341,125,697,453,867,14,115,670,457,6,391,403,1,475,958,523,450,117,102,197,629,365,33,569,607,7,2,758,120,66,899,515,743,134,217,791,342,97,16,326,67,733,74,350,150,773,749,260,202,52,29,18,9,501,456,805,809,734,482,56,0,317,135,4,906,458,153,3,380,103,645,579,558,490,24,93,83,388,196,108,817,11,99,903,685,177,69,32,561,267,236,335,5,180,594,696,554,451,642,576,564,559,546,349,291,238,122,89,87,76,75,73 }, +{ 49,0,24,16,105,159,28,9,35,158,68,320,18,109,239,55,65,170,124,7,1,154,301,347,71,95,14,286,518,101,2,344,327,272,127,118,39,21,60,374,890,98,577,623,12,149,22,5,916,338,512,168,36,713,395,23,31,67,51,371,13,48,185,175,125,137,10,424,169,64,202,737,479,30,747,538,6,761,37,108,128,346,242,32,740,293,131,46,50,302,654,69,44,252,419,289,810,86,114,92,83,616,141,385,846,299,164,254,364,506,403,677,310,671,375,191,221,116,938,318,634,100,165,439,115,143,333,96 }, +{ 2,5,29,45,207,58,61,1,76,292,32,655,263,14,476,52,72,6,119,17,306,70,186,7,106,46,179,214,790,90,148,243,720,38,227,50,240,112,151,710,255,592,84,89,64,48,25,464,75,241,209,21,129,74,155,460,358,3,128,454,496,425,36,77,19,22,141,738,10,115,56,26,86,16,123,66,408,311,27,245,0,249,95,65,205,287,519,290,618,40,24,177,549,171,202,87,4,59,163,307,54,334,237,51,23,62,269,12,102,37,18,449,869,137,80,69,113,28,99,33,67,13,135,180,318,315,769,288 }, +{ 125,21,115,95,715,197,49,165,64,56,66,32,6,278,13,33,713,681,808,350,204,237,120,408,263,241,172,135,81,22,324,87,509,74,0,85,97,4,90,86,468,65,888,353,174,558,391,841,192,180,23,950,872,645,571,554,650,580,141,5,2,412,283,467,341,673,59,138,38,108,903,177,91,51,7,511,370,164,75,31,620,475,203,893,170,98,712,104,44,19,885,10,1,561,426,415,308,63,403,196,153,117,102,84,389,128,27,825,569,210,77,347,53,123,386,316,159,143,835,194,900,448,93,11,546,232,18,572 }, +{ 612,325,427,202,107,494,5,403,470,147,518,376,64,362,49,21,392,788,465,98,14,342,125,623,318,178,48,31,845,804,534,858,423,70,690,595,414,278,93,422,269,453,7,660,102,77,33,693,61,953,767,798,819,581,307,298,234,198,116,110,97,39,32,30,10,237,4,671,663,389,289,194,128,43,0,20,904,725,761,711,678,509,501,493,366,319,236,232,220,174,104,99,95,88,87,68,55,44,38,37,18,9,900,293,13,715,441,315,105,291,163,141,391,112,848,397,22,763,713,558,846,835,828,822,757,657,557,546 }, +{ 32,76,21,72,14,241,449,50,292,2,350,5,1,155,48,221,46,38,89,179,270,45,476,90,54,720,214,430,148,464,17,281,129,460,37,205,413,741,181,36,245,519,29,227,52,290,257,184,405,12,123,710,3,528,162,7,773,243,541,10,358,0,288,431,67,57,249,536,316,152,99,96,23,106,171,51,513,4,743,308,618,128,137,6,115,64,474,477,103,408,86,207,769,229,263,9,328,83,133,180,59,183,119,552,794,787,540,114,217,488,425,218,62,11,421,659,77,645,760,721,22,524,202,58,400,401,141,770 }, +{ 24,154,149,28,124,16,35,0,71,7,14,68,175,108,65,18,55,92,109,302,118,9,336,570,484,398,69,140,60,2,420,67,189,22,101,46,150,185,158,346,230,1,439,169,252,273,168,419,289,39,49,38,505,251,83,345,701,535,799,610,637,267,21,36,97,10,650,50,95,12,429,246,103,533,114,388,458,133,5,48,399,314,86,6,671,375,159,66,364,29,236,329,962,703,105,547,23,51,172,601,333,283,64,265,886,126,96,194,52,13,232,722,32,20,389,54,37,677,164,100,143,125,327,602,75,917,344,286 }, +{ 5,14,1,50,48,2,46,38,67,54,45,103,24,270,21,96,513,155,290,83,58,61,281,69,32,36,181,72,502,0,29,171,17,12,16,7,76,263,37,18,28,52,227,35,425,207,89,666,22,221,243,133,541,663,65,10,741,218,408,223,114,536,108,474,75,9,186,6,592,552,51,23,99,238,328,115,309,794,540,3,433,834,86,292,760,477,148,449,428,64,476,128,400,528,119,655,55,236,241,106,202,141,765,766,487,70,13,163,137,366,95,71,587,697,488,720,179,160,19,418,807,308,659,149,27,90,431,4 }, +{ 961,13,569,37,197,23,474,94,148,79,12,629,401,111,901,341,15,51,165,405,734,873,99,4,413,188,134,89,10,11,358,152,179,117,308,115,40,59,72,468,245,139,457,33,153,247,365,477,205,277,339,317,77,758,5,38,498,76,579,453,216,180,0,3,542,762,36,490,2,177,102,143,482,217,196,471,14,452,128,437,55,1,54,304,22,210,9,377,64,638,202,17,21,488,349,431,300,775,528,485,32,7,141,318,372,86,8,509,157,45,291,497,137,184,6,864,402,670,275,880,403,110,543,328,529,875,90,657 }, +{ 102,33,51,77,23,40,128,202,64,13,141,139,196,318,403,153,269,117,99,137,22,4,453,134,59,115,12,10,11,475,0,14,86,65,95,180,15,2,120,1,210,494,342,28,18,165,352,6,36,49,177,217,300,260,82,719,401,421,24,402,386,21,5,457,7,197,730,3,125,37,450,19,291,48,523,17,351,237,27,114,326,93,50,111,38,341,317,25,285,98,26,277,110,54,194,46,9,391,361,275,16,44,31,961,32,308,96,349,899,34,133,854,569,29,485,757,108,105,283,67,144,373,57,509,678,498,221,116 }, +{ 24,35,1,67,0,28,18,16,103,9,68,69,50,65,83,149,12,5,71,133,14,7,22,114,75,96,2,55,189,218,108,48,21,54,223,175,281,251,181,23,46,58,37,124,101,51,336,238,36,70,13,154,118,163,418,10,99,60,745,766,137,537,39,128,38,335,202,141,487,49,361,64,32,45,115,486,155,765,283,109,502,345,230,140,87,430,171,29,269,398,30,267,95,221,543,92,86,820,236,217,6,412,285,17,160,302,77,177,596,61,318,180,458,488,31,52,540,165,352,4,237,105,920,545,125,657,705,273 }, +{ 384,617,940,911,959,855,332,206,282,434,244,253,141,268,352,93,120,144,10,269,137,110,160,201,417,36,143,450,116,203,260,44,811,31,717,951,55,295,318,64,187,261,373,567,202,276,489,258,326,498,98,642,559,180,473,127,22,728,860,217,1,661,219,499,86,23,851,638,8,197,692,341,13,531,165,224,351,523,817,115,9,854,164,125,752,365,28,533,367,51,922,15,564,131,21,96,317,128,82,5,662,699,843,105,871,372,615,237,0,83,386,30,864,509,242,800,901,284,291,551,77,2,776,349,7,704,37,574 }, +{ 93,30,203,473,326,704,137,652,450,31,800,269,848,160,127,190,274,144,254,219,187,625,349,303,166,352,120,372,116,521,224,110,131,752,435,88,217,202,141,260,291,77,367,156,340,228,822,744,5,382,197,296,658,438,180,863,410,44,730,698,578,317,418,1,859,165,850,829,735,494,102,795,616,159,264,921,98,960,373,36,100,23,13,341,128,21,64,258,10,0,523,330,318,39,717,321,778,709,7,968,125,919,942,177,49,86,158,9,365,225,901,478,105,686,531,33,924,888,851,96,2,363,371,442,385,747,691,615 }, +{ 53,73,27,250,26,200,19,297,630,714,472,611,42,122,235,25,17,34,6,436,65,684,106,680,690,80,14,256,113,322,57,813,85,108,64,45,59,648,283,4,161,194,573,445,236,389,128,862,444,114,624,390,469,78,56,51,141,133,2,90,22,84,11,176,491,0,18,255,495,453,416,32,3,412,23,47,145,137,63,29,665,123,95,269,46,378,62,61,614,483,86,361,814,10,5,605,136,396,216,177,112,50,249,619,730,77,129,119,503,13,432,202,179,12,719,285,318,24,597,102,151,183,76,21,130,742,37,209 }, +{ 15,2,1,5,7,3,0,6,141,180,10,4,13,33,11,352,77,197,9,217,14,134,64,12,341,317,365,165,752,40,498,372,269,102,23,17,115,117,291,8,349,22,51,153,196,59,32,237,16,901,36,120,21,260,202,342,961,128,569,143,82,57,864,37,45,318,873,110,93,111,457,18,72,391,386,76,28,86,351,125,137,509,95,31,56,954,326,450,79,48,27,854,139,29,482,19,38,44,546,89,373,99,49,244,90,55,84,719,523,94,403,35,177,54,579,453,277,24,164,922,41,253,30,203,730,61,20,58 }, +{ 254,530,960,613,39,829,688,221,406,190,166,475,827,438,30,9,669,269,180,23,98,51,31,228,21,541,202,274,141,840,318,131,938,105,48,158,127,125,96,44,846,137,453,49,393,219,352,116,13,1,679,421,856,625,286,317,36,308,100,333,310,403,115,890,144,217,791,101,931,616,569,10,386,551,401,0,876,272,910,55,258,88,102,77,327,656,224,629,252,64,468,731,160,385,860,518,926,28,697,344,326,159,33,5,197,93,944,627,128,165,816,293,574,455,187,765,371,364,843,404,242,120,22,284,95,32,345,506 }, +{ 39,9,166,28,158,35,101,30,175,364,68,168,190,0,67,346,194,100,49,88,131,36,333,420,274,310,570,344,404,252,83,289,706,109,55,69,208,1,86,160,137,610,96,65,967,251,438,352,637,336,114,16,577,159,128,254,756,154,345,219,22,64,230,799,228,810,95,24,51,747,224,185,141,818,149,584,768,202,103,419,429,21,826,752,124,530,857,31,656,10,13,169,323,505,237,143,865,562,189,165,793,238,694,535,23,217,874,455,164,116,105,7,197,127,44,547,125,286,180,213,868,539,71,2,703,272,498,115 }, +{ 62,555,548,56,3,507,161,440,34,215,527,136,514,162,361,4,211,17,130,328,383,757,14,84,183,123,205,152,494,11,184,57,585,38,236,202,377,59,54,504,452,129,402,128,176,556,229,880,80,372,300,381,495,40,33,378,37,469,106,266,472,119,12,214,497,248,102,803,403,342,249,73,53,77,245,412,313,51,196,27,153,543,257,485,210,471,76,283,488,99,305,200,421,13,139,157,23,349,432,79,45,90,339,32,117,240,46,179,714,457,292,108,134,250,719,318,41,217,64,21,22,369,710,188,72,133,277,453 }, +{ 126,107,92,356,493,279,359,7,246,97,298,362,392,441,508,60,199,35,423,16,146,132,121,14,43,230,501,232,939,20,649,150,173,5,779,660,414,607,672,314,590,259,534,21,66,140,729,683,387,191,18,71,941,955,953,32,104,49,48,22,38,0,46,319,640,95,819,172,147,167,24,357,28,10,2,13,858,603,324,195,68,598,12,1,345,595,426,61,64,653,522,407,612,470,4,885,77,23,52,236,125,37,33,702,31,687,581,6,118,74,98,711,399,212,40,75,70,376,465,517,360,135,271,105,102,50,192,156 }, +{ 291,141,349,373,372,82,15,260,351,120,922,854,326,217,318,77,450,1,352,898,93,269,523,102,202,21,203,144,13,64,23,304,5,752,137,437,32,51,473,98,704,247,180,177,317,237,197,342,59,457,947,817,494,316,76,33,719,615,300,961,12,36,968,954,475,386,367,268,165,115,86,83,69,67,48,39,22,2,851,341,275,210,201,110,662,576,418,403,391,365,284,241,194,116,105,96,88,54,44,31,7,730,485,205,125,40,117,875,873,864,661,579,498,490,482,468,393,332,308,244,221,190,185,169,160,128,109,100 }, +{ 141,217,352,115,269,180,317,752,341,244,13,291,21,77,23,197,365,5,372,165,51,64,237,349,48,9,4,304,482,102,32,864,28,318,247,137,11,202,308,33,39,98,7,3,1,498,351,125,2,468,437,110,10,82,490,253,120,100,88,954,515,177,93,901,31,12,401,201,0,49,922,72,203,569,55,30,6,342,260,143,22,117,898,717,44,89,35,153,144,854,128,36,386,350,101,68,196,40,373,221,134,96,206,873,391,316,288,241,164,160,105,16,90,579,403,326,645,629,523,421,284,116,65,61,961,59,24,18 }, +{ 78,47,390,108,130,711,813,19,453,412,27,283,730,444,690,196,250,123,236,128,389,503,200,650,183,14,59,4,26,65,472,133,73,34,381,297,153,51,177,57,269,11,53,318,33,216,719,17,194,630,680,322,619,114,113,368,714,46,684,803,432,56,77,29,657,6,480,141,117,3,445,22,603,122,42,235,84,361,90,899,13,64,23,102,249,2,754,524,605,457,99,63,0,475,349,789,40,38,588,210,18,152,106,134,678,436,24,10,25,597,50,529,291,594,401,52,245,95,402,894,129,266,86,471,339,119,523,137 }, +{ 250,297,34,80,472,495,311,64,53,17,667,61,148,27,56,483,469,136,90,73,141,161,45,243,194,3,106,597,527,176,128,751,352,151,436,62,86,874,738,4,209,153,833,491,881,614,396,32,383,84,26,76,445,231,95,5,659,14,887,237,29,378,573,431,947,196,405,58,23,255,119,117,65,11,113,59,33,719,134,814,292,287,57,177,721,240,12,585,611,247,269,51,22,283,200,498,2,583,304,180,19,145,312,165,678,205,202,10,835,349,72,217,114,514,40,555,315,588,137,130,37,875,389,197,207,179,0,13 }, +{ 51,23,551,421,453,13,33,102,77,21,326,153,401,958,308,120,615,196,5,48,202,221,115,692,523,791,403,899,342,4,801,475,210,805,260,107,450,291,966,93,730,576,32,117,842,541,468,18,1,16,203,116,110,697,60,96,126,121,386,68,31,40,2,494,259,125,463,407,137,154,24,704,427,373,218,180,92,10,575,244,35,104,64,175,302,628,351,318,197,160,98,71,817,770,743,670,612,12,859,158,17,590,199,650,7,945,815,752,346,949,851,717,642,597,473,439,402,367,253,252,217,146,56,44,6,3,36,847 }, +{ 98,223,393,363,411,834,478,664,1,691,791,284,156,447,724,914,759,697,293,354,541,807,51,586,421,48,948,668,765,848,264,195,9,163,658,23,801,18,125,551,21,386,96,340,772,909,682,963,730,698,31,453,360,13,371,949,905,770,520,5,621,781,221,744,271,296,116,202,647,857,132,70,118,335,821,44,656,842,367,897,719,121,167,446,32,69,457,450,318,101,100,670,379,638,127,634,144,137,410,761,662,217,846,758,146,401,932,615,75,777,883,538,87,165,33,225,936,591,286,93,166,269,197,812,326,141,105,919 }, +{ 232,7,107,92,14,46,135,172,359,220,314,278,60,5,171,192,2,526,16,620,21,262,52,75,35,32,126,294,29,872,24,132,18,77,246,87,448,571,230,48,1,54,12,58,38,236,50,606,247,86,23,70,177,112,461,298,121,380,97,71,37,888,217,392,66,279,150,356,155,64,13,20,441,108,678,304,751,572,147,226,115,334,140,61,291,785,153,739,579,43,451,178,146,186,947,437,387,394,90,51,330,195,194,875,307,216,352,597,587,357,117,0,269,181,45,603,349,210,199,137,415,128,267,95,366,315,156,31 }, +{ 32,76,2,241,21,14,292,1,476,72,48,5,45,720,50,179,270,214,46,89,90,29,148,129,464,155,17,290,519,38,227,460,205,123,245,350,243,358,52,184,54,257,171,710,249,408,7,400,186,221,405,513,3,36,162,618,12,106,37,288,181,57,502,413,0,263,10,428,316,99,281,207,6,229,4,58,152,64,115,425,430,769,67,741,119,59,22,128,659,308,773,183,23,202,96,86,431,83,75,62,141,9,524,51,16,103,328,645,69,237,497,255,209,217,130,11,24,496,666,28,137,403,528,18,247,536,452,77 }, +{ 15,77,33,102,13,23,51,117,4,32,11,202,40,90,0,1,5,141,153,247,177,128,134,214,288,21,137,269,17,453,3,318,291,403,8,110,82,180,10,59,165,494,22,6,342,197,457,351,64,475,260,9,93,2,120,523,326,48,31,217,115,57,421,386,12,373,44,196,16,19,719,730,25,139,96,352,341,36,961,49,210,317,349,203,125,194,401,18,275,7,24,28,98,817,285,898,365,95,308,901,450,30,99,661,372,86,223,65,854,615,922,402,56,899,697,758,208,103,14,237,116,105,569,37,304,221,704,638 }, +{ 66,74,135,69,6,380,278,267,97,14,2,29,1,75,24,87,324,52,150,38,335,138,357,189,204,172,67,103,25,388,85,7,462,220,83,423,108,550,149,71,603,42,733,571,696,174,26,582,140,36,458,565,16,808,895,802,486,46,238,35,19,70,298,21,18,114,65,226,22,86,60,418,32,251,192,92,64,451,280,273,500,309,95,594,222,501,12,50,5,145,563,398,353,0,889,28,443,48,607,711,641,517,112,262,336,348,37,125,580,596,906,705,622,572,10,160,81,43,63,54,58,133,792,68,620,124,165,294 }, +{ 152,471,4,339,59,188,79,77,11,377,128,94,529,102,33,202,402,111,12,99,51,216,542,452,291,40,474,342,210,196,13,141,184,64,403,37,453,153,134,318,17,177,117,494,23,961,139,5,32,76,431,3,300,257,205,757,497,115,401,275,269,10,229,179,72,475,21,2,86,90,14,57,89,349,1,0,180,157,285,277,148,629,137,266,197,38,36,304,45,217,485,6,498,678,129,405,700,95,569,245,437,734,247,460,22,413,313,352,19,372,775,308,421,292,880,48,237,143,769,503,358,873,488,54,7,618,710,386 }, +{ 15,1,196,0,33,13,2,77,5,10,9,102,217,23,7,6,3,12,117,14,4,134,177,153,40,11,457,291,210,275,22,730,16,115,18,165,21,386,341,719,120,82,180,64,197,51,141,523,317,8,569,365,450,202,32,961,372,260,28,349,342,125,352,95,37,31,899,351,17,128,318,269,48,110,49,403,19,93,901,59,475,401,72,326,24,139,36,65,111,61,237,758,494,90,99,30,854,304,817,44,482,137,391,56,509,898,437,25,308,752,98,38,629,247,76,922,421,86,57,373,670,26,253,70,203,221,801,579 }, +{ 209,106,17,207,45,255,119,655,5,742,306,378,61,62,136,425,263,80,311,84,240,29,790,2,58,56,186,32,3,383,738,243,454,592,869,76,151,14,179,129,290,123,48,72,440,148,21,52,292,64,90,504,287,667,214,46,1,128,54,408,507,50,476,34,27,833,549,57,38,548,155,227,162,86,241,19,70,152,89,205,6,334,171,26,496,483,112,183,181,141,130,400,249,59,245,266,184,555,663,75,270,358,77,229,361,37,215,95,445,4,297,10,211,25,33,666,519,881,11,257,202,73,710,269,237,40,221,180 }, +{ 120,450,260,817,523,15,23,1,51,326,98,13,202,82,125,64,5,351,318,21,31,269,457,32,116,93,115,165,102,77,33,12,95,922,719,30,494,110,44,615,128,48,14,180,141,36,2,730,61,9,137,342,403,899,197,86,49,10,704,551,386,237,8,203,96,83,67,37,7,177,291,352,90,22,18,16,402,196,153,373,179,144,76,39,17,6,961,854,475,898,692,401,241,207,148,105,100,65,50,20,453,349,344,3,217,901,678,341,293,263,247,210,99,88,69,45,40,11,958,801,317,132,126,107,35,24,752,661 }, +{ 116,268,492,918,93,206,203,940,23,51,551,417,523,499,473,959,22,421,8,125,791,692,730,120,31,401,949,10,0,13,899,165,44,386,450,260,867,453,64,9,629,115,36,719,559,341,728,170,55,137,599,299,197,615,569,180,457,28,391,141,1,326,685,301,564,479,475,338,506,86,509,105,638,202,95,817,308,242,253,762,244,2,373,801,670,127,901,21,49,870,951,5,558,237,143,269,37,752,239,12,642,922,403,873,7,352,4,758,365,318,128,18,965,456,32,320,221,14,805,512,317,498,961,16,704,98,33,898 }, +{ 15,13,165,23,197,4,0,1,180,12,11,117,5,8,82,901,719,341,2,120,51,9,523,260,509,7,3,730,141,386,10,450,351,6,33,752,758,134,115,498,352,961,317,365,153,898,14,899,854,64,217,922,873,22,569,326,93,128,817,31,269,237,291,202,21,110,391,37,801,615,28,318,17,457,44,16,77,18,32,98,99,36,49,30,349,401,453,403,102,38,373,958,35,629,116,40,137,125,55,19,482,203,304,242,86,546,474,96,24,144,65,308,475,139,143,247,210,402,89,54,69,177,196,48,485,661,83,734 }, +{ 14,24,69,66,7,108,2,398,267,150,67,97,189,29,149,74,38,71,36,6,83,52,273,388,140,1,135,18,46,172,602,65,28,124,429,302,154,380,336,451,547,16,278,114,732,68,484,324,75,733,92,175,103,118,22,462,0,25,251,357,86,596,55,160,87,705,594,35,60,531,133,467,423,711,236,95,650,109,458,443,298,21,886,5,50,138,26,12,505,238,168,309,314,42,32,10,646,220,49,70,335,192,232,906,420,204,48,20,64,37,43,603,13,19,533,96,85,23,565,51,330,226,550,221,570,208,283,54 }, +{ 330,335,96,839,523,662,367,443,615,418,141,403,717,244,692,576,922,372,1,352,269,217,752,349,64,498,36,854,966,180,822,831,954,873,864,317,284,291,776,201,318,144,69,98,351,678,7,21,5,160,77,203,83,82,442,137,551,2,260,223,622,187,494,44,13,385,128,202,23,475,75,341,901,48,31,437,238,304,247,230,765,93,373,120,393,22,67,251,371,293,125,661,342,859,285,898,163,365,30,32,450,29,110,459,326,934,51,10,197,842,851,399,246,189,800,421,237,0,37,116,221,165,16,579,127,9,321,88 }, +{ 184,257,205,90,245,229,769,524,152,432,497,619,17,57,5,452,32,45,381,266,106,2,59,21,179,4,292,217,313,460,288,1,214,129,14,76,77,3,128,216,48,207,471,339,157,141,710,11,202,38,64,519,369,6,61,209,12,37,119,79,99,123,29,317,72,188,7,62,255,33,58,162,269,247,40,54,102,115,177,51,618,372,476,318,10,89,137,86,249,148,241,263,180,316,352,402,50,403,655,155,19,210,23,94,136,291,308,349,358,237,453,377,111,56,139,425,365,27,341,46,143,277,401,153,588,221,0,186 }, +{ 7,14,16,46,107,32,35,24,70,60,23,307,172,5,18,2,38,87,92,58,13,21,20,10,177,51,202,77,135,48,37,128,0,97,232,115,171,112,126,43,9,220,140,318,12,141,102,71,852,155,4,54,291,22,90,1,269,52,163,33,137,298,192,28,66,64,217,356,61,132,294,278,359,147,349,461,95,29,45,609,448,121,259,186,314,76,474,304,362,17,108,6,150,415,59,400,780,146,36,342,31,275,67,86,246,402,334,279,678,75,247,50,620,30,409,502,39,372,72,49,230,40,494,227,69,465,872,68 }, +{ 140,66,97,172,92,7,267,189,14,314,16,69,298,380,60,135,451,230,150,35,24,71,74,357,423,232,330,38,324,246,192,443,278,563,2,572,877,550,462,126,448,52,893,508,67,501,6,568,29,21,603,46,565,607,83,388,204,633,22,279,18,149,723,733,399,65,48,1,95,64,526,915,5,32,726,28,75,36,841,25,350,87,138,108,68,500,895,10,43,0,251,808,125,104,12,426,49,81,50,20,37,309,517,674,86,802,174,107,103,653,173,582,220,226,85,387,26,55,712,903,241,197,121,237,262,54,711,191 }, +{ 121,167,354,212,132,407,191,107,147,146,271,199,101,446,126,647,195,35,259,18,16,60,55,20,590,465,43,411,68,363,178,198,142,562,9,427,463,92,640,14,346,7,727,798,941,520,364,325,815,5,581,13,1,621,21,23,522,534,0,589,279,158,28,806,706,125,439,109,98,97,118,48,22,694,955,393,604,362,150,932,608,51,105,33,252,165,234,265,108,49,598,156,96,419,818,10,467,197,154,493,12,765,376,120,70,786,169,777,967,102,687,38,202,32,682,246,64,39,31,447,781,612,58,159,61,260,40,450 }, +{ 76,90,460,205,179,257,245,32,288,184,21,45,769,358,316,524,229,241,152,5,57,48,497,2,476,148,720,619,452,266,72,155,1,17,38,432,14,54,243,290,292,227,449,381,12,4,89,247,50,29,313,645,270,59,350,11,129,216,51,217,106,157,221,128,99,58,37,471,64,188,77,214,408,339,141,115,186,3,369,23,52,202,79,46,428,425,308,6,317,464,7,255,405,162,413,33,171,519,496,372,365,341,269,401,10,0,94,36,137,875,318,263,237,62,588,352,123,474,181,431,40,177,291,180,102,61,207,618 }, +{ 147,259,427,178,798,581,878,786,198,534,465,325,929,142,362,20,376,43,107,771,35,481,132,470,234,5,626,146,590,279,97,522,49,70,77,345,21,199,640,450,612,202,13,48,14,102,150,0,33,725,595,846,941,23,22,7,58,195,423,660,501,121,687,126,518,260,117,904,120,858,953,39,342,60,455,1,196,165,125,158,105,159,246,92,28,153,10,31,360,315,346,271,761,683,603,212,18,298,128,711,167,379,318,113,623,817,95,32,291,61,16,134,72,359,108,221,197,115,98,163,767,523,493,191,329,30,87,50 }, +{ 473,778,450,203,855,144,93,617,30,704,141,466,530,330,372,459,201,326,934,321,88,839,254,36,505,403,128,559,83,120,523,217,86,213,131,613,717,110,332,728,344,251,264,352,686,434,82,39,578,317,206,35,625,435,44,795,840,961,539,274,244,116,137,55,662,265,228,863,577,406,96,924,601,564,417,384,351,341,225,95,49,28,800,418,21,831,719,531,367,219,202,31,64,822,224,335,960,968,922,854,829,688,365,197,22,160,410,165,873,752,716,518,456,347,242,125,731,269,180,948,498,478,349,237,98,77,33,9 }, +{ 523,475,899,730,453,51,23,403,719,13,421,457,33,386,196,817,801,4,120,117,153,11,450,260,102,202,515,361,670,165,758,494,125,108,128,961,283,236,99,21,3,880,5,401,32,59,77,543,615,10,6,318,402,221,183,137,650,57,40,7,2,1,90,847,412,247,241,210,207,115,12,208,181,150,133,114,63,42,467,775,389,342,111,56,39,31,30,28,22,9,0,576,558,304,229,216,177,152,123,113,47,29,25,958,554,391,629,968,967,966,965,964,963,962,960,959,957,956,955,954,953,952,951,950,949,948,947,946 }, +{ 1,5,15,2,13,0,7,23,10,3,317,341,6,9,4,365,11,115,33,180,217,77,17,64,197,165,102,117,141,40,14,21,51,32,134,12,8,291,901,752,16,569,22,509,352,237,482,372,143,18,498,153,457,59,128,349,269,125,202,177,961,546,31,391,342,196,45,36,873,57,38,28,37,86,48,386,24,19,139,318,111,95,29,44,403,27,719,304,120,41,110,629,93,72,164,275,61,55,253,99,56,90,49,137,730,210,864,758,35,260,76,50,579,116,94,300,221,26,25,277,326,98,54,475,453,30,79,244 }, +{ 19,119,40,4,27,84,77,56,202,73,102,59,757,33,485,26,300,210,504,494,128,80,862,23,402,63,42,139,880,342,129,32,5,361,25,13,196,51,12,11,2,99,21,6,153,469,123,10,710,53,65,618,64,161,117,736,90,38,22,106,17,803,95,76,0,45,37,14,548,813,250,245,108,297,453,378,162,57,113,460,403,611,255,205,256,383,217,1,235,114,86,742,209,152,292,630,576,240,343,790,667,475,16,48,133,122,18,136,134,85,115,15,283,34,179,436,180,78,29,9,200,605,561,318,49,269,684,495 }, +{ 32,20,13,21,2,38,5,43,29,23,61,12,52,115,90,6,202,37,64,48,95,207,51,81,66,7,25,49,17,74,26,76,178,147,98,46,362,54,22,63,19,14,0,259,128,577,125,97,58,91,77,56,182,65,197,465,376,27,141,427,316,1,241,291,113,878,217,247,312,137,120,4,858,481,142,595,165,31,237,93,230,379,534,482,180,116,86,72,42,11,44,203,263,470,356,352,279,102,84,40,33,687,626,835,786,107,683,260,18,318,24,414,298,468,367,326,304,216,117,105,99,3,933,612,415,347,501,344 }, +{ 339,188,11,377,79,4,99,94,12,542,102,111,474,37,51,33,471,453,152,40,475,342,77,403,59,196,277,139,153,57,134,117,13,775,229,128,401,38,880,23,210,64,184,202,431,115,961,157,300,3,266,72,485,402,89,629,21,54,15,129,177,76,14,162,32,17,216,10,148,275,269,5,757,452,361,477,141,90,205,313,569,494,2,65,179,120,257,381,421,237,197,405,86,488,328,36,318,48,22,137,165,413,50,308,0,1,341,130,82,95,49,457,161,543,245,18,304,260,62,45,285,180,291,41,529,498,125,56 }, +{ 128,283,503,34,318,529,432,176,495,275,231,412,527,291,402,161,381,202,59,3,389,469,177,803,53,585,216,14,269,57,200,17,73,472,27,56,250,136,678,4,152,51,862,361,80,245,11,789,619,194,123,719,401,23,714,84,507,210,77,12,65,322,106,99,436,130,129,46,62,2,13,38,162,369,236,108,33,183,133,457,153,64,483,211,297,741,514,403,114,18,629,961,378,555,440,119,383,597,475,29,453,229,40,165,240,215,196,655,611,556,21,249,117,54,667,134,102,308,814,19,50,86,391,22,0,197,95,26 }, +{ 15,217,13,341,317,141,365,115,23,51,180,349,291,1,120,77,260,269,202,137,21,5,450,352,373,110,0,372,64,33,36,128,177,4,318,351,102,9,197,2,304,854,10,901,7,12,32,165,326,752,40,244,922,160,96,22,523,117,11,48,16,86,661,90,457,82,386,494,403,308,59,28,55,253,247,18,498,134,8,143,153,221,14,6,83,30,196,93,3,37,615,453,401,898,421,817,24,95,961,285,475,719,873,17,35,39,391,730,125,67,237,49,98,811,704,642,717,216,203,69,482,99,342,402,851,50,57,569 }, +{ 13,961,15,4,11,12,457,51,3,37,2,197,5,10,569,99,115,23,6,72,89,14,0,1,134,7,59,33,40,38,165,111,196,76,405,79,139,117,148,498,873,339,719,300,277,485,17,471,157,152,128,21,94,431,32,177,188,509,474,64,45,54,482,730,153,36,629,386,41,22,28,141,901,864,18,180,77,523,9,401,678,437,102,304,210,308,16,775,579,48,31,317,179,413,365,758,352,57,24,341,86,752,546,137,58,318,245,44,27,402,95,899,19,108,269,216,202,49,184,110,93,954,880,56,55,452,358,8 }, +{ 15,13,1,2,0,5,10,23,33,9,7,3,4,6,12,102,115,14,11,217,51,117,77,40,569,153,341,196,317,18,457,22,8,16,961,197,719,180,64,365,165,730,120,134,21,141,899,202,82,31,523,291,125,372,386,28,17,139,93,349,128,304,269,177,110,482,32,342,111,37,318,95,453,579,36,901,59,24,352,450,44,437,137,403,49,260,401,210,237,247,326,817,494,98,99,57,752,19,48,203,30,86,351,485,758,116,402,391,26,898,308,38,27,25,275,801,498,854,65,56,629,76,90,300,922,468,144,35 }, +{ 76,292,5,214,45,32,2,519,710,460,129,179,29,1,90,58,123,249,464,207,205,618,17,14,245,257,72,61,48,263,106,21,358,425,769,241,119,52,184,6,162,186,50,46,38,7,655,148,209,243,128,255,3,155,288,183,290,64,141,454,227,89,77,476,54,115,4,25,59,19,57,229,36,202,269,408,152,99,70,151,130,86,177,75,592,216,720,62,318,12,217,40,26,10,37,27,496,306,33,247,524,311,316,56,497,350,84,51,137,171,790,102,270,23,0,112,11,22,16,452,334,237,432,402,96,180,95,449 }, +{ 76,32,72,21,38,4,54,89,57,17,179,12,14,99,148,45,23,59,51,90,488,3,11,241,401,2,428,37,543,152,205,528,453,128,5,421,13,115,129,77,10,474,245,184,350,670,449,413,1,569,137,79,403,629,475,328,308,221,155,464,0,361,123,743,202,460,214,257,33,40,64,794,292,165,405,770,805,41,476,62,162,270,229,358,86,285,36,402,775,316,341,94,197,50,576,106,431,720,46,29,540,217,139,773,119,838,48,15,216,181,657,177,290,514,288,7,457,227,961,9,22,102,247,828,6,141,365,180 }, +{ 98,223,393,1,834,284,791,264,697,724,293,909,772,905,541,478,51,821,807,363,897,48,682,421,765,447,411,730,801,410,163,948,386,96,9,31,963,23,21,691,664,453,125,5,382,100,13,521,949,551,656,44,116,770,686,670,639,221,39,367,914,265,166,144,719,335,812,848,371,0,101,842,318,321,127,88,252,520,662,329,354,32,759,438,33,698,93,202,593,201,286,156,450,658,281,115,165,49,401,120,604,586,82,2,22,36,225,217,77,744,10,498,141,102,615,180,118,435,69,30,921,404,326,197,433,475,83,70 }, +{ 341,13,23,509,762,638,569,242,901,629,165,8,197,873,10,961,506,456,391,499,116,338,180,457,492,206,12,51,115,268,143,365,951,1,93,965,642,867,417,37,2,253,546,64,5,203,3,498,758,752,719,0,401,15,352,44,31,7,36,244,86,4,141,386,734,237,6,22,308,730,55,844,164,918,473,326,125,82,564,949,898,551,940,791,120,38,9,260,523,137,661,373,559,110,14,854,959,615,317,11,54,33,102,692,269,99,351,903,89,870,740,217,708,170,824,40,801,728,864,817,717,160,128,28,117,776,479,77 }, +{ 818,230,755,443,352,420,1,2,244,50,14,5,38,46,29,36,114,218,65,96,22,133,52,181,58,0,17,28,21,103,75,54,45,86,67,175,48,171,7,12,323,137,24,285,208,51,487,6,283,18,10,70,23,155,32,83,194,361,3,503,163,389,328,128,223,37,803,532,16,19,168,735,69,13,124,61,202,160,186,433,9,115,484,27,236,514,238,782,95,25,754,108,64,657,112,141,281,220,412,289,55,756,401,394,584,151,308,49,545,851,822,4,227,717,866,334,269,646,403,402,26,400,87,35,745,366,99,143 }, +{ 481,878,32,202,5,21,23,269,13,318,494,142,77,51,70,1,182,557,141,137,33,291,58,178,415,465,61,48,590,259,922,115,2,929,640,403,207,884,4,260,120,87,817,626,534,349,342,102,195,0,132,351,59,427,252,109,35,7,961,678,345,247,153,72,845,158,169,49,12,10,6,196,112,798,450,687,279,841,713,475,457,128,40,146,28,3,416,290,265,75,767,854,771,947,795,753,704,637,631,597,592,581,546,509,493,354,352,341,326,316,288,285,241,237,212,197,159,90,54,52,46,38,37,36,17,14,39,9 }, +{ 15,1,5,4,2,13,11,341,0,3,7,12,10,6,21,365,9,180,32,134,317,197,33,23,37,117,40,165,17,77,217,498,8,752,153,901,64,102,569,141,873,51,14,961,196,115,59,110,352,509,22,128,57,111,120,143,125,72,38,48,372,36,291,308,349,99,139,31,90,202,260,269,457,44,16,89,76,49,55,401,342,237,27,93,177,94,82,18,45,29,326,28,19,386,221,351,137,86,277,546,391,41,50,35,403,453,54,164,318,719,474,629,373,203,98,95,475,854,253,304,148,24,450,244,56,758,542,300 }, +{ 25,26,119,42,19,6,27,45,122,84,469,4,17,790,814,113,80,76,59,73,483,2,29,611,128,32,202,40,1,77,90,862,106,56,209,33,240,0,684,136,102,14,292,416,5,311,11,504,196,318,123,680,378,255,12,129,161,22,690,151,53,117,63,179,667,10,85,72,61,115,65,210,402,396,57,141,64,614,134,757,153,137,62,18,99,445,51,269,66,177,7,3,24,710,256,648,36,655,235,38,23,306,58,618,70,491,48,454,21,403,742,361,49,34,86,494,275,383,342,28,300,145,13,74,112,52,297,245 }, +{ 14,7,24,2,1,16,69,6,38,67,108,22,65,28,124,0,46,18,29,86,66,35,83,5,71,398,36,12,168,289,140,95,610,189,150,149,10,21,92,756,52,302,23,109,97,20,584,25,50,267,194,64,49,51,32,60,103,55,505,13,429,172,128,273,420,158,37,458,19,547,118,48,75,115,137,74,135,9,185,164,143,388,154,202,26,701,650,251,133,336,17,114,169,68,230,236,43,54,141,244,671,253,703,105,208,90,3,160,159,278,99,462,314,403,380,87,389,175,357,324,484,318,232,708,125,799,451,886 }, +{ 128,51,202,23,141,120,403,15,64,13,318,82,10,269,99,33,12,197,475,453,260,153,137,165,196,0,3,402,351,5,494,1,22,14,110,117,180,102,93,17,2,31,37,38,125,9,326,450,49,21,86,44,401,457,7,6,36,352,115,18,77,523,237,28,134,95,285,386,421,719,32,48,342,98,11,54,203,730,57,341,961,373,139,8,854,361,757,111,898,45,4,40,308,194,65,16,922,277,41,880,678,116,291,24,498,19,27,391,143,56,217,899,59,569,30,79,365,775,50,503,208,55,144,817,61,105,901,94 }, +{ 16,24,35,18,71,68,9,0,65,189,1,7,133,50,69,149,60,28,103,101,108,537,67,118,230,75,114,23,335,55,167,21,124,92,12,191,181,433,13,39,140,5,14,48,223,64,212,96,51,218,443,281,487,154,141,163,22,217,99,37,70,565,2,115,49,267,58,54,394,766,46,246,545,83,30,128,10,330,820,202,32,361,137,622,125,238,237,100,121,77,165,412,175,87,143,352,399,171,317,86,126,745,146,486,418,177,36,291,269,318,283,336,105,302,398,695,4,98,197,180,31,345,251,271,109,309,430,38 }, +{ 50,2,1,46,14,29,38,52,171,67,5,58,103,96,36,24,218,133,223,83,114,236,45,70,54,181,163,186,108,155,75,28,65,238,334,151,18,433,160,745,328,0,366,112,532,48,516,17,514,61,832,400,281,21,227,22,760,19,6,69,754,766,412,12,7,309,545,409,27,361,283,137,16,220,675,480,55,86,765,822,502,552,32,10,307,99,956,285,25,113,9,398,323,37,51,800,26,388,394,64,891,128,124,243,549,828,3,458,194,208,35,646,95,262,23,311,150,966,820,866,894,149,13,115,87,430,80,287 }, +{ 21,32,5,76,72,14,17,2,3,38,54,89,45,51,1,12,23,4,10,48,90,137,36,148,221,401,22,0,421,179,115,86,99,543,11,453,13,37,128,270,46,241,285,29,290,64,428,57,6,361,202,59,540,50,403,328,114,475,528,65,155,449,77,308,794,569,181,7,805,27,40,141,292,133,33,576,263,129,514,205,341,9,413,350,208,19,838,629,323,28,430,480,119,657,474,214,741,123,245,402,102,720,15,770,476,408,494,56,425,106,269,62,207,775,243,152,477,670,180,318,197,405,165,95,58,281,18,697 }, +{ 434,384,268,144,940,855,617,206,959,911,332,116,93,203,137,489,282,244,141,120,36,44,160,269,253,367,110,450,10,127,202,276,417,219,98,258,326,131,260,31,64,373,201,9,187,318,692,473,22,224,523,817,143,86,352,1,55,30,23,51,28,295,567,105,559,499,261,717,811,0,125,951,180,8,115,13,217,128,551,39,165,49,96,341,284,77,21,851,728,564,662,498,317,386,752,48,351,531,5,642,365,197,83,661,237,615,800,291,242,15,170,164,494,82,95,638,704,349,37,854,228,7,32,24,935,968,35,16 }, +{ 2,5,1,15,7,13,0,10,6,3,180,217,23,9,141,11,317,4,341,17,352,117,197,14,77,33,64,165,134,365,115,12,102,752,237,40,498,153,291,32,372,51,22,21,8,349,901,269,16,196,457,45,569,128,36,143,59,57,509,125,27,38,19,342,86,202,864,873,120,31,546,318,110,37,28,961,29,164,177,95,48,244,111,137,93,391,18,719,139,44,49,82,386,629,54,24,482,56,99,26,453,260,50,579,76,25,304,35,98,253,326,41,55,277,210,72,275,30,730,437,90,523,403,203,351,61,79,450 }, +{ 113,25,2,29,6,833,45,61,145,7,32,74,151,0,42,1,210,128,177,5,19,4,72,14,64,106,655,59,22,122,26,112,174,66,40,77,738,209,525,402,138,90,148,20,483,104,684,141,51,76,17,27,425,18,21,416,23,10,202,95,16,255,52,13,115,624,318,137,102,311,33,269,85,81,49,887,87,746,544,491,275,86,119,790,454,690,65,24,9,222,216,665,291,117,80,204,58,243,134,48,476,814,217,46,349,287,70,28,285,43,348,36,84,342,11,757,194,91,207,315,31,38,73,153,192,355,135,300 }, +{ 116,268,918,551,203,692,31,940,403,791,421,206,417,93,499,523,299,44,8,473,23,51,805,22,13,450,120,475,202,0,959,730,386,125,599,326,137,9,10,401,899,105,1,576,817,559,453,564,165,393,494,21,28,36,456,5,127,949,64,670,728,638,391,242,685,2,260,762,457,197,180,615,569,141,170,253,308,963,49,115,341,492,55,143,719,479,873,37,18,4,86,7,77,506,48,244,801,867,95,318,269,901,512,98,951,33,6,301,958,642,338,30,352,870,39,221,365,12,16,395,373,24,237,629,164,102,159,239 }, +{ 107,612,126,132,362,146,259,199,279,660,493,590,121,20,939,683,534,43,147,941,376,35,595,953,649,581,60,470,640,955,356,522,5,97,195,150,407,49,465,16,92,14,7,687,359,298,22,858,246,0,13,360,21,48,18,427,271,167,423,191,10,441,325,392,32,598,929,28,33,31,178,414,1,77,232,173,55,230,156,125,24,68,23,95,345,4,904,626,105,212,319,98,786,783,70,61,202,142,44,702,58,71,40,314,37,198,51,64,2,623,163,159,819,93,446,158,59,346,357,12,501,118,260,46,663,116,120,102 }, +{ 15,13,1,2,0,23,5,4,3,77,102,33,10,51,6,7,9,115,341,12,11,217,342,317,14,117,40,22,365,180,120,8,569,18,196,165,457,141,21,210,197,64,177,31,291,153,16,961,59,523,817,453,719,450,134,730,93,260,352,44,269,275,49,386,401,32,901,110,82,28,17,629,125,349,139,475,899,24,128,37,326,111,752,95,202,36,318,403,372,498,216,90,308,247,30,98,482,137,86,237,116,615,300,48,65,203,19,57,351,55,391,509,99,38,734,922,468,244,144,221,854,304,579,373,39,143,758,148 }, +{ 53,27,161,17,469,73,378,527,136,250,383,19,862,495,62,26,56,200,297,106,84,585,440,80,34,14,436,128,119,45,4,3,472,123,129,813,504,240,483,361,59,444,205,65,611,5,202,548,108,255,742,514,283,78,814,113,179,162,690,57,402,183,38,555,714,6,245,390,122,54,42,803,184,25,61,33,630,318,209,12,194,64,77,684,11,141,328,757,503,269,99,236,2,102,389,51,22,618,176,152,257,453,47,21,48,210,86,680,40,381,148,133,217,880,650,215,46,10,130,377,18,0,412,497,23,114,13,229 }, +{ 209,45,17,106,207,119,5,255,454,243,263,425,290,655,306,186,84,29,62,61,2,408,136,58,742,378,21,56,311,240,3,592,48,32,383,50,52,80,227,790,270,46,171,14,72,440,148,287,334,76,659,400,666,179,504,496,869,64,155,241,129,1,123,54,38,214,738,75,663,162,90,141,89,221,151,292,57,358,237,507,513,130,34,548,152,70,205,37,128,183,211,184,667,41,229,328,476,266,115,555,181,86,549,405,6,281,163,269,215,12,96,245,11,220,502,720,257,36,313,430,288,180,833,40,202,165,449,27 }, +{ 152,4,452,59,128,471,529,339,79,216,11,188,377,77,291,202,318,94,102,402,32,33,99,5,90,51,269,76,349,12,184,257,542,474,17,177,111,141,37,21,342,210,23,205,275,678,13,72,403,38,285,3,2,40,64,229,588,57,10,769,494,48,134,45,372,196,117,217,129,6,497,1,148,460,757,245,453,475,0,137,153,157,89,139,7,115,961,292,179,498,304,524,266,197,14,629,431,477,405,775,288,437,180,15,165,214,237,86,401,308,119,123,54,22,221,120,9,162,27,619,300,62,317,316,31,488,277,880 }, +{ 15,1,2,5,0,7,3,4,6,13,23,115,9,12,51,180,8,317,197,10,961,901,11,22,77,21,752,33,202,141,110,341,14,16,217,134,120,102,31,165,365,40,93,269,457,352,64,44,28,18,37,351,260,117,569,82,308,125,32,196,49,36,153,719,128,482,318,99,17,401,326,137,450,403,203,111,177,730,386,291,55,873,523,373,98,30,237,24,453,89,48,95,221,475,39,59,116,372,349,105,144,139,509,143,72,96,758,391,898,494,854,86,342,899,210,629,57,468,247,90,304,65,148,83,817,421,160,20 }, +{ 951,752,638,811,642,253,351,180,10,197,341,901,873,352,110,244,143,762,165,8,898,55,564,141,15,365,854,499,82,137,417,498,509,36,535,922,479,559,206,661,373,317,282,115,31,22,338,160,93,569,164,260,717,217,391,86,533,33,44,1,28,9,242,965,728,120,116,77,64,968,21,102,203,125,37,117,506,30,13,386,5,4,531,269,40,911,153,23,0,169,384,2,326,372,105,196,851,170,134,109,704,332,268,349,96,127,48,291,32,758,237,318,24,177,202,35,11,961,401,39,308,83,3,629,51,210,12,434 }, +{ 6,197,350,74,262,841,509,26,47,115,13,196,583,903,165,4,235,138,50,308,21,130,749,324,675,19,38,331,391,145,711,569,33,75,241,453,734,64,153,223,52,558,236,468,99,220,34,70,112,11,117,546,237,125,3,27,32,1,42,134,409,51,78,25,163,29,14,648,23,645,180,820,46,2,85,108,65,773,802,624,122,394,341,785,22,730,95,386,606,57,412,247,923,316,66,498,357,221,630,133,457,59,853,90,53,226,873,632,48,113,719,629,45,889,17,307,141,766,69,12,37,444,83,171,54,600,5,765 }, +{ 125,391,165,867,558,386,221,758,23,457,21,51,13,197,401,308,963,115,961,97,719,77,791,743,451,603,949,48,180,670,770,341,89,120,730,352,291,148,450,426,607,304,7,403,393,66,64,350,316,217,134,135,14,421,74,247,177,50,380,24,696,236,172,87,629,10,141,475,144,35,901,801,523,453,138,509,467,456,241,210,150,98,18,565,357,267,103,582,545,342,301,278,237,216,20,16,99,69,903,650,551,412,389,283,81,32,298,739,543,498,370,365,317,309,300,260,170,140,116,85,52,37,33,30,25,5,273,203 }, +{ 14,1,46,2,24,16,38,50,5,69,58,35,7,75,18,67,54,48,0,155,513,171,163,61,103,45,29,52,430,270,37,223,21,70,12,28,9,780,32,267,60,290,83,227,71,36,666,767,22,189,140,807,10,72,96,65,108,181,64,186,220,309,17,335,92,394,23,592,243,89,536,663,115,281,51,6,474,55,68,112,141,400,128,133,87,13,425,99,741,236,95,675,278,230,76,221,114,86,263,113,820,552,502,27,269,202,49,760,537,149,262,328,408,90,366,137,150,207,77,357,315,307,218,241,19,148,25,66 }, +{ 23,51,13,115,453,12,0,2,4,10,403,1,202,5,401,475,15,21,457,59,77,165,9,3,37,99,32,11,197,719,308,6,14,64,7,569,17,629,961,318,180,22,33,494,120,18,730,141,40,102,269,217,177,57,16,128,523,450,341,8,90,421,386,237,28,48,125,19,117,31,247,402,72,210,899,221,260,291,134,89,342,317,365,509,38,24,391,216,352,468,817,153,54,36,45,49,27,196,56,326,82,65,76,304,349,139,50,25,86,275,44,137,482,372,670,498,26,734,546,62,95,758,29,41,46,93,351,79 }, +{ 129,84,17,27,56,495,80,378,19,548,123,162,161,53,469,618,73,26,504,106,40,257,205,59,3,183,184,862,4,136,62,6,128,202,14,77,11,65,769,5,527,108,45,57,383,361,2,497,250,249,200,130,23,34,22,90,402,483,245,240,12,51,214,38,0,139,25,99,64,318,102,152,32,210,300,10,297,757,21,494,880,775,114,710,111,33,54,29,460,403,452,36,42,133,18,61,86,141,436,177,814,269,13,194,119,179,49,28,342,453,217,95,115,514,229,556,76,137,37,1,742,283,485,401,503,488,475,24 }, +{ 28,109,49,9,22,67,168,158,39,185,159,420,1,55,0,83,35,95,194,272,86,105,69,103,36,114,169,327,50,254,208,175,65,584,286,323,535,346,251,345,797,30,131,756,374,12,333,533,128,2,218,68,96,133,344,160,101,23,64,289,7,597,189,24,646,577,874,896,13,5,601,51,46,503,531,837,16,238,747,627,115,14,347,10,98,310,789,137,124,21,202,127,164,578,54,799,141,154,230,532,547,31,312,708,285,480,37,810,637,596,48,143,336,100,389,835,252,439,237,38,518,912,219,424,44,239,253,868 }, +{ 242,391,456,116,23,13,492,8,51,341,401,165,93,206,499,457,867,338,268,569,417,509,386,22,1,762,479,638,10,629,918,758,64,551,203,730,901,719,12,31,197,791,115,5,86,14,125,949,0,559,37,2,642,951,873,959,237,961,692,55,170,752,870,120,44,180,940,9,141,564,523,3,308,36,7,4,15,899,450,365,728,137,21,473,253,898,558,143,128,453,740,164,801,260,903,6,685,202,403,670,965,54,114,28,854,326,817,844,352,95,82,498,11,923,244,98,299,922,239,110,38,317,105,599,373,661,160,99 }, +{ 211,162,248,130,556,57,507,4,305,266,183,41,152,361,11,229,129,471,62,38,377,313,300,514,77,339,184,157,381,59,128,452,99,3,328,40,202,123,54,210,440,494,369,555,342,136,117,542,402,757,64,548,139,23,196,188,880,457,775,33,153,56,14,102,432,13,177,205,257,497,17,12,37,291,51,89,403,215,961,111,141,90,349,216,488,269,485,803,477,180,165,115,10,475,437,383,79,197,95,21,84,134,503,421,86,453,318,308,1,670,76,194,719,618,106,32,629,15,730,65,22,5,245,94,214,45,482,288 }, +{ 92,7,97,298,140,16,126,232,314,60,246,279,35,173,230,71,107,14,43,508,423,150,779,653,441,607,66,189,359,501,24,362,20,172,104,387,356,21,48,38,18,399,199,330,5,674,192,877,81,191,729,517,74,672,426,939,560,633,0,267,2,649,69,121,149,392,324,135,462,22,95,29,46,52,125,598,37,723,707,49,10,357,68,345,6,32,830,98,64,1,702,380,118,28,900,12,695,251,861,885,493,783,603,146,65,715,61,55,278,36,236,273,138,33,75,25,31,124,175,221,293,83,165,763,712,451,414,319 }, +{ 15,1,2,5,0,7,13,6,23,9,3,12,180,4,51,115,8,901,457,117,14,165,22,341,82,120,141,10,37,197,961,31,21,11,202,351,64,260,77,365,110,16,134,28,217,93,269,33,44,36,569,18,719,308,450,317,128,102,125,137,32,629,153,98,401,55,352,30,318,373,49,116,898,730,48,17,752,523,196,237,386,24,95,86,326,403,291,99,453,40,39,72,38,177,89,203,873,391,144,247,349,498,899,59,734,90,475,372,54,494,421,143,482,304,854,105,758,35,210,65,76,244,160,216,221,83,45,546 }, +{ 403,18,0,102,25,523,64,6,91,85,42,74,342,32,24,13,51,306,95,49,22,33,475,151,681,880,421,128,899,750,397,214,122,757,730,77,28,519,288,247,23,5,817,805,317,879,861,673,273,177,145,21,120,52,283,81,275,701,577,453,104,59,202,193,697,636,401,287,249,216,207,196,180,165,138,125,123,118,90,84,60,48,43,587,260,389,16,10,4,1,351,326,318,933,804,635,498,280,237,197,186,172,153,148,137,134,117,99,61,29,9,671,289,958,838,814,579,312,238,124,65,56,847,719,654,650,457,424 }, +{ 56,65,554,33,561,18,84,123,63,64,343,194,0,90,49,22,6,27,19,289,240,129,73,659,431,214,475,523,370,368,233,899,701,287,257,95,85,28,102,183,736,650,467,256,153,25,207,124,108,80,32,24,120,26,389,874,718,635,51,525,891,752,751,730,648,614,498,496,450,249,237,209,184,177,162,114,59,45,29,235,77,42,817,497,444,137,112,74,53,352,511,681,630,643,196,947,942,847,764,719,696,651,615,610,603,602,543,457,412,386,337,326,312,288,262,260,247,227,226,179,152,139,134,119,117,105,86,76 }, +{ 127,98,31,293,299,395,44,9,105,0,49,599,284,28,276,116,242,685,159,456,22,763,125,935,272,492,158,1,623,393,109,144,201,39,36,95,18,51,24,320,137,23,286,55,16,512,916,489,202,21,662,301,268,367,327,371,35,761,567,13,434,65,7,101,918,616,2,5,141,737,506,870,12,64,131,68,30,48,900,948,86,128,295,10,219,239,269,910,403,627,338,578,143,14,37,124,160,203,318,384,170,244,165,261,96,347,254,713,963,168,457,237,187,93,67,154,421,221,253,401,344,424,164,479,6,386,83,855 }, +{ 1,14,5,2,46,50,38,58,171,61,45,155,7,48,69,270,290,24,21,29,75,52,16,67,54,666,32,663,227,72,502,0,425,223,18,592,89,17,35,243,37,207,76,186,103,263,36,163,96,83,741,6,408,10,400,22,181,28,9,428,12,536,64,221,220,70,262,281,23,267,115,106,65,807,292,309,119,148,474,449,394,128,141,86,241,3,655,430,51,189,476,140,431,541,587,95,334,738,675,90,114,13,780,108,71,720,19,133,27,202,335,413,4,767,112,77,25,405,513,179,659,99,218,787,55,180,528,151 }, +{ 23,51,13,453,457,12,719,401,961,4,629,730,99,475,115,2,11,14,403,17,40,5,15,165,1,3,64,0,10,57,33,21,77,569,32,50,37,308,7,386,9,16,38,670,197,59,41,217,128,494,46,6,758,62,22,775,65,45,18,86,48,29,72,899,514,361,328,139,102,247,153,54,56,421,468,79,523,95,108,757,196,52,19,317,117,482,342,341,24,734,509,210,202,180,177,237,488,402,114,36,134,880,76,498,90,275,111,34,28,291,67,20,474,120,743,125,82,391,130,752,490,437,405,318,277,27,141,89 }, +{ 968,967,966,965,964,963,962,961,960,959,958,957,956,955,954,953,952,951,950,949,948,947,946,945,944,943,942,941,940,939,938,937,936,935,934,933,932,931,930,929,928,927,926,925,924,923,922,921,920,919,918,917,916,915,914,913,912,911,910,909,908,907,906,905,904,903,902,901,900,899,898,897,896,895,894,893,892,891,890,889,888,887,886,885,884,883,882,881,880,879,878,877,876,875,874,873,872,871,870,869,868,867,866,865,864,863,862,861,860,859,858,857,856,855,854,853,852,851,850,849,848,847,846,845,844,843,842,841 }, +{ 2,1,29,103,75,52,14,67,46,309,6,83,70,238,38,24,108,220,218,133,74,96,262,36,114,87,388,26,50,25,366,236,69,160,112,5,65,58,66,394,19,145,226,7,532,135,163,42,18,45,150,545,458,433,891,331,357,594,17,28,412,27,294,418,86,278,21,124,398,149,732,894,324,537,283,223,273,222,71,12,606,739,16,646,547,138,267,48,696,171,137,151,0,175,602,800,22,37,487,181,99,208,32,194,287,785,336,64,23,13,113,55,95,335,54,97,323,154,61,711,128,68,302,118,10,308,600,484 }, +{ 7,92,71,97,140,60,150,16,298,24,189,230,14,149,423,35,508,314,18,43,279,232,66,399,273,246,251,38,607,69,126,172,517,674,2,173,28,375,74,467,104,68,0,809,20,823,336,118,267,175,135,108,191,154,21,345,501,796,36,52,125,653,29,81,330,107,124,362,356,6,387,55,359,46,779,302,192,22,5,575,48,67,877,462,10,388,441,9,566,723,783,221,1,65,49,398,101,37,278,702,83,91,357,705,392,12,109,121,236,426,380,32,212,695,443,86,596,103,868,608,560,165,830,167,13,558,25,541 }, +{ 51,23,202,120,141,13,1,260,128,21,269,450,77,137,318,48,115,165,15,32,102,5,197,64,180,494,82,351,326,125,291,817,12,349,342,98,523,403,203,93,7,33,854,352,2,31,14,719,457,6,110,144,44,4,386,177,730,96,116,308,99,221,9,217,10,153,17,210,922,453,18,16,402,237,90,704,37,36,117,25,421,11,615,95,20,898,373,30,475,40,401,391,19,317,8,3,304,498,576,69,285,901,275,223,293,247,196,86,899,134,83,341,127,59,45,39,365,216,692,372,50,0,801,752,367,163,139,100 }, +{ 77,102,33,13,177,23,51,141,202,64,40,128,115,4,269,59,318,6,217,137,210,291,0,7,2,1,180,196,14,22,10,95,153,11,117,125,165,15,86,352,197,134,90,403,216,275,3,16,9,18,237,453,494,342,49,31,19,139,25,65,28,5,17,317,349,93,36,304,475,26,82,12,98,44,341,27,120,365,57,24,20,110,372,386,21,247,457,719,194,402,498,116,29,730,99,260,8,143,144,523,164,114,569,48,32,111,326,285,208,391,401,244,30,509,312,961,300,752,56,160,105,203,34,450,546,67,482,170 }, +{ 101,9,363,676,264,18,411,682,604,905,520,821,271,167,621,647,364,121,252,404,118,39,166,848,781,265,100,16,382,664,447,212,777,478,354,146,907,812,132,446,360,724,0,60,463,759,24,329,23,1,88,71,35,936,68,857,195,21,48,593,865,639,932,156,897,31,98,96,51,191,13,286,333,125,656,49,694,30,254,105,591,55,806,562,109,158,44,438,419,272,521,124,154,407,274,5,65,310,553,28,144,691,33,116,159,127,93,32,77,22,67,50,410,37,538,108,706,393,115,83,36,92,12,199,327,223,10,418 }, +{ 144,203,326,382,166,96,822,418,438,93,859,88,921,1,367,141,744,269,349,110,576,473,83,77,410,23,966,21,403,120,692,39,494,274,521,317,291,217,615,523,855,303,817,13,639,264,800,32,100,909,691,225,36,478,914,765,506,5,498,652,44,435,265,321,260,160,910,551,404,137,48,772,202,897,453,450,208,101,960,254,31,863,342,286,190,51,30,593,831,616,419,730,682,98,340,372,341,197,69,33,9,625,724,848,686,284,466,704,656,223,180,102,335,125,617,352,285,363,0,662,244,530,919,812,393,374,351,318 }, +{ 21,13,180,23,32,115,48,125,197,191,165,51,5,199,237,146,12,225,18,16,93,901,350,271,22,241,375,0,71,118,408,49,447,264,126,1,167,31,24,522,302,98,65,64,575,864,569,92,566,247,744,86,58,391,352,713,212,141,60,44,9,4,88,562,459,308,303,203,202,100,45,37,17,2,818,463,360,316,919,897,698,586,478,363,293,221,213,103,72,40,691,909,608,558,509,166,68,35,727,340,517,217,724,629,546,467,446,341,304,156,120,108,11,640,288,110,7,734,296,150,591,914,346,354,321,317,269,114 }, +{ 15,13,23,51,102,0,8,77,1,5,12,33,9,4,2,10,180,82,165,197,115,110,18,403,202,22,120,326,457,7,11,351,260,898,141,342,14,373,401,901,961,64,36,386,3,291,453,31,16,40,28,93,21,6,352,59,44,99,27,341,450,217,719,49,117,48,125,475,128,37,391,308,153,95,269,24,318,177,854,730,203,615,421,98,30,32,38,137,922,317,498,704,523,57,817,509,56,569,629,116,899,133,83,365,758,105,752,144,661,196,304,638,494,134,300,670,206,402,221,210,208,86,535,143,65,532,109,35 }, +{ 77,33,102,64,202,141,128,51,40,196,23,13,117,318,269,153,134,1,177,115,10,342,137,180,210,2,0,95,6,139,403,86,14,22,15,7,217,291,4,352,165,197,36,59,237,275,3,11,5,494,65,125,49,9,453,475,16,19,28,18,120,82,216,17,402,31,93,110,260,326,12,341,194,349,401,203,98,25,99,27,24,457,44,317,365,498,372,26,351,285,719,143,386,450,300,21,752,391,116,29,312,38,114,304,111,244,164,961,509,57,247,569,523,144,308,373,901,34,421,96,56,160,437,35,20,678,32,8 }, +{ 174,104,544,151,624,74,525,25,644,66,29,177,636,416,0,204,750,122,216,6,81,45,186,222,319,138,2,145,287,112,665,192,64,59,243,7,77,32,355,22,715,128,784,17,173,20,311,135,789,280,52,33,549,113,879,4,227,21,194,90,5,141,491,102,86,42,95,43,137,58,12,27,496,275,65,529,614,11,348,678,304,651,402,580,712,503,269,422,49,37,14,318,210,673,396,202,792,10,18,500,869,36,148,85,426,247,405,681,19,226,97,825,72,16,196,40,61,609,597,115,24,217,233,51,28,881,91,153 }, +{ 372,141,304,352,947,954,291,177,217,269,349,128,318,437,498,77,678,202,864,64,102,115,86,210,873,137,751,597,247,13,180,5,275,32,16,35,21,950,752,92,197,83,87,7,457,160,776,70,33,24,179,36,317,245,165,126,107,569,288,717,95,402,237,23,263,108,774,156,546,509,340,37,18,12,946,342,872,75,800,244,114,103,851,735,365,341,146,121,99,60,579,312,120,1,195,354,296,140,888,71,51,260,223,418,758,622,537,473,363,196,98,69,10,526,48,820,817,765,704,586,450,316,246,208,148,96,90,50 }, +{ 200,161,714,53,73,27,472,383,17,440,34,585,62,56,136,250,106,78,527,322,19,862,361,4,3,514,80,84,390,14,176,179,236,378,162,377,129,548,202,47,469,495,217,26,11,494,128,757,555,453,402,412,130,880,328,507,257,183,51,45,5,38,300,184,123,497,54,12,57,33,444,59,556,283,205,245,108,485,297,209,803,152,342,40,119,99,318,452,2,46,102,211,23,196,64,229,65,77,215,684,21,814,6,240,0,153,368,403,90,730,475,13,117,133,61,37,317,305,32,255,813,22,504,181,503,29,436,95 }, +{ 32,76,72,21,38,14,241,89,54,428,449,5,45,148,37,181,90,179,2,17,221,350,12,292,46,129,48,476,155,1,794,99,477,205,720,3,770,50,464,773,4,540,308,36,57,460,270,245,413,743,184,214,741,328,115,23,123,474,137,257,10,358,11,0,281,431,29,488,421,405,162,51,288,59,64,401,710,536,430,152,290,128,218,519,22,316,697,552,7,587,86,670,361,487,6,227,13,52,618,133,249,543,77,106,62,165,769,141,243,202,403,229,114,453,263,67,475,745,341,119,197,207,497,285,9,40,180,920 }, +{ 128,141,202,64,102,33,23,77,51,13,196,318,269,153,2,3,180,165,40,137,5,15,1,6,403,10,117,139,197,86,352,134,14,17,0,19,494,210,120,342,22,7,402,237,453,291,177,475,125,115,93,217,95,21,12,82,260,110,36,457,11,65,341,450,31,25,27,194,99,9,4,326,498,275,961,349,26,49,111,401,317,44,203,391,351,277,38,28,386,18,421,37,752,164,16,143,29,285,98,24,48,678,114,116,32,373,509,300,244,208,54,719,56,59,365,57,8,50,546,144,20,523,30,569,372,898,90,308 }, +{ 39,137,96,840,30,202,9,0,613,88,406,669,100,141,180,576,160,28,49,530,403,1,269,22,228,822,829,679,856,190,244,166,219,352,816,966,765,827,36,55,68,83,128,274,418,23,318,98,44,442,35,10,253,258,51,863,77,31,317,251,21,876,158,494,475,213,95,65,116,254,859,268,125,48,13,16,931,187,367,131,805,574,105,5,365,800,102,177,93,115,33,224,127,242,120,110,291,924,217,960,144,64,842,616,165,531,385,351,101,159,276,839,778,203,201,492,109,67,24,284,748,860,345,692,295,421,272,456 }, +{ 180,352,141,752,1,901,269,365,115,5,217,2,15,4,317,23,197,7,13,341,0,244,3,253,6,51,498,202,21,318,31,372,873,165,44,77,98,9,33,10,116,137,237,93,482,125,64,864,105,12,32,48,349,509,128,131,22,120,579,11,143,8,102,569,717,37,391,177,260,127,308,110,28,30,291,450,40,559,49,39,401,160,203,144,14,437,373,564,72,24,453,403,386,546,421,96,326,16,95,36,148,661,951,247,533,494,219,109,201,159,55,961,251,475,728,468,642,117,254,221,134,18,811,153,210,82,345,224 }, +{ 1,67,28,50,46,2,103,83,14,65,114,24,0,38,133,69,36,55,18,75,9,54,124,29,22,283,7,218,5,238,16,154,35,52,96,160,480,109,108,547,12,68,398,429,646,323,181,732,175,220,21,361,171,71,335,236,389,418,70,394,48,49,149,86,58,866,531,87,309,366,37,484,208,328,251,118,194,6,10,302,412,487,420,189,336,23,168,545,800,51,803,433,185,137,128,163,754,39,45,505,169,285,596,486,13,503,533,60,101,155,95,64,32,289,570,223,158,267,927,140,745,516,17,115,514,346,956,99 }, +{ 180,352,365,317,115,217,752,901,341,1,253,141,873,2,15,4,0,5,197,3,244,308,951,31,7,498,318,44,6,372,291,165,269,202,98,137,120,127,160,36,116,373,105,260,482,13,131,33,509,110,51,125,77,39,569,349,304,437,9,11,8,450,96,64,22,21,391,221,23,93,638,717,28,219,12,421,10,403,187,83,17,102,49,661,564,851,401,128,247,811,351,523,117,535,546,468,224,201,86,35,559,48,16,148,494,134,143,579,968,922,854,817,800,551,475,55,801,758,728,615,196,177,153,144,14,32,30,533 }, +{ 32,5,21,347,13,95,49,713,51,48,197,23,107,60,126,64,237,180,153,77,165,241,33,379,850,1,263,296,196,195,4,121,134,132,102,28,115,22,117,217,0,291,16,841,929,928,652,534,660,39,493,247,31,279,18,50,36,626,150,90,76,40,407,883,509,362,349,260,9,356,125,10,177,92,441,340,259,35,24,752,351,199,65,11,71,809,546,953,933,854,312,75,61,56,37,30,7,598,207,44,423,372,298,522,783,590,354,156,645,608,591,939,937,638,498,450,437,391,359,303,273,86,57,342,316,59,640,446 }, +{ 341,365,180,901,317,115,752,15,873,217,564,82,951,197,110,165,0,1,141,351,4,3,482,352,6,372,2,12,269,13,10,559,253,854,5,468,7,244,282,23,535,18,898,14,661,417,349,308,318,102,33,509,125,811,704,36,143,77,964,28,498,326,237,137,51,533,692,638,38,475,31,22,9,642,867,391,30,8,202,120,203,966,758,373,338,206,24,569,291,922,494,403,332,144,116,49,44,697,55,450,386,128,943,842,768,728,706,405,346,299,295,261,260,258,242,160,148,39,27,26,19,17,11,50,48,46,843,634 }, +{ 45,17,106,21,227,209,62,243,5,155,32,3,290,38,255,119,54,270,14,2,207,181,587,48,263,12,794,29,46,552,540,186,659,37,430,408,770,496,721,50,454,136,84,52,221,425,56,76,308,828,99,697,400,328,115,123,741,80,488,1,528,474,401,57,218,281,41,129,541,513,421,36,487,128,10,787,61,241,378,477,543,64,51,742,306,133,240,413,11,72,428,4,165,734,23,152,655,453,211,34,920,311,59,90,236,137,214,58,162,629,431,179,832,341,6,366,583,89,77,202,313,361,383,317,514,86,745,365 }, +{ 16,24,35,18,50,7,71,75,60,14,69,0,9,1,189,108,68,67,21,181,28,12,48,223,92,5,23,140,46,103,51,133,13,2,267,230,54,163,149,65,22,335,70,281,10,32,55,96,87,37,64,171,128,394,114,141,502,58,115,486,236,99,820,98,766,202,38,137,83,309,118,246,269,49,36,95,125,191,155,167,121,430,39,218,101,77,6,150,237,126,641,774,31,318,221,487,180,443,177,433,251,545,220,565,388,29,86,72,352,399,165,217,552,112,61,105,537,17,852,45,44,30,212,175,124,89,307,102 }, +{ 127,308,293,98,165,395,258,219,13,286,23,629,401,51,115,301,569,197,457,276,512,391,509,105,616,239,924,338,22,734,170,55,961,295,31,758,169,910,272,567,116,763,201,320,944,341,143,261,237,506,180,479,873,365,141,185,206,36,242,86,374,762,498,546,913,131,44,64,923,327,843,601,386,332,492,160,125,860,317,654,964,900,284,95,28,634,943,164,137,10,901,282,224,740,468,456,144,109,841,558,719,268,120,417,535,384,299,844,482,903,110,93,9,1,935,662,578,217,253,928,730,187,393,2,752,244,531,434 }, +{ 748,957,840,213,669,539,466,88,30,251,863,321,217,679,190,137,93,144,317,530,352,831,228,230,160,617,800,689,219,203,816,96,418,202,36,473,258,406,141,871,326,83,855,752,120,854,778,82,44,100,187,201,1,435,224,351,345,719,335,116,110,443,274,28,310,55,21,450,332,382,822,574,86,931,494,531,365,31,839,128,39,180,341,367,898,23,613,498,372,731,860,9,410,926,197,704,5,817,131,69,35,125,434,166,22,944,318,924,856,268,64,48,567,244,206,291,922,523,269,32,876,717,98,49,13,349,169,859 }, +{ 5,48,21,14,1,2,54,50,36,430,38,32,181,270,45,281,12,72,17,67,46,0,290,218,99,22,28,83,76,155,24,541,37,10,513,89,114,65,58,133,61,103,96,488,51,221,425,263,3,9,328,29,23,487,697,207,741,115,18,86,745,160,428,474,148,408,6,16,502,7,64,171,137,794,108,52,920,477,842,227,55,236,552,543,13,119,514,361,449,202,238,828,536,128,308,292,476,243,592,90,241,186,179,666,285,4,27,655,663,480,421,401,106,208,141,165,433,528,532,19,834,95,283,197,11,180,720,403 }, +{ 13,23,51,115,217,33,77,0,4,2,9,317,102,141,7,11,1,10,180,5,40,202,22,197,3,6,352,365,12,269,341,165,14,18,16,28,64,15,59,318,177,403,752,31,24,196,308,32,372,125,128,291,498,137,17,247,153,21,49,401,453,349,98,117,44,8,95,237,901,36,86,93,494,342,475,30,482,210,468,457,304,873,569,90,105,143,421,391,386,37,509,39,134,139,110,116,35,275,244,144,120,326,55,961,19,546,203,46,864,27,719,221,111,65,164,253,285,216,127,402,730,50,437,38,67,20,288,54 }, +{ 13,23,165,15,37,54,134,12,117,38,457,51,64,197,99,115,153,89,961,719,33,10,82,509,14,128,120,237,3,196,308,86,141,328,0,1,629,72,450,277,139,102,523,386,351,77,8,401,65,4,546,95,155,202,180,21,2,498,391,40,352,7,5,32,11,6,730,474,31,475,110,137,899,854,453,922,569,291,114,108,22,898,44,9,177,403,148,49,361,342,285,260,514,269,93,17,758,901,210,864,479,59,34,29,28,67,217,494,318,236,179,90,76,56,50,18,242,417,528,488,206,98,413,94,817,789,468,402 }, +{ 5,21,32,14,2,48,17,3,1,12,72,38,45,54,51,10,36,22,76,23,0,89,540,13,137,86,37,4,430,270,50,114,11,221,99,285,115,64,6,61,46,361,29,181,290,65,58,403,208,155,28,133,480,281,148,128,323,453,657,401,514,541,754,421,697,202,90,9,7,475,241,263,40,56,328,308,95,59,207,576,805,165,838,513,57,27,402,19,425,18,775,488,119,283,218,741,428,341,24,141,77,33,794,408,227,197,102,194,52,179,832,55,180,494,269,365,449,164,236,350,745,318,171,859,244,474,108,880 }, +{ 202,318,77,291,128,275,102,33,269,342,177,210,402,678,141,403,494,40,20,196,0,137,7,23,49,32,64,216,4,416,104,139,285,59,352,43,61,66,13,304,234,90,22,475,319,450,11,349,117,300,113,5,153,58,947,17,112,21,6,115,120,70,544,38,16,142,2,315,194,134,95,18,498,9,74,597,237,51,329,884,25,12,453,864,24,789,173,415,639,859,155,277,52,81,86,222,178,10,48,560,14,230,665,198,97,31,355,557,65,1,186,105,817,111,39,226,57,546,121,29,147,28,15,87,60,96,265,523 }, +{ 1,24,46,2,14,67,69,75,103,38,35,50,52,83,29,108,18,16,71,220,149,394,309,236,189,70,96,133,171,54,68,366,28,58,5,238,181,163,87,7,433,0,335,60,218,114,251,487,65,267,537,36,328,418,223,9,486,55,516,262,112,150,155,12,388,48,21,45,675,175,336,22,409,118,37,186,281,140,412,696,6,124,25,514,151,294,61,99,154,10,334,32,17,273,92,135,160,283,760,278,458,227,766,64,307,19,86,361,398,230,552,641,532,400,137,565,23,502,345,51,13,95,765,26,66,72,128,399 }, +{ 15,1,5,2,13,0,7,23,6,3,180,12,51,4,9,115,457,165,197,21,8,14,141,901,37,752,117,16,77,10,32,120,202,11,317,217,110,961,22,33,365,341,82,269,719,93,352,31,102,18,128,153,134,28,260,48,569,64,137,40,318,196,386,351,509,17,44,30,326,125,730,308,450,49,177,36,95,453,89,72,98,99,203,873,523,401,498,403,59,291,116,38,24,629,372,90,349,76,55,391,373,144,758,494,237,244,96,482,475,54,854,39,468,143,45,139,105,221,247,86,899,50,148,253,35,210,304,421 }, +{ 28,65,124,14,0,154,24,67,69,55,175,7,9,429,46,2,109,35,149,336,68,1,189,83,114,251,108,16,133,398,484,103,36,18,38,22,50,267,420,140,283,289,71,570,158,236,389,101,505,230,86,346,185,39,12,169,168,799,194,118,49,596,29,732,782,302,309,5,533,52,252,95,388,419,535,21,439,458,705,75,797,160,412,6,10,54,531,218,92,345,208,96,159,610,48,60,64,323,756,105,37,23,646,238,51,128,329,886,547,278,13,650,637,32,172,135,364,164,143,584,602,115,701,601,137,333,399,443 }, +{ 162,403,56,775,129,880,161,527,99,3,17,40,33,475,23,4,361,128,196,14,200,183,34,202,102,184,548,402,383,53,130,153,757,494,27,257,108,453,38,12,136,117,51,485,123,64,57,65,19,585,141,73,13,139,421,134,229,106,11,80,556,469,670,84,59,152,62,78,714,412,378,77,5,495,21,54,236,488,497,2,6,90,514,86,111,730,22,180,26,46,507,440,95,269,37,472,115,32,401,45,283,452,618,0,328,47,18,29,266,390,10,211,165,899,250,24,300,862,719,504,133,543,197,318,277,50,176,457 }, +{ 151,29,112,2,58,311,186,52,70,113,315,45,66,74,61,631,5,87,287,243,491,209,1,869,6,227,614,135,7,307,75,80,27,145,496,226,25,454,17,14,549,26,845,19,32,46,73,106,294,163,155,64,255,659,849,667,632,644,592,72,220,331,21,334,222,172,42,69,400,48,76,138,396,337,148,635,50,86,611,445,128,171,97,122,141,95,738,119,16,37,38,4,207,67,425,290,409,53,24,65,262,89,90,12,297,278,192,461,137,22,624,36,20,483,881,84,292,684,43,10,833,56,651,655,114,263,204,115 }, +{ 6,74,75,1,70,66,138,29,25,85,26,42,87,220,52,2,135,226,222,802,324,112,145,357,103,19,278,108,380,67,114,309,235,14,500,69,451,133,606,280,38,24,582,83,65,262,163,86,97,394,294,267,486,96,50,36,889,388,603,641,58,256,32,895,238,223,853,45,122,594,46,537,64,21,204,150,27,335,792,102,7,115,337,218,13,418,71,33,160,632,5,189,696,571,308,461,95,565,16,545,331,125,4,137,134,153,113,117,17,35,558,54,287,149,22,171,18,711,174,630,247,63,12,23,0,194,462,90 }, +{ 15,13,23,51,1,120,0,180,202,5,260,9,2,102,450,115,77,7,141,12,21,269,318,197,137,523,403,6,165,326,33,351,341,817,110,901,342,128,365,36,14,4,16,457,308,401,18,494,8,48,32,59,64,82,352,22,475,10,217,386,730,317,28,93,37,3,373,854,453,719,752,31,615,24,44,30,421,291,11,210,221,39,899,99,96,961,55,86,98,40,116,177,402,922,160,898,244,203,35,576,17,569,117,509,144,391,90,49,372,704,349,153,873,285,498,253,275,125,89,143,300,196,72,247,67,95,629,127 }, +{ 5,45,17,14,46,2,50,48,186,181,227,61,29,38,155,281,54,52,80,106,400,209,21,58,3,171,243,487,540,32,328,311,103,218,133,430,255,62,67,12,828,454,36,56,536,236,27,1,207,496,787,119,697,366,114,37,19,108,6,760,513,541,600,587,592,84,745,842,99,920,96,502,151,76,13,26,270,583,263,86,221,115,83,832,666,51,23,136,34,334,65,10,290,72,308,137,663,869,739,361,514,25,721,738,794,4,659,543,89,401,667,11,64,576,378,148,22,24,834,0,474,357,73,483,754,516,41,53 }, +{ 18,16,35,60,68,121,265,158,159,24,329,105,9,132,167,252,327,709,101,55,286,39,21,71,146,49,7,109,374,23,22,126,604,107,419,333,271,14,28,1,48,0,191,120,92,212,20,310,272,890,553,118,5,326,137,199,260,455,51,95,446,320,13,32,450,259,170,627,621,639,2,10,195,647,128,98,364,301,557,463,43,37,239,77,576,848,64,100,351,82,102,125,65,285,202,12,354,6,363,346,178,344,197,165,493,407,147,29,523,242,907,96,538,932,403,33,279,916,97,704,36,31,402,520,318,156,912,846 }, +{ 467,650,389,108,283,56,412,123,177,216,670,619,349,269,33,475,899,775,453,503,789,401,678,65,543,51,196,403,730,128,84,23,194,523,13,421,719,576,457,11,597,153,880,141,249,847,3,801,117,817,57,133,18,0,202,114,59,24,150,32,386,64,40,95,291,4,183,450,130,524,162,5,554,152,90,22,9,361,134,236,102,17,27,120,304,19,961,629,210,432,21,770,86,16,14,10,77,165,46,28,594,381,377,240,137,26,229,494,947,372,602,342,318,275,125,98,630,743,701,605,184,103,97,63,50,49,958,444 }, +{ 514,11,377,328,361,3,4,880,507,403,485,57,130,14,757,152,38,102,300,211,556,215,54,176,236,62,229,56,494,40,585,555,266,34,183,59,342,12,162,440,453,548,51,37,77,402,488,305,184,196,542,421,775,471,123,527,210,369,313,33,457,136,719,23,202,231,13,128,153,381,629,452,803,543,129,412,157,17,248,339,111,84,139,497,475,205,99,119,32,828,188,177,961,349,383,117,46,89,79,730,216,504,277,401,161,134,308,291,322,76,249,283,72,65,64,21,2,22,670,503,241,41,257,90,108,618,472,94 }, +{ 3,555,62,266,507,215,130,440,99,514,229,152,471,40,57,139,403,12,56,548,305,33,475,38,313,775,361,14,123,211,880,328,196,102,23,556,381,184,136,757,377,77,277,54,183,494,453,421,4,11,205,349,291,129,162,452,34,236,84,17,369,37,59,485,497,111,401,402,51,153,157,477,585,13,670,128,117,339,176,161,41,488,248,202,383,300,457,249,119,76,432,46,342,134,79,188,89,214,2,474,72,527,961,21,32,308,719,543,165,177,257,730,412,133,413,64,283,216,210,245,106,618,803,90,15,629,115,460 }, +{ 120,318,450,260,77,15,202,13,817,51,269,5,33,494,922,128,1,326,102,23,403,523,137,141,21,177,351,40,342,48,291,82,854,153,125,64,7,615,32,196,22,704,210,402,98,4,475,12,373,115,93,2,96,453,285,44,275,349,36,61,31,116,216,859,197,117,49,28,16,899,678,203,165,59,90,180,139,50,24,6,144,217,95,898,692,170,110,45,18,17,10,0,730,352,72,37,207,457,576,341,237,35,20,9,661,372,194,961,223,214,958,949,811,805,791,551,424,391,365,244,242,221,208,160,127,99,89,76 }, +{ 23,1,15,51,13,2,5,0,7,6,180,12,9,115,21,3,197,165,4,457,386,120,141,33,202,453,14,730,523,719,401,37,40,32,341,8,31,403,260,82,450,352,16,48,110,269,317,77,217,308,44,137,22,102,18,365,318,901,93,351,421,117,475,752,11,326,28,391,49,98,153,498,128,99,64,30,569,10,899,509,36,196,961,24,221,59,39,89,177,758,854,134,494,116,17,629,90,817,670,468,86,247,72,95,35,105,96,203,482,55,373,125,372,873,144,210,922,291,38,244,237,898,54,50,20,349,615,65 }, +{ 15,180,351,317,82,115,365,217,141,898,10,854,901,197,143,77,23,752,341,352,33,110,13,36,64,102,51,4,1,260,253,244,120,9,21,5,22,40,498,55,165,137,117,28,11,873,372,922,482,134,59,0,72,153,196,160,32,48,202,3,2,961,90,12,35,811,164,291,177,349,31,86,30,308,7,125,509,642,450,128,6,139,17,96,210,237,89,951,523,342,457,93,730,57,37,39,661,16,83,437,373,546,269,49,18,14,531,8,717,318,111,45,24,569,247,76,533,719,221,67,326,282,468,386,203,52,44,38 }, +{ 104,289,707,66,712,173,97,214,90,64,426,20,874,414,568,43,65,32,194,715,7,204,312,560,74,500,192,835,324,0,38,376,819,861,147,319,95,636,22,86,729,21,470,879,288,5,107,526,52,81,178,2,751,49,10,519,142,595,427,247,77,356,232,249,4,750,237,597,28,885,784,36,889,858,51,202,802,14,135,298,115,13,59,524,808,1,330,325,23,92,180,359,392,31,24,583,681,61,18,259,172,48,177,123,825,798,37,357,12,208,33,174,387,362,140,102,35,76,40,17,137,198,11,216,600,16,671,91 }, +{ 84,65,56,18,194,249,0,108,123,643,847,554,63,524,416,141,453,24,26,403,22,511,467,630,475,128,73,137,33,9,80,899,95,64,370,76,45,16,133,42,289,19,650,214,27,240,730,719,210,196,119,114,49,494,402,304,102,90,32,11,10,588,561,283,235,202,523,183,25,736,609,580,576,500,457,433,357,349,343,209,124,60,57,39,28,3,1,757,150,23,368,670,51,597,947,813,667,659,529,431,291,287,243,236,233,216,179,177,153,134,122,117,112,89,85,72,31,30,6,421,389,165,125,895,874,619,519,361 }, +{ 21,350,308,13,346,197,826,352,101,841,570,68,165,509,23,115,100,694,749,569,903,51,212,401,9,0,221,734,391,154,777,252,88,124,610,265,237,39,558,125,48,30,217,230,421,35,289,468,64,55,535,475,213,546,175,909,1,403,382,166,589,28,645,812,419,180,32,706,98,593,410,703,759,141,317,629,65,241,83,194,818,773,22,321,752,689,923,341,962,96,671,699,158,95,16,329,429,854,264,724,190,439,31,722,316,72,900,144,10,93,247,49,831,676,372,24,223,36,553,269,293,69,365,490,191,5,639,251 }, +{ 92,16,60,24,7,71,126,18,35,140,330,14,246,46,50,267,121,146,236,108,75,220,199,571,394,68,309,150,132,107,314,448,230,54,232,633,5,366,118,2,21,9,278,38,388,37,87,64,1,181,328,12,641,55,70,672,582,726,48,526,0,622,172,516,563,86,69,32,103,10,133,36,156,167,28,65,101,135,195,52,171,950,149,22,830,189,99,649,191,451,67,13,29,262,294,606,83,545,58,517,462,271,23,572,95,163,696,128,675,594,620,392,514,77,785,45,155,192,217,653,335,279,522,212,237,51,177,565 }, +{ 15,82,898,365,33,77,341,180,115,901,21,10,102,197,55,5,4,351,13,165,117,36,11,1,217,23,317,141,40,752,120,153,450,134,253,12,2,260,143,196,873,51,89,457,32,3,177,352,349,854,372,244,160,59,7,719,961,64,202,96,6,110,291,137,523,498,111,0,210,482,28,31,30,17,951,125,318,22,128,509,139,811,730,386,169,304,9,391,37,164,48,326,269,203,93,373,237,148,535,342,72,569,704,308,247,38,533,49,98,922,86,734,109,14,45,275,216,95,817,403,44,437,57,899,661,41,39,968 }, +{ 16,24,35,71,18,68,108,140,189,69,103,60,267,50,149,118,230,7,1,133,191,92,46,167,9,75,246,0,2,443,309,236,486,335,14,54,212,330,218,516,65,67,38,394,641,21,181,55,28,101,48,83,399,366,563,412,545,114,220,696,388,51,328,13,23,5,64,458,273,124,126,12,238,487,52,345,37,537,87,121,29,915,723,150,175,32,302,154,163,361,146,375,674,22,10,171,271,96,514,251,86,433,70,336,36,571,622,223,906,796,237,726,398,602,283,39,262,115,702,565,891,58,695,562,199,128,894,49 }, +{ 60,20,16,43,7,71,18,92,118,14,35,628,608,68,154,28,375,126,175,107,0,279,150,173,22,356,97,149,566,55,153,169,49,140,69,33,13,230,117,38,23,10,362,51,2,77,783,414,421,109,298,246,359,196,125,124,446,102,104,386,722,66,21,1,601,575,621,326,439,429,12,392,134,32,882,192,74,345,551,533,419,185,336,191,4,232,37,273,595,81,189,791,314,202,158,917,576,467,570,6,5,302,48,422,115,801,319,197,165,677,535,925,420,387,9,423,399,251,517,515,403,291,199,260,694,553,64,40 }, +{ 187,258,871,295,434,489,219,201,224,268,839,384,699,940,110,261,116,160,131,44,36,574,228,144,141,93,137,567,406,244,926,120,98,253,260,373,64,442,206,269,127,908,318,55,31,911,959,855,617,10,1,531,332,523,202,613,203,661,326,352,860,217,717,367,276,21,51,143,180,96,23,105,13,450,22,385,533,284,662,197,851,86,77,251,30,115,83,9,48,816,164,282,811,317,365,39,856,165,351,843,669,473,899,341,125,922,902,752,291,817,372,918,345,417,177,728,82,293,242,951,498,968,854,386,559,15,349,49 }, +{ 66,97,7,172,192,712,232,204,324,387,74,426,448,526,104,173,107,568,560,43,356,825,808,500,729,135,359,52,707,92,32,95,330,20,636,64,65,22,140,38,138,81,298,2,289,715,885,5,802,392,29,49,14,174,0,889,91,86,226,348,90,750,861,6,357,280,314,312,126,214,246,792,353,10,36,24,380,580,21,835,37,28,414,58,230,69,222,595,362,194,673,16,278,12,77,85,33,46,874,18,819,61,60,681,147,858,888,612,600,13,241,25,501,102,48,35,67,189,87,71,40,4,583,519,779,1,237,150 }, +{ 15,1,5,341,4,13,0,3,2,134,33,12,11,7,10,77,6,9,21,23,40,197,8,32,365,37,17,901,111,102,51,117,180,317,961,153,217,165,59,14,22,16,82,141,31,457,94,202,115,291,453,403,196,57,36,372,475,318,719,28,349,18,401,72,269,99,48,386,110,498,64,494,120,352,93,351,54,139,27,509,474,177,45,44,30,670,90,421,326,76,56,89,128,898,642,629,304,260,450,19,482,137,29,55,391,873,342,24,49,730,203,62,373,143,210,79,752,41,39,758,661,817,157,152,98,523,579,402 }, +{ 0,24,35,28,124,68,9,154,65,16,109,149,55,175,158,71,39,252,7,419,14,108,101,346,168,22,185,289,429,49,18,420,169,67,439,671,703,336,302,1,159,92,610,962,484,118,333,95,69,2,677,398,344,100,364,810,272,345,105,46,329,114,60,535,265,327,577,30,10,189,21,12,254,286,133,194,83,23,140,793,553,86,36,50,570,637,13,103,5,722,310,51,374,230,48,38,64,32,747,706,505,701,273,251,150,164,601,455,166,627,143,634,125,283,424,239,458,799,128,246,267,388,547,6,137,96,37,538 }, +{ 7,92,14,16,46,140,220,60,35,24,2,126,38,87,75,278,71,172,262,135,52,314,107,54,294,246,394,150,18,50,330,29,309,267,230,171,1,70,232,108,21,5,121,620,97,236,69,622,32,572,563,950,366,409,58,199,12,48,64,328,37,571,189,146,448,132,66,68,155,872,112,192,461,67,441,181,675,279,13,298,28,226,0,451,23,163,423,606,9,582,128,20,86,526,36,334,307,55,380,462,888,357,335,61,43,10,22,45,83,337,331,186,353,359,103,726,133,633,95,51,65,202,72,74,785,114,141,149 }, +{ 92,7,126,16,314,172,60,140,35,135,278,14,232,2,107,246,46,150,330,24,66,38,71,230,52,380,97,298,279,448,563,192,357,262,199,121,87,18,582,309,462,441,29,75,74,132,220,5,146,451,526,324,423,50,21,550,69,32,68,226,508,1,70,37,633,64,12,108,606,267,48,572,493,54,189,726,366,0,392,359,22,95,43,58,571,603,28,10,20,86,236,55,171,600,36,9,388,163,65,607,387,6,67,49,294,649,830,583,356,362,90,622,394,620,167,259,939,149,443,83,580,114,25,103,118,672,61,45 }, +{ 235,26,53,297,6,436,73,113,27,445,42,25,250,611,90,19,65,214,648,614,85,624,289,64,122,200,491,684,416,32,17,194,45,665,145,34,80,95,573,151,112,322,86,680,714,315,630,106,690,14,29,472,255,141,396,56,312,57,22,247,76,209,4,128,61,288,123,84,352,249,11,874,108,3,138,5,217,256,70,119,59,74,0,835,283,161,114,751,269,597,525,389,136,77,51,10,40,115,287,47,2,52,78,176,881,62,13,524,519,23,137,33,133,28,46,177,746,311,18,236,12,63,180,453,237,631,179,243 }, +{ 352,141,752,854,36,1,351,217,230,244,661,110,816,258,160,224,180,219,82,201,269,567,144,295,260,373,922,93,120,203,489,64,35,202,406,317,206,699,137,116,268,871,717,10,839,251,434,228,864,253,326,96,21,131,9,30,83,384,44,442,39,450,574,69,332,48,318,418,728,898,55,127,187,642,365,86,276,88,28,341,831,498,22,901,443,100,367,523,128,531,940,213,5,689,748,190,860,679,0,31,811,843,559,164,7,669,143,662,851,77,564,372,165,335,98,800,261,2,959,349,284,170,115,291,824,345,50,855 }, +{ 126,60,16,35,107,18,191,121,68,92,279,446,598,463,375,43,167,493,7,20,118,199,212,566,14,150,97,28,575,608,589,55,407,101,246,154,146,24,346,21,0,175,48,71,362,815,354,49,628,132,939,5,467,22,649,10,9,1,356,259,147,271,149,783,439,140,23,108,32,109,230,376,683,125,13,621,345,441,363,31,51,647,612,273,124,562,173,695,768,105,38,252,2,727,169,818,640,195,755,590,637,158,722,364,104,37,953,535,12,44,64,66,359,33,202,806,298,46,419,98,221,95,77,185,660,399,653,520 }, +{ 1,15,2,5,4,3,13,0,23,11,7,6,341,115,77,51,10,9,12,33,180,197,102,40,217,165,141,365,901,21,317,32,202,8,14,64,177,22,269,352,59,17,291,31,569,318,125,210,16,93,386,453,196,457,401,18,494,403,153,752,37,28,475,961,117,110,498,120,342,523,139,116,629,44,326,134,349,203,98,372,49,128,275,57,308,48,237,391,719,30,509,24,95,36,86,19,421,260,758,247,90,730,615,216,137,373,450,873,111,482,144,29,899,304,817,670,25,300,143,801,55,20,244,82,45,27,39,242 }, +{ 51,23,13,202,21,5,403,137,32,141,318,128,1,120,165,125,12,48,15,475,269,180,453,197,115,64,401,33,2,7,450,6,523,308,260,421,221,4,494,37,386,99,16,14,196,457,730,237,93,817,102,77,61,82,352,9,719,98,110,10,20,899,326,19,18,3,117,90,17,391,402,95,0,341,31,11,217,25,961,629,351,96,134,72,49,153,30,26,569,801,203,372,40,509,24,50,317,285,27,758,116,349,342,70,59,58,76,39,44,291,89,57,45,29,854,28,22,144,177,35,194,576,86,670,54,365,678,247 }, +{ 131,716,371,224,737,385,219,254,616,924,836,187,258,98,761,916,944,127,39,105,442,578,9,49,272,31,688,44,116,242,137,871,908,159,395,518,890,28,293,284,574,202,623,51,160,23,0,201,276,36,827,938,839,22,333,125,456,299,286,144,109,438,158,13,95,910,64,30,829,228,327,166,101,1,860,926,613,96,86,512,846,310,21,295,10,406,55,318,252,320,93,843,128,301,627,567,203,165,48,662,492,100,268,37,531,457,221,190,434,403,68,384,421,239,393,261,902,35,533,169,170,120,110,260,244,308,5,506 }, +{ 15,5,1,2,3,4,7,0,6,11,13,10,180,197,12,217,341,141,33,134,77,17,165,9,352,40,365,32,317,64,498,752,102,21,117,901,59,14,237,8,23,291,349,36,864,269,372,153,57,37,82,961,22,115,569,196,120,509,873,342,202,48,51,351,110,16,93,45,31,457,125,19,76,72,111,244,27,260,128,143,318,79,546,56,139,28,18,86,44,98,90,391,94,61,55,854,95,38,386,29,482,898,49,99,89,253,210,177,137,730,450,35,221,84,25,304,954,758,326,922,474,403,203,20,373,50,247,523 }, +{ 66,74,192,173,222,226,174,7,29,712,52,204,353,43,20,500,2,138,145,97,104,673,426,355,6,64,560,87,0,580,331,172,414,90,32,25,337,65,22,49,135,825,861,112,280,5,287,81,95,525,86,21,707,42,38,792,415,85,750,889,356,36,324,819,10,632,232,45,526,330,1,107,544,91,319,140,746,151,294,194,58,92,28,14,67,289,4,636,115,37,17,12,262,715,470,16,155,59,27,61,233,387,298,18,376,568,216,26,122,69,77,113,24,23,888,70,13,114,51,75,448,729,11,609,102,348,312,33 }, +{ 786,259,5,534,590,493,465,929,581,21,279,941,49,13,612,132,32,362,23,147,61,48,35,121,939,626,683,146,178,107,771,687,408,347,713,345,58,427,126,70,18,60,4,197,878,150,344,640,379,1,195,841,33,120,263,356,51,509,522,260,28,22,0,115,55,10,376,663,207,350,199,16,423,142,407,221,156,246,623,595,241,105,31,903,298,11,71,68,95,92,64,17,40,450,441,90,167,308,165,518,44,783,316,247,953,360,351,98,30,102,82,65,159,955,290,87,817,134,20,182,39,24,230,117,59,501,315,569 }, +{ 278,14,135,2,140,16,267,69,92,24,7,46,35,189,38,52,71,29,60,75,357,172,87,309,66,380,262,74,324,230,462,220,1,451,150,335,226,571,108,18,97,50,443,606,563,103,67,246,550,6,641,126,330,70,388,366,5,83,149,68,21,486,48,314,25,12,54,565,298,394,600,622,32,423,294,232,37,582,236,222,572,28,65,251,603,36,192,64,58,145,0,171,331,55,950,853,95,133,86,22,10,114,112,399,620,138,448,26,163,353,508,500,583,96,45,877,238,42,726,9,785,121,279,181,696,72,19,223 }, +{ 165,13,308,391,197,23,401,509,758,457,569,51,629,734,180,115,873,317,901,961,3,453,719,29,46,546,80,34,2,482,217,468,181,867,48,106,45,341,52,17,14,287,297,5,730,445,386,552,428,62,221,186,15,11,171,56,50,12,38,365,250,328,133,61,211,540,153,215,119,53,41,903,670,58,743,311,99,305,231,176,27,236,4,54,130,881,403,151,6,600,57,136,84,40,764,787,114,475,862,472,469,73,32,26,33,400,227,137,21,1,255,196,270,281,155,65,752,635,440,405,326,161,110,549,477,430,396,592 }, +{ 1,14,22,2,28,7,67,168,65,24,49,0,35,46,55,50,95,69,12,159,36,105,16,114,109,158,5,83,194,103,38,133,239,169,9,289,164,185,18,424,6,21,10,708,124,170,320,283,312,389,29,68,51,23,420,143,189,347,505,336,37,175,835,892,86,149,108,64,874,54,13,577,251,48,32,654,756,547,96,71,115,301,286,128,154,202,236,344,160,253,484,638,52,75,208,230,747,610,308,39,218,533,137,140,267,141,374,3,345,31,127,237,799,98,601,597,244,272,323,125,398,401,118,17,327,99,412,101 }, +{ 34,453,130,196,322,3,14,236,361,47,51,377,11,730,514,711,4,412,153,57,161,440,485,102,176,59,162,108,17,62,283,757,202,56,136,38,328,880,77,494,184,12,719,128,402,403,200,23,78,117,27,475,21,300,40,381,54,472,33,53,183,152,129,99,2,714,452,13,585,803,80,210,862,555,383,73,46,123,37,139,84,670,556,503,548,6,507,421,5,111,527,29,19,342,229,457,26,775,134,318,469,133,115,90,106,257,277,401,250,497,205,1,543,266,211,542,629,899,45,488,215,217,65,72,275,339,432,32 }, +{ 48,18,23,21,35,60,16,24,156,51,13,71,121,221,5,132,46,141,163,7,1,43,271,202,115,92,411,178,340,137,447,308,32,363,150,698,125,223,195,781,541,318,147,142,28,22,0,108,360,621,401,241,2,463,354,49,658,586,67,303,269,97,20,761,50,296,146,70,65,763,867,691,638,182,647,93,82,10,767,167,31,914,670,290,128,107,75,61,225,98,64,4,120,14,12,758,110,29,949,457,316,165,105,95,293,30,668,932,180,39,446,724,743,666,421,391,372,351,114,96,86,66,591,352,33,942,922,459 }, +{ 13,165,629,457,961,569,23,197,341,401,41,758,901,719,17,509,51,115,38,421,62,54,157,12,32,45,117,57,37,339,328,106,188,14,365,734,99,546,488,477,76,873,2,453,308,542,248,72,10,184,40,721,214,211,4,386,305,479,474,152,3,243,90,139,11,5,1,153,313,21,468,134,471,217,148,129,123,179,77,89,670,255,162,130,94,7,391,136,59,102,111,536,196,828,247,237,743,428,6,514,277,556,128,33,475,155,266,229,29,528,209,720,413,241,64,361,476,183,9,0,519,405,317,202,181,805,775,431 }, +{ 77,141,2,102,33,1,64,6,5,3,7,269,202,128,180,23,318,14,0,10,13,291,352,153,40,197,165,137,237,51,17,177,15,217,117,115,196,9,86,4,139,134,11,19,22,12,210,403,16,342,95,125,36,457,317,21,498,752,349,275,143,59,341,120,27,93,82,18,28,111,494,110,164,38,25,365,901,475,391,57,8,203,509,260,31,961,49,453,386,48,678,351,326,56,37,719,402,99,401,546,372,569,44,65,304,864,32,730,300,194,26,45,244,873,277,29,285,41,208,597,20,253,160,523,24,98,54,312 }, +{ 212,68,154,124,562,101,191,0,9,375,118,520,167,149,818,65,16,722,302,21,777,35,24,71,604,350,100,806,252,478,346,264,664,841,1,759,265,48,13,695,429,55,701,329,51,23,724,398,589,18,936,115,882,484,755,88,749,69,903,125,189,410,22,221,535,382,108,308,166,917,411,39,28,223,67,92,509,32,10,197,64,5,694,650,114,558,95,246,60,909,7,83,241,96,165,363,391,593,98,230,321,103,364,826,553,925,639,49,458,734,628,419,336,447,768,399,140,105,175,37,886,682,905,674,821,404,109,812 }, +{ 48,23,51,98,125,541,598,259,293,13,807,126,21,115,221,493,963,223,35,31,670,386,121,0,107,770,165,144,90,203,116,719,949,783,727,279,590,197,32,326,10,284,146,49,401,407,132,64,199,16,955,342,97,39,834,1,194,453,191,522,427,114,105,92,30,14,7,465,393,763,271,50,725,615,195,108,22,77,44,9,640,291,20,202,269,941,767,671,591,498,467,360,147,128,28,704,551,389,289,33,150,141,60,43,697,534,362,589,58,325,621,523,475,403,260,237,218,210,140,137,127,109,102,87,83,46,36,867 }, +{ 21,23,13,5,1,586,32,48,167,51,781,31,647,33,271,165,49,141,591,44,115,522,77,197,221,22,360,18,93,146,391,558,105,932,217,447,118,153,961,352,60,900,317,761,196,459,0,363,260,241,82,4,12,698,463,7,291,98,27,247,815,156,120,36,623,35,10,191,628,95,65,46,17,50,922,341,202,45,411,64,521,372,102,16,934,308,269,663,410,351,326,14,116,686,349,225,110,457,29,2,509,71,621,854,701,475,354,342,159,158,132,127,24,478,446,321,246,199,640,848,777,759,371,318,316,273,223,126 }, +{ 2,29,61,52,7,1,5,14,58,70,45,425,6,290,112,738,32,207,46,72,66,186,87,76,655,38,243,263,74,151,21,75,16,227,25,50,135,17,148,64,89,255,292,128,24,106,155,141,315,476,209,226,90,26,833,19,113,27,306,115,48,454,549,592,18,4,163,307,0,20,119,77,10,86,36,270,54,177,35,171,720,22,65,137,496,202,40,887,59,179,95,294,790,23,69,269,220,172,631,334,9,67,449,28,408,318,483,311,145,13,97,845,84,287,240,214,51,37,43,241,405,33,49,102,663,80,12,180 }, +{ 39,265,9,333,100,310,411,254,363,101,447,404,18,272,98,639,264,166,1,286,203,327,682,676,31,905,156,520,821,16,93,656,593,30,625,354,105,24,438,44,664,688,23,116,51,557,0,326,329,21,812,195,49,190,159,35,759,60,478,125,118,121,48,364,132,627,13,167,33,110,921,242,68,274,158,96,127,252,137,668,77,5,731,578,692,102,795,865,131,435,165,22,604,586,271,473,455,382,146,120,10,88,144,65,64,966,212,71,67,128,83,691,897,160,576,551,36,724,615,223,82,50,647,7,95,197,621,115 }, +{ 6,26,2,25,74,1,29,19,42,138,51,108,114,70,324,14,23,386,133,103,5,96,85,13,719,75,453,52,27,66,730,602,220,38,309,65,388,457,58,17,112,122,50,128,323,357,64,235,280,238,670,421,135,165,150,475,7,523,197,32,145,278,87,500,899,401,550,3,218,273,758,208,113,86,83,603,532,222,115,711,12,137,21,895,394,46,24,163,53,45,97,67,125,283,28,458,236,117,34,11,262,294,90,36,801,792,141,516,484,341,302,33,802,297,204,57,95,4,756,594,226,256,134,480,891,545,99,450 }, +{ 20,43,356,414,49,595,858,359,819,107,392,319,97,7,422,470,376,612,173,5,246,0,427,14,683,147,197,22,660,95,230,347,945,713,32,344,92,61,830,362,325,259,33,31,21,12,4,904,707,623,126,509,18,2,77,357,232,165,140,105,102,729,196,153,426,117,408,841,518,98,64,10,17,16,501,663,560,76,44,104,38,558,125,391,23,324,178,134,46,54,29,13,40,51,330,120,493,279,168,581,263,180,158,116,241,455,159,81,28,903,298,90,900,66,465,441,423,202,198,30,534,237,58,24,3,52,887,845 }, +{ 68,35,0,101,65,9,124,154,175,114,336,67,28,69,149,189,103,133,1,24,39,83,50,230,55,345,16,429,251,7,283,218,252,346,18,389,547,71,118,364,100,2,238,140,646,109,46,22,12,439,166,289,36,23,49,484,30,399,14,51,732,13,86,930,532,398,64,96,54,323,694,88,21,722,158,265,5,75,38,48,329,181,705,212,194,952,480,361,419,443,208,302,868,782,520,10,95,108,503,128,191,115,246,237,335,267,637,37,167,160,137,723,702,141,806,565,309,105,264,831,826,701,604,535,60,330,562,375 }, +{ 23,13,51,115,15,5,202,12,1,165,2,0,21,197,453,457,4,318,269,10,32,7,141,9,6,401,3,180,14,403,37,11,341,77,22,719,120,569,217,475,308,177,33,18,260,365,17,961,64,59,99,16,317,237,48,28,629,128,450,730,90,117,137,386,352,523,31,40,494,291,102,247,8,421,509,72,24,498,125,391,196,76,49,44,57,221,36,93,468,203,326,30,45,153,210,899,89,304,216,482,372,38,110,95,82,19,275,134,402,758,342,139,349,98,901,678,752,670,873,546,734,27,56,351,65,817,61,86 }, +{ 121,195,407,126,60,271,146,199,167,16,107,493,98,132,191,223,35,279,1,212,20,362,727,92,48,43,18,21,150,522,446,259,393,467,5,783,590,598,360,640,612,939,649,354,71,246,7,156,834,68,49,125,293,24,0,147,55,608,591,163,14,376,534,31,660,541,463,683,807,97,13,345,589,23,953,668,562,22,32,818,221,108,955,118,284,96,581,595,10,50,28,470,765,695,575,346,116,2,37,61,356,535,566,95,12,51,105,101,70,281,64,273,423,230,202,441,44,9,755,75,697,653,465,38,768,173,175,77 }, +{ 31,44,299,116,393,492,144,268,918,367,456,434,105,489,22,948,127,384,963,9,98,28,0,49,125,242,293,206,395,940,36,512,55,51,203,1,855,93,911,617,159,95,272,23,959,332,763,301,137,21,13,141,64,109,86,282,10,170,120,286,320,551,202,417,599,239,165,685,7,2,616,5,128,730,143,457,352,719,48,39,8,253,160,37,692,326,327,386,450,421,158,269,12,276,18,506,791,473,115,24,16,244,391,164,558,318,403,338,623,131,219,14,523,401,284,30,197,499,900,951,308,479,221,260,728,169,910,935 }, +{ 23,457,51,401,13,758,719,165,629,453,961,730,197,901,386,569,308,670,391,3,29,14,12,739,231,34,52,153,832,828,115,341,2,40,46,514,176,365,5,21,33,0,838,181,4,328,45,54,555,130,509,494,540,734,552,583,215,196,507,227,11,211,99,117,361,180,38,10,1,351,894,134,27,26,186,50,48,543,152,749,369,202,62,236,421,102,600,468,403,171,161,155,57,9,528,17,880,753,36,477,19,440,903,488,377,22,585,108,37,7,6,697,899,757,523,475,576,326,67,479,469,281,867,805,801,659,657,587 }, +{ 105,131,22,36,127,115,180,341,201,219,272,31,365,169,98,1,44,317,9,64,239,187,141,197,498,143,873,320,752,286,901,352,86,164,160,258,327,244,224,185,109,159,217,13,535,253,538,144,116,0,374,276,261,776,170,28,83,634,531,2,569,533,269,616,95,896,251,824,482,165,37,55,128,282,10,5,578,49,546,654,947,913,371,384,295,208,8,860,21,137,202,954,312,284,30,23,567,509,125,574,39,285,318,268,206,203,843,623,33,708,12,77,716,385,837,7,638,120,237,301,254,308,293,16,951,3,395,15 }, +{ 6,1,2,29,14,25,52,38,74,66,26,65,67,114,42,19,108,5,69,83,46,388,75,86,324,135,103,7,17,27,85,380,36,24,70,150,278,87,138,97,594,886,357,267,50,51,18,484,650,23,458,45,646,21,732,398,95,208,64,96,273,13,238,0,124,22,711,550,12,532,323,302,28,220,160,37,335,63,32,194,421,133,262,451,602,283,386,309,730,10,172,467,453,235,168,3,401,782,115,610,670,48,719,81,145,16,20,500,137,226,58,280,165,603,244,866,141,308,222,197,189,112,547,53,99,73,475,180 }, +{ 51,453,386,23,719,730,670,13,758,6,457,401,899,475,629,19,2,29,569,47,26,165,14,894,421,130,801,236,197,108,412,27,74,1,308,754,5,50,17,961,133,45,52,285,523,711,657,78,324,196,128,137,65,34,115,25,99,138,357,59,480,42,114,46,361,867,53,739,58,4,743,403,603,366,11,859,38,262,12,594,202,22,235,153,64,391,218,0,516,487,10,145,200,3,33,444,70,117,901,57,223,134,54,745,390,112,283,95,123,309,96,21,66,803,220,86,341,18,365,402,176,37,328,77,488,73,322,250 }, +{ 252,121,18,101,16,9,167,132,271,60,146,419,364,35,265,363,212,0,158,604,329,682,68,647,109,848,907,191,520,463,39,24,333,28,446,159,821,55,621,553,118,126,593,411,49,71,407,23,286,634,105,195,354,107,404,857,13,676,374,310,259,22,538,562,92,100,31,10,21,793,199,1,327,639,108,48,98,806,51,7,709,346,522,932,166,147,5,590,96,137,254,465,272,20,150,694,64,706,12,14,203,169,95,36,124,77,2,116,360,44,865,402,125,154,128,33,627,576,185,810,455,156,822,777,37,326,127,842 }, +{ 283,503,128,432,269,529,63,26,275,789,193,678,318,389,102,122,291,77,59,177,256,6,862,342,25,18,235,57,216,445,27,4,573,605,349,65,11,85,403,523,643,84,813,630,494,397,233,40,56,73,475,202,138,416,210,718,42,32,133,415,381,736,453,412,511,19,619,91,240,255,80,22,64,114,23,289,194,0,17,123,561,87,174,152,343,81,436,66,300,297,148,95,78,3,217,51,803,510,370,341,139,137,130,49,45,119,396,372,90,237,544,141,74,476,129,86,21,650,421,378,352,209,196,33,288,247,402,701 }, +{ 2,75,29,52,87,74,226,69,220,1,278,135,46,262,70,267,14,380,6,66,324,38,357,294,7,394,222,140,145,97,331,25,335,189,24,67,92,16,112,853,571,26,42,309,163,50,138,35,550,582,103,172,83,96,337,451,71,108,150,5,58,486,785,537,21,60,298,19,462,583,600,461,171,32,230,48,65,366,45,500,606,54,774,12,18,36,149,223,114,37,443,641,133,632,287,563,64,409,85,22,764,388,314,238,151,950,95,61,27,307,155,86,28,415,565,17,246,423,43,572,113,0,186,609,251,396,10,802 }, +{ 97,298,172,140,69,66,7,189,267,92,501,314,60,423,135,24,232,81,71,16,43,150,462,74,14,330,192,517,173,426,733,149,251,35,885,273,6,279,38,18,221,67,278,508,83,607,2,91,380,52,125,448,124,29,387,458,87,20,36,443,230,861,108,915,711,302,104,21,568,526,723,324,12,68,48,5,362,1,336,335,0,28,22,46,541,126,357,103,25,451,877,726,246,398,565,50,138,879,175,963,65,86,165,809,391,715,197,705,191,118,117,42,712,386,204,160,823,674,54,107,467,399,441,633,831,572,563,236 }, +{ 475,421,805,899,403,453,958,615,817,51,801,523,120,401,670,730,450,326,494,23,576,202,386,629,569,260,115,365,697,77,165,110,153,351,318,24,117,13,317,0,341,221,16,704,137,5,196,342,308,757,543,22,719,197,11,4,457,20,9,302,484,758,300,10,7,217,6,193,901,391,91,361,1,791,515,862,291,273,118,102,66,38,36,2,402,125,114,661,643,352,474,134,82,33,133,756,642,511,48,373,240,847,735,608,480,172,135,52,43,21,14,859,244,116,29,370,124,754,180,35,42,898,886,851,842,811,770,558 }, +{ 7,128,20,14,177,97,16,112,43,202,269,77,107,66,415,318,2,135,141,275,147,38,18,58,35,52,307,173,10,356,298,29,210,414,172,32,6,0,137,192,24,291,70,22,102,494,9,87,334,13,104,60,115,64,40,678,342,46,49,186,33,217,5,121,48,90,23,21,4,155,86,232,403,59,28,74,132,37,227,324,319,61,362,402,36,126,95,92,431,337,178,400,609,1,376,71,51,294,665,631,392,151,549,105,81,359,353,304,230,196,67,349,15,134,544,311,285,278,884,352,259,76,427,262,194,50,496,171 }, +{ 24,14,7,28,2,16,0,22,124,1,65,35,108,168,149,67,109,69,71,18,46,55,158,49,289,12,92,95,185,38,6,169,5,420,83,10,189,154,159,584,140,150,36,68,429,164,21,671,484,50,398,60,105,175,29,302,143,703,239,103,86,23,64,344,424,32,51,9,336,251,230,273,577,114,747,133,37,118,194,66,13,48,267,97,128,835,505,52,170,137,320,892,253,115,236,570,388,347,374,125,286,54,20,345,202,458,314,638,246,654,610,25,75,708,17,172,312,96,135,677,141,19,221,3,650,799,547,601 }, +{ 26,80,122,73,27,25,42,684,19,209,445,573,667,17,680,690,106,6,255,297,611,483,4,495,59,119,250,45,84,378,113,469,65,436,128,311,85,64,29,53,200,56,648,396,235,61,742,11,136,76,383,77,0,86,32,161,62,22,814,361,256,14,12,5,137,123,40,389,194,491,503,813,283,269,862,21,3,95,112,454,179,63,141,129,33,177,233,57,202,803,2,102,70,10,34,240,402,18,416,624,275,145,605,614,52,358,504,665,527,343,133,114,108,139,50,881,151,58,205,51,790,24,210,38,174,49,216,285 }, +{ 107,259,465,362,376,595,534,660,683,470,581,147,612,20,858,43,427,49,5,178,246,786,0,35,97,356,279,878,21,423,929,61,142,14,230,126,70,132,146,904,590,325,319,493,22,199,48,32,501,392,298,953,95,77,414,640,347,345,359,798,31,13,198,7,173,58,64,121,105,711,28,125,150,344,12,518,10,941,713,603,4,60,33,450,1,481,92,202,98,120,687,55,207,23,16,18,626,197,102,522,819,163,232,771,113,577,17,318,37,65,357,263,40,324,422,260,623,59,44,663,408,702,494,441,68,455,845,237 }, +{ 131,30,190,856,228,406,224,530,88,863,219,778,274,944,613,539,816,258,187,100,435,871,39,160,321,840,44,116,31,166,93,625,137,36,957,531,127,442,213,669,466,716,385,1,144,839,492,202,827,829,96,382,908,242,662,731,9,254,120,367,276,456,371,960,203,795,201,98,730,574,83,13,924,410,800,926,326,21,217,851,318,523,836,717,268,473,931,352,533,418,23,49,141,28,260,110,77,22,86,310,567,494,457,64,10,55,244,737,251,373,728,128,299,125,105,0,51,48,679,822,489,5,616,265,295,284,615,102 }, +{ 113,311,611,6,491,80,667,25,209,45,26,27,684,42,122,73,85,396,255,145,106,17,614,29,648,297,690,19,454,61,64,624,4,5,416,445,112,32,2,138,76,65,74,151,483,90,174,66,665,119,22,287,1,59,86,378,128,833,194,573,881,141,70,52,0,525,742,214,315,243,544,680,14,87,216,469,11,746,179,250,495,887,95,12,10,222,21,348,814,289,280,204,84,72,436,77,123,56,33,114,177,235,53,137,102,81,161,631,136,46,790,503,58,240,292,208,200,153,36,202,63,7,186,50,135,57,49,37 }, +{ 15,0,1,5,23,9,51,13,2,7,12,120,115,77,260,82,180,341,202,4,450,141,10,8,351,197,21,901,6,817,403,137,102,14,64,22,165,342,457,269,93,16,3,32,11,110,37,128,318,177,401,40,494,18,365,523,854,453,33,36,421,352,475,217,28,210,922,31,569,898,317,125,44,719,30,730,59,86,144,24,48,203,961,17,49,373,116,98,99,308,326,117,752,386,576,153,55,291,39,629,899,95,221,196,139,642,661,402,237,143,244,134,160,90,509,670,391,65,498,349,89,54,127,57,805,275,253,758 }, +{ 219,127,258,98,395,924,421,276,293,697,944,567,201,284,453,401,242,512,308,51,860,475,23,105,295,131,137,272,770,301,843,670,116,299,578,386,935,125,261,13,763,31,576,456,791,115,44,492,403,221,662,268,9,730,801,269,202,49,365,964,55,805,616,159,699,943,22,141,506,224,327,206,541,21,551,144,39,393,165,326,0,842,535,203,36,743,910,28,170,332,318,282,494,949,716,253,569,197,1,143,10,341,533,5,160,615,457,391,916,317,911,187,737,286,384,93,24,599,109,371,180,900,783,244,352,12,623,48 }, +{ 15,352,752,317,180,217,854,141,351,82,1,898,36,341,260,115,23,13,365,21,120,110,10,33,291,922,64,901,372,77,349,160,143,244,51,137,32,498,661,269,5,4,202,304,197,48,253,40,237,102,450,457,373,318,11,22,35,482,251,67,55,9,719,165,59,117,72,153,177,196,12,83,28,961,579,247,326,45,96,17,864,69,523,2,468,125,0,86,730,811,128,817,134,93,90,148,144,437,37,111,403,342,76,57,546,230,704,345,308,139,89,58,873,29,203,164,531,50,30,3,275,559,127,855,629,473,8,56 }, +{ 129,123,257,184,162,17,205,249,183,769,80,119,497,229,106,5,45,130,84,90,59,152,3,99,128,4,618,27,57,77,245,40,62,452,556,378,19,214,460,136,14,12,240,56,26,2,202,432,6,33,61,266,11,209,402,403,64,21,269,102,381,25,141,29,504,76,139,318,54,73,305,34,217,619,179,1,10,32,177,38,361,207,475,383,37,483,22,210,255,115,494,58,196,524,65,23,710,775,53,306,153,742,86,180,421,36,469,137,117,15,51,216,48,134,313,0,111,72,161,285,288,7,757,250,880,503,151,372 }, +{ 102,33,128,64,77,23,51,202,141,269,13,137,40,10,196,22,0,86,117,153,403,318,342,210,65,1,115,139,15,95,134,475,453,180,165,2,177,217,14,6,494,291,197,99,36,4,59,49,28,5,7,11,402,120,12,114,194,3,18,9,19,31,125,237,82,352,17,16,21,110,275,401,44,260,24,285,719,349,25,93,326,457,164,351,111,300,216,27,317,421,133,26,37,203,143,312,116,108,8,277,498,730,365,678,341,98,244,20,386,523,308,105,29,752,372,32,485,450,901,391,361,57,283,48,30,208,96,38 }, +{ 7,14,172,107,92,2,16,135,46,35,232,294,60,97,87,5,278,24,126,29,220,52,38,21,262,66,192,202,58,20,70,48,43,171,359,54,32,448,356,337,37,353,226,112,75,298,1,526,18,12,872,132,64,409,140,22,155,387,334,128,121,392,357,318,307,461,331,0,150,314,49,146,61,415,86,69,71,620,673,230,494,380,324,6,362,355,330,45,259,77,888,137,10,13,246,95,186,141,74,23,50,279,147,194,28,25,51,9,4,609,151,177,55,849,145,199,31,115,102,550,597,59,36,216,90,17,441,222 }, +{ 576,403,615,805,523,475,326,817,494,421,120,51,450,859,137,202,453,260,670,402,77,23,551,401,386,33,958,318,704,342,269,197,196,0,165,480,373,323,65,153,899,753,629,10,803,697,102,7,569,59,4,2,351,90,12,13,962,677,110,9,801,118,40,6,95,5,128,28,143,730,446,124,122,114,14,917,306,207,82,621,407,86,16,164,115,154,64,842,886,848,735,661,484,397,302,247,174,149,141,71,60,1,719,216,104,81,925,628,457,208,168,49,25,22,844,824,685,338,175,109,259,810,236,933,932,854,804,788 }, +{ 141,352,202,269,137,217,180,372,349,9,18,0,51,115,23,13,16,365,221,318,165,317,2,494,752,7,125,120,77,98,20,10,64,49,341,291,22,901,95,260,854,351,308,55,30,28,403,144,93,44,922,873,326,223,82,48,39,35,21,14,197,203,24,153,33,102,753,498,473,156,88,65,386,32,717,275,268,31,196,177,817,730,704,678,661,482,450,437,393,373,288,241,195,132,121,118,110,105,101,36,1,947,475,237,5,253,244,206,201,168,164,158,143,128,961,954,864,751,615,576,551,546,523,509,489,453,442,421 }, +{ 23,202,51,141,13,165,1,21,5,180,197,269,318,137,48,128,15,7,403,120,12,102,125,64,2,33,14,326,523,450,260,93,37,16,386,32,9,4,237,6,115,352,77,203,457,153,196,82,18,117,0,110,421,494,453,401,40,24,817,98,351,475,99,221,730,144,391,31,3,44,96,291,30,719,308,36,39,341,342,11,217,35,8,899,50,20,402,854,615,922,116,160,72,28,17,22,45,10,373,317,89,88,61,59,285,509,752,365,372,58,19,901,46,139,70,349,25,177,65,86,293,90,247,498,54,576,758,898 }, +{ 25,151,145,6,122,174,29,138,74,665,42,416,113,614,45,746,525,287,348,311,396,544,85,66,26,81,280,243,624,355,52,222,32,64,65,680,4,59,19,0,491,2,22,90,7,204,194,104,77,216,112,445,1,61,5,128,86,177,651,91,214,17,95,209,611,141,648,297,80,415,21,102,87,233,454,33,27,43,881,503,20,137,636,573,10,18,14,117,289,76,58,869,202,153,337,496,186,196,789,106,315,11,40,28,135,49,667,269,192,36,13,57,12,115,134,750,70,208,63,72,73,46,597,529,51,48,684,173 }, +{ 166,190,382,254,30,100,731,829,438,264,274,88,795,131,613,578,716,625,9,93,921,616,435,404,265,1,530,203,31,44,960,36,39,827,160,127,272,326,98,924,96,473,224,23,410,116,252,77,144,310,137,856,105,219,101,33,83,0,863,110,800,13,812,51,128,406,478,228,125,521,120,639,21,49,836,102,593,217,242,28,897,772,931,5,688,957,286,115,64,141,691,269,656,317,321,10,494,159,187,197,291,86,363,576,944,67,22,450,201,523,55,411,373,82,822,557,260,456,704,329,367,225,276,48,202,268,213,32 }, +{ 132,21,379,5,32,13,23,195,626,51,48,121,70,1,850,296,687,883,156,771,61,49,407,95,146,125,105,55,652,4,98,761,77,58,33,518,260,18,623,586,349,340,120,82,523,522,354,163,24,16,0,293,45,87,786,165,929,75,64,888,763,535,308,102,31,698,40,269,202,159,37,35,9,845,622,207,663,153,117,900,608,386,817,591,351,347,310,241,221,199,170,158,115,76,65,59,28,22,706,223,774,640,629,461,415,116,101,71,303,60,941,638,427,421,401,391,372,197,196,127,299,937,854,768,475,453,450,403 }, +{ 254,9,272,0,49,688,39,101,578,131,159,518,158,327,333,105,286,68,716,224,252,28,890,344,616,310,846,829,219,827,228,613,24,35,364,16,30,18,938,371,406,166,419,65,455,22,95,627,793,709,98,910,124,154,51,100,31,127,48,1,21,385,190,23,320,242,137,187,374,347,258,36,44,13,301,404,109,856,116,170,125,118,96,160,442,656,424,865,438,931,67,168,64,55,239,623,128,737,265,924,553,221,202,10,71,677,86,83,88,916,60,329,293,538,7,577,512,5,761,395,912,421,212,144,816,944,506,346 }, +{ 16,35,60,20,7,18,126,68,14,43,92,107,118,101,55,71,28,121,9,154,0,97,109,24,621,150,2,191,419,252,5,202,279,363,917,22,21,124,364,169,439,604,199,49,346,446,175,158,12,167,575,48,38,13,493,246,185,10,362,32,95,51,259,375,1,39,598,628,932,566,421,6,23,64,29,108,146,925,61,46,356,494,318,132,140,345,694,149,359,147,65,407,329,376,125,403,697,273,302,212,96,69,137,50,848,25,467,298,77,453,173,230,570,826,221,66,70,143,677,535,265,608,551,58,815,128,907,100 }, +{ 15,1,13,2,341,5,0,11,4,3,10,7,33,23,12,6,117,21,9,40,17,901,197,8,134,77,22,37,365,14,32,165,317,153,102,180,217,457,202,51,961,569,141,128,115,318,38,31,64,19,16,291,137,269,196,372,111,59,44,36,25,352,349,57,99,18,509,72,30,27,752,120,93,54,45,41,342,48,110,49,453,498,79,177,28,719,94,403,402,386,89,82,474,29,285,139,86,482,56,237,361,95,55,391,26,277,116,873,34,210,76,758,326,50,39,143,90,328,260,308,247,125,898,164,24,494,401,421 }, +{ 78,444,47,19,26,390,813,130,108,453,711,27,412,730,65,122,680,283,690,113,51,235,236,73,128,630,196,297,14,250,123,53,194,183,200,133,64,445,368,90,719,3,381,42,23,389,269,6,472,650,249,56,475,32,59,4,57,45,34,648,605,624,503,114,17,25,736,256,22,803,141,318,153,315,95,214,0,684,119,99,18,432,216,899,85,289,46,457,243,33,11,436,480,50,322,177,29,152,129,54,13,657,304,285,84,52,401,117,881,255,714,202,665,597,2,961,12,339,38,361,77,524,115,58,24,145,670,402 }, +{ 2,29,7,52,66,61,45,112,738,113,74,14,5,151,6,315,20,592,70,128,32,425,16,25,1,222,87,135,177,43,64,491,186,59,38,655,21,72,76,46,290,90,243,141,115,77,631,58,106,19,337,207,145,137,4,24,454,833,97,0,26,40,227,311,402,192,36,27,89,210,35,292,269,48,86,10,202,209,496,18,217,22,102,226,138,17,887,204,122,65,318,33,28,104,172,51,255,13,23,42,148,644,216,790,307,287,174,180,92,69,291,95,49,415,75,416,483,549,179,275,263,270,107,9,334,869,214,155 }, +{ 145,112,74,66,26,6,29,70,396,287,138,87,135,222,226,25,644,151,42,19,122,307,651,624,635,337,648,45,2,113,294,58,85,632,849,5,416,75,52,172,61,573,445,27,1,315,884,665,64,7,53,204,32,461,95,17,97,297,174,163,436,220,280,525,192,65,792,73,14,186,86,609,881,353,36,311,549,72,243,22,194,331,148,500,235,80,69,46,680,278,38,312,48,90,21,106,37,409,491,128,10,114,614,631,209,67,415,227,50,108,237,250,580,155,76,141,171,544,262,24,620,12,43,133,611,4,334,389 }, +{ 92,126,60,16,7,24,35,232,71,14,150,140,121,246,314,146,172,132,199,107,18,267,46,278,572,230,108,451,330,236,380,388,462,135,68,38,830,633,392,279,97,2,448,309,649,517,672,32,357,21,5,75,9,118,22,55,167,87,582,192,526,594,366,156,359,298,191,195,64,653,28,50,189,324,563,13,220,0,1,66,467,12,10,423,441,149,733,52,262,877,48,29,70,95,49,516,133,36,54,508,407,360,354,598,550,101,69,37,58,65,271,640,711,212,571,86,345,895,696,273,125,522,23,90,603,72,739,163 }, +{ 13,115,569,734,219,341,127,365,197,509,629,23,98,538,873,901,286,762,55,206,258,165,253,332,170,169,638,535,180,391,116,276,844,31,44,12,546,0,395,164,1,293,10,752,911,36,964,143,282,37,384,512,237,498,308,616,301,740,5,141,352,8,261,9,2,943,654,317,120,284,38,338,913,601,295,244,105,7,3,201,468,924,125,923,910,144,239,21,4,479,482,242,6,86,131,417,961,185,22,64,758,374,110,841,533,401,93,160,15,944,434,82,951,567,843,268,14,564,837,299,272,719,28,860,109,11,351,935 }, +{ 151,58,396,53,287,74,27,73,297,549,29,651,145,70,445,112,113,315,436,26,331,6,573,250,75,45,334,52,307,17,5,34,80,163,2,138,19,491,61,881,220,409,631,262,1,614,632,226,171,106,611,186,32,200,66,50,62,311,25,46,845,767,500,21,135,644,243,223,87,14,48,42,64,3,472,108,324,76,122,161,635,684,65,294,56,38,136,95,72,255,114,849,780,278,209,37,90,852,84,714,7,51,89,148,624,237,383,36,207,236,502,22,12,133,86,322,659,227,119,69,222,11,4,96,309,263,179,33 }, +{ 214,289,90,874,104,751,64,835,312,249,204,65,750,288,875,348,519,174,247,194,74,715,81,138,32,280,524,784,192,636,319,437,879,0,95,85,6,681,217,173,954,91,29,177,86,597,792,77,317,671,422,22,115,864,888,52,202,752,216,673,588,712,18,123,180,233,306,43,355,304,352,20,76,693,579,66,4,5,25,51,21,343,59,237,2,28,61,426,235,577,33,10,397,316,707,287,145,370,24,102,49,291,17,108,84,945,222,97,57,208,56,401,42,526,560,13,730,40,402,23,117,207,474,7,484,72,494,36 }, +{ 9,39,166,28,35,158,49,30,101,175,439,0,346,1,364,64,88,274,100,168,36,67,404,310,333,952,159,55,345,505,190,706,254,420,86,237,23,336,68,22,251,344,51,69,194,114,164,131,570,208,180,756,10,799,143,399,109,83,50,115,141,967,438,768,584,868,128,752,105,95,103,189,286,252,352,5,323,2,230,65,596,289,137,498,7,702,165,13,12,577,503,535,160,125,202,48,547,455,244,610,96,21,16,253,169,857,217,154,797,646,656,228,197,530,747,149,810,124,930,272,865,597,98,185,31,24,389,625 }, +{ 341,901,15,1,365,10,569,0,2,5,180,4,3,115,6,31,7,197,9,13,22,317,951,165,44,629,143,23,253,401,116,244,762,55,93,242,479,752,30,217,51,37,141,28,873,137,638,36,202,203,12,21,86,386,421,77,24,758,11,268,453,318,269,110,33,576,64,811,352,326,164,16,494,506,498,308,670,82,717,642,351,206,120,338,170,261,32,125,40,851,805,260,102,859,8,403,54,661,961,291,144,105,965,98,35,898,482,391,373,153,509,492,958,117,17,499,615,38,824,559,456,384,854,457,282,89,564,237 }, +{ 202,450,120,77,260,318,494,128,102,817,15,269,51,13,342,403,23,291,5,523,137,326,21,141,33,1,922,32,82,351,48,854,177,859,402,64,196,70,615,31,475,40,4,153,285,59,0,12,22,10,2,9,95,98,61,90,28,210,30,17,453,194,117,139,8,352,96,49,275,35,678,899,113,349,165,65,180,6,730,93,692,115,58,704,217,37,203,719,116,576,125,50,457,75,44,421,163,216,36,134,7,39,3,416,14,67,197,25,373,110,341,207,112,86,386,87,56,127,99,11,223,551,365,300,105,16,19,237 }, +{ 241,32,13,115,197,180,165,316,64,288,125,21,23,237,247,141,95,16,22,92,167,522,191,546,498,391,341,28,752,177,586,4,48,509,447,118,459,90,5,352,59,901,264,225,304,350,35,199,126,363,18,217,24,410,468,691,246,841,744,10,478,102,77,649,386,117,86,49,317,308,302,212,146,33,71,954,156,72,46,674,93,31,0,640,411,923,903,645,686,271,698,2,521,140,602,591,120,36,375,68,12,864,321,695,873,758,340,336,318,269,179,143,65,897,772,446,880,839,682,366,190,57,30,818,781,303,953,388 }, +{ 16,24,35,71,60,14,267,18,46,140,69,1,7,108,189,2,92,50,75,230,309,68,236,38,335,220,394,54,366,52,486,21,150,149,246,48,29,87,67,5,571,388,9,181,133,0,103,443,28,163,537,171,55,223,126,545,121,70,12,96,262,328,565,83,32,118,65,516,58,218,37,191,330,563,64,167,22,114,13,51,23,238,251,622,281,278,487,294,399,99,696,115,36,10,582,135,514,86,6,128,517,950,675,674,273,98,101,594,172,49,72,146,95,314,451,155,45,409,212,433,221,345,141,66,247,97,733,125 }, +{ 15,898,180,197,901,341,638,165,33,10,115,77,365,1,4,217,317,5,102,117,253,13,951,752,3,134,2,762,153,569,37,21,0,196,40,11,244,23,6,82,7,120,143,291,32,141,137,51,31,873,55,12,351,44,260,349,352,401,372,479,177,93,22,342,116,36,9,629,308,59,758,450,386,210,854,139,110,111,482,457,164,17,922,421,961,202,719,269,203,373,247,523,28,125,160,48,437,64,326,242,57,704,509,304,14,318,730,498,403,506,148,817,670,8,90,453,494,282,144,72,127,275,579,221,206,899,54,89 }, +{ 523,193,18,84,730,817,90,56,65,240,214,475,453,196,33,719,4,233,202,32,51,153,289,152,123,421,11,194,403,129,99,96,386,249,135,102,40,27,23,361,847,352,59,511,91,457,177,370,134,128,117,510,13,561,75,753,643,388,278,247,204,115,494,450,415,304,77,524,217,216,64,6,5,3,174,701,880,659,609,437,433,432,381,287,260,257,165,139,120,111,87,74,66,1,402,372,318,269,21,899,183,63,141,650,605,770,743,735,718,667,629,611,569,544,543,348,306,288,209,207,162,95,93,86,61,57,49,48 }, +{ 151,29,112,74,222,66,145,2,287,624,52,6,416,45,7,113,186,25,122,665,549,337,87,204,243,5,135,644,311,58,884,226,138,396,61,32,192,70,42,525,415,614,315,19,64,491,651,26,4,227,174,609,544,20,21,43,14,27,128,22,172,632,0,59,209,65,353,97,631,72,1,85,580,86,17,445,80,454,307,104,90,334,81,496,177,46,635,141,255,48,37,77,75,10,155,869,659,76,294,106,49,73,11,95,38,36,115,673,69,18,216,712,148,173,792,33,331,102,280,12,16,24,137,881,89,297,91,28 }, +{ 13,2,23,0,77,5,217,7,1,6,33,3,15,51,10,115,141,102,180,64,4,9,40,317,11,197,14,117,165,153,341,22,352,237,17,365,134,269,291,125,16,202,12,196,372,59,349,177,752,498,128,28,18,32,318,8,36,95,457,19,86,49,210,143,391,901,24,342,21,304,569,31,509,137,139,120,27,386,57,25,164,98,29,111,401,403,45,26,244,864,482,546,110,44,961,308,38,275,453,37,35,93,48,402,247,629,20,50,46,719,873,55,475,65,56,30,72,579,203,523,99,253,494,468,170,54,116,76 }, +{ 28,67,0,65,1,114,103,83,69,50,9,55,2,109,124,46,133,24,14,35,22,154,36,68,38,547,389,218,7,283,323,16,54,18,12,160,194,420,49,251,175,5,75,185,238,39,289,429,158,96,208,168,532,29,86,480,505,531,484,189,503,169,149,797,782,361,21,101,570,52,346,71,181,336,927,533,48,95,37,108,51,596,10,252,23,159,128,799,105,285,418,64,118,535,335,398,732,6,70,803,419,137,220,13,866,171,756,394,30,32,309,328,140,115,87,60,230,433,345,141,58,439,831,402,610,366,99,754 }, +{ 121,271,132,167,146,18,363,101,411,621,354,212,520,60,9,647,932,446,806,16,195,407,463,777,364,562,35,360,522,68,55,1,98,118,31,252,706,694,191,0,24,107,936,664,676,604,199,48,126,781,23,590,156,28,591,150,21,447,39,125,5,109,264,49,759,265,96,22,404,33,13,10,100,44,51,905,259,71,346,857,682,116,223,848,941,7,92,154,93,105,108,478,77,467,286,821,166,64,638,907,765,691,628,279,178,14,203,102,12,30,120,393,158,159,593,834,724,419,147,50,293,163,815,786,367,70,634,2 }, +{ 26,6,85,122,396,624,42,25,573,445,174,416,665,19,680,648,138,145,348,881,64,436,214,113,235,65,297,544,194,90,45,746,525,280,112,289,74,233,32,53,27,355,29,222,287,389,17,151,86,80,95,813,81,73,256,5,249,141,283,91,597,0,343,76,22,66,87,204,415,874,209,312,690,524,611,255,288,63,644,123,243,70,614,216,4,128,2,77,119,61,370,106,52,491,315,58,10,56,651,21,12,519,18,14,835,59,250,352,247,114,49,179,636,205,684,619,311,57,84,28,337,667,177,108,11,208,503,240 }, +{ 146,16,199,24,71,18,126,60,92,121,271,156,360,953,640,108,132,674,35,246,118,273,32,354,195,649,7,314,150,458,522,95,517,167,446,191,21,140,340,463,22,388,68,107,566,575,241,727,10,345,591,359,296,48,586,14,407,28,5,608,149,672,594,602,660,1,467,363,212,13,9,823,49,267,101,225,124,55,232,37,653,0,392,877,50,447,330,702,12,809,462,441,230,508,46,668,189,77,303,236,733,23,698,621,115,423,411,399,298,97,350,247,72,796,451,64,658,217,69,589,309,51,928,264,168,102,163,133 }, +{ 51,23,13,453,475,730,719,899,403,202,401,21,15,64,523,115,95,457,33,12,141,308,32,102,4,128,40,19,11,421,5,117,386,3,99,22,153,125,26,817,120,25,6,961,165,1,180,196,17,197,494,57,134,221,0,194,2,237,260,7,670,48,27,365,37,629,450,49,170,65,14,569,137,342,236,38,352,10,56,29,20,16,468,316,241,402,391,77,177,350,84,34,283,269,217,82,9,247,546,351,318,42,59,372,903,841,558,317,129,86,47,46,41,28,18,326,615,341,90,291,164,801,509,119,76,63,54,24 }, +{ 151,396,53,113,73,445,145,6,27,297,573,26,287,315,112,436,74,58,138,491,45,70,881,19,614,34,250,61,29,549,611,80,17,631,632,5,307,2,200,644,32,222,64,25,52,106,42,1,243,87,66,75,845,226,311,186,334,849,95,122,472,322,294,21,651,714,255,684,48,171,331,194,65,161,209,14,128,86,72,141,46,624,50,135,22,635,262,227,85,108,136,7,220,90,76,163,235,176,114,312,280,62,409,10,337,4,148,56,454,283,237,12,469,37,51,780,214,667,236,3,767,133,36,89,495,155,0,23 }, +{ 223,774,1,888,385,260,98,863,202,269,421,730,96,349,697,453,141,393,622,834,318,751,87,418,791,765,801,922,163,21,335,352,5,7,120,13,291,854,64,23,717,851,293,30,284,372,31,678,371,217,947,351,70,51,32,137,48,77,128,82,442,450,44,49,36,752,125,386,541,931,304,873,0,498,22,2,201,276,807,317,10,670,180,102,6,75,237,39,373,33,69,770,144,177,95,17,16,160,116,100,842,203,197,968,949,221,115,275,165,494,475,127,898,58,963,523,403,12,954,537,45,50,433,872,652,526,268,241 }, +{ 60,16,126,92,121,35,150,246,7,598,279,199,191,167,441,107,71,97,146,195,271,18,298,24,407,356,517,230,1,14,98,508,423,140,674,132,359,223,653,212,48,68,399,783,345,21,314,108,607,43,467,232,362,649,118,392,189,20,5,0,149,702,727,55,22,695,28,446,672,69,66,49,493,10,50,388,809,273,2,522,31,46,640,293,125,9,13,95,101,267,173,501,393,834,877,38,575,67,32,75,37,796,346,779,608,12,156,589,236,566,163,96,221,104,360,375,535,64,711,463,23,175,755,172,414,562,953,354 }, +{ 22,105,28,1,239,95,55,170,320,301,127,36,98,31,49,109,9,2,0,168,35,67,185,169,12,14,86,512,7,44,395,159,5,194,293,312,114,64,83,928,713,740,65,24,23,623,21,299,10,338,16,51,242,50,125,141,933,420,13,237,37,164,844,286,143,69,175,584,892,202,48,6,46,371,103,133,269,116,685,39,916,137,115,160,599,208,272,128,96,284,158,737,762,638,180,244,276,187,763,308,165,841,761,479,505,401,347,424,318,389,30,352,870,18,131,32,197,219,965,327,538,54,456,900,506,253,385,874 }, +{ 2,292,6,119,29,207,76,61,306,45,5,790,17,240,32,58,106,1,151,476,710,14,214,209,25,179,84,52,70,64,90,7,112,26,311,19,618,72,128,129,27,255,123,655,460,59,249,141,86,4,80,40,113,22,115,46,10,245,464,205,145,243,263,358,77,56,36,74,65,315,504,454,148,3,186,95,0,519,50,177,75,202,21,269,38,257,66,42,137,23,16,769,51,738,48,136,33,162,18,184,62,241,99,227,24,318,194,87,491,37,11,12,57,425,833,180,402,49,237,89,287,216,13,720,102,54,631,138 }, +{ 51,221,23,115,254,438,48,421,530,541,13,21,401,613,308,39,688,386,125,840,228,551,453,960,827,1,166,96,406,326,393,627,670,158,49,697,656,829,846,770,333,856,404,5,180,30,863,190,202,669,518,890,219,83,159,98,50,131,33,102,310,217,165,137,743,77,32,101,31,752,679,15,105,615,468,391,341,317,0,224,9,498,352,258,286,455,364,187,778,197,344,44,865,539,457,482,272,128,116,37,28,949,403,876,385,963,373,709,931,719,64,574,68,35,160,274,268,616,345,442,95,492,450,293,843,839,22,261 }, +{ 5,1,22,21,2,12,14,36,48,0,54,86,32,10,28,50,65,37,38,23,51,17,7,208,3,9,114,45,281,13,4,137,323,46,24,6,99,95,18,49,58,55,115,480,72,202,96,745,285,194,181,67,64,16,270,89,133,218,83,168,403,11,430,29,61,155,128,160,503,361,540,223,77,244,221,389,318,757,164,103,141,283,76,402,502,401,59,859,920,40,341,543,290,31,803,269,513,735,124,165,308,33,75,253,421,143,838,754,69,90,180,197,125,102,19,494,453,475,966,105,27,289,109,584,576,569,765,148 }, +{ 182,23,363,284,296,447,340,98,1,291,698,367,93,668,658,156,144,21,269,70,433,379,31,318,478,354,203,44,691,335,180,13,223,125,116,937,586,411,326,110,107,48,18,421,664,724,273,197,141,51,692,195,87,5,67,914,687,486,759,349,551,386,375,268,232,149,102,60,35,33,115,795,251,69,65,49,415,364,77,771,626,163,453,281,165,127,121,24,10,360,806,791,393,336,39,475,372,217,850,662,942,682,518,484,361,359,221,196,146,133,128,117,75,64,22,12,7,883,591,457,132,16,647,153,120,68,801,670 }, +{ 45,498,17,106,546,115,32,62,873,41,197,180,509,54,129,468,243,255,123,752,165,155,38,341,13,241,292,217,720,449,57,136,536,734,569,148,51,476,248,428,214,139,76,369,221,482,328,237,542,365,72,89,339,181,23,227,350,64,90,211,117,11,519,270,290,405,721,587,457,21,901,50,128,741,710,141,552,313,430,162,2,540,130,202,15,102,528,247,488,317,209,157,77,48,14,3,391,152,431,308,629,4,618,743,281,179,5,183,33,40,12,37,358,361,266,46,86,961,828,471,773,372,421,543,249,507,119,10 }, +{ 32,95,64,237,92,180,246,22,241,197,125,115,312,652,126,638,49,316,13,21,5,165,288,534,7,4,48,0,28,23,498,392,296,107,888,340,90,303,658,626,247,70,441,10,16,365,953,468,260,230,546,18,24,774,12,379,883,98,490,482,863,31,877,508,391,141,51,830,933,354,9,517,347,150,314,385,177,132,65,146,645,40,653,76,33,2,341,217,14,269,44,698,263,156,195,850,649,633,199,120,360,359,563,923,509,216,128,86,60,733,873,267,35,1,442,168,330,293,929,545,11,622,591,87,77,641,437,660 }, +{ 15,33,341,217,13,77,141,4,317,102,23,40,115,180,365,351,117,21,5,352,854,1,137,197,11,10,153,51,82,901,32,134,59,64,202,196,36,244,269,22,922,165,2,291,260,177,752,143,0,120,898,48,3,318,12,9,372,253,128,457,31,569,28,37,160,30,247,86,139,55,44,72,7,57,349,110,90,237,498,342,304,111,719,579,450,96,8,730,403,308,811,49,523,164,386,35,125,482,437,17,6,14,961,39,98,494,116,93,275,642,401,210,95,221,76,277,105,717,373,203,216,38,873,899,18,817,326,509 }, +{ 120,260,1,15,141,217,352,13,77,82,93,351,110,23,180,317,349,51,202,21,854,326,165,64,269,128,137,5,291,304,752,36,373,372,922,450,318,177,203,197,115,48,457,10,342,2,0,898,102,160,12,16,901,22,125,386,86,9,144,32,96,35,719,28,7,365,341,244,275,494,210,730,18,95,83,661,4,403,237,523,498,247,143,864,473,90,14,961,437,33,817,40,37,8,678,482,253,579,569,98,72,24,50,475,391,67,69,954,717,288,164,65,49,6,206,216,55,17,134,947,899,576,308,76,316,196,490,402 }, +{ 23,13,51,115,77,217,141,177,33,202,4,64,269,102,0,128,10,2,7,318,40,59,32,14,6,11,352,22,9,291,90,247,180,237,137,1,165,16,317,21,210,197,117,153,196,18,125,49,403,3,95,15,17,349,341,304,12,372,216,365,342,19,36,134,453,86,98,28,308,24,31,244,494,93,275,143,475,57,401,44,48,5,498,457,241,120,203,20,8,105,144,221,25,326,110,752,29,65,523,164,37,55,30,719,421,402,139,39,160,27,116,260,26,96,35,300,437,961,82,569,38,45,901,99,72,253,678,316 }, +{ 478,264,724,520,682,98,1,772,905,664,759,821,9,223,21,48,411,604,909,447,363,23,265,100,13,410,897,166,5,907,293,31,88,329,252,39,101,0,51,125,639,848,32,167,96,647,691,807,781,541,686,382,284,521,44,321,777,393,621,274,118,212,18,676,116,163,69,271,936,834,221,593,812,16,24,914,30,10,127,765,115,37,49,354,335,217,144,68,93,386,105,404,586,22,33,4,83,70,806,165,553,697,932,60,77,50,744,463,71,64,286,421,254,281,435,102,35,12,656,371,191,225,963,446,121,213,201,763 }, +{ 29,20,128,7,43,77,174,74,2,23,32,416,52,102,66,491,104,113,81,33,13,6,51,342,222,202,173,14,0,21,525,59,137,45,192,141,319,5,318,64,10,210,22,291,631,18,72,122,90,145,269,196,151,86,61,58,16,665,4,177,40,355,204,153,134,402,28,285,42,738,544,135,48,216,25,275,115,85,24,65,217,9,12,87,76,833,415,226,179,198,38,138,114,49,97,84,243,337,315,311,287,36,426,869,422,172,89,255,17,209,784,112,454,317,425,35,349,117,95,27,494,208,91,194,503,232,30,11 }, +{ 2,1,5,14,58,7,52,29,46,61,290,45,75,38,592,425,50,270,70,16,32,163,24,155,21,186,171,72,6,227,69,334,263,207,220,17,243,663,48,18,89,76,0,67,400,655,738,35,408,54,112,87,148,36,64,449,66,23,106,22,307,10,262,128,292,74,409,115,119,135,9,476,90,65,141,278,37,767,103,83,86,28,720,140,95,780,255,96,25,405,27,77,3,241,51,12,13,306,20,97,666,179,223,40,19,177,4,428,92,151,496,209,172,26,502,113,84,202,790,137,60,431,833,240,659,331,217,324 }, +{ 80,483,209,106,6,26,17,495,255,833,378,469,113,742,27,25,19,814,119,445,161,887,45,42,76,667,61,64,491,151,90,573,73,235,84,29,65,32,136,614,179,383,128,112,145,297,122,2,527,396,214,62,56,240,85,5,53,862,194,648,123,311,611,624,207,141,58,86,315,95,14,22,306,292,34,4,454,436,59,684,790,0,1,289,440,250,129,738,205,70,416,361,3,249,245,57,269,287,665,52,108,402,202,46,12,177,11,114,216,21,137,36,10,200,33,115,72,180,74,138,50,28,318,247,312,51,585,148 }, +{ 98,223,393,834,1,354,195,791,697,447,293,284,360,541,781,807,51,664,591,156,421,411,48,801,163,883,948,668,765,386,18,586,132,121,23,963,363,125,21,446,551,759,31,621,949,770,407,777,271,658,146,730,167,670,13,116,936,453,5,367,296,478,144,371,221,96,70,9,44,202,105,719,127,687,379,49,118,55,60,698,101,842,626,318,771,217,647,212,691,137,281,638,340,165,75,522,463,706,724,32,93,401,39,364,269,16,520,562,35,141,77,50,265,914,450,33,203,197,268,498,264,69,857,335,22,317,103,905 }, +{ 679,876,36,816,141,144,93,137,752,406,669,228,180,269,96,251,332,21,1,48,352,203,351,202,855,160,831,318,345,498,83,5,116,187,120,617,201,64,317,902,258,261,840,23,839,613,473,110,864,466,190,574,219,32,860,13,873,418,450,326,539,926,748,856,260,44,51,384,704,143,50,295,531,197,489,843,661,206,10,944,778,530,217,567,871,88,82,268,901,800,128,822,125,372,365,86,957,31,30,165,37,442,244,728,224,768,851,373,854,505,625,863,386,367,276,55,827,237,115,922,349,420,274,523,223,169,72,61 }, +{ 15,13,2,1,341,5,23,3,0,4,7,11,10,6,51,12,14,9,33,165,115,569,197,180,17,365,217,153,77,64,196,102,22,21,40,453,317,8,32,901,141,457,117,961,134,31,18,37,16,386,629,291,719,110,28,44,475,177,120,93,269,202,86,237,372,24,95,19,125,59,49,403,57,401,352,318,48,349,730,36,111,326,137,128,98,38,82,391,27,139,65,30,873,275,242,203,54,260,29,45,494,55,25,342,210,450,99,56,46,116,509,498,482,752,373,50,351,899,523,308,247,90,143,35,437,76,72,26 }, +{ 202,13,23,51,403,21,494,5,141,128,48,269,450,125,120,523,64,12,318,180,165,453,95,475,15,33,817,32,197,1,137,115,102,7,260,899,4,37,10,99,2,98,326,93,49,196,730,719,221,457,77,16,237,9,31,22,401,6,421,14,386,308,0,117,134,18,352,40,82,20,19,177,615,59,44,61,402,17,391,203,116,110,27,25,90,341,351,153,30,28,285,105,217,3,291,8,958,854,96,24,50,805,922,54,961,758,576,39,26,801,247,347,509,46,757,342,317,365,498,11,194,72,670,210,38,139,275,170 }, +{ 16,24,71,146,60,18,92,199,126,118,246,35,121,108,140,167,132,230,649,271,360,267,7,9,640,388,150,156,232,191,133,522,273,68,236,101,107,212,0,672,55,446,953,14,149,458,674,330,46,67,314,566,95,189,602,633,392,562,195,64,591,103,21,467,39,354,69,463,407,114,247,98,32,5,50,172,10,1,733,399,278,366,49,36,653,309,412,65,823,22,28,915,288,877,316,124,75,345,340,264,37,12,594,516,135,563,598,451,225,86,38,572,830,125,31,30,359,517,115,571,363,164,726,398,48,237,116,251 }, +{ 269,141,177,678,202,77,318,947,128,349,33,291,40,102,352,210,498,137,954,120,205,864,216,64,196,597,304,275,217,494,461,23,402,115,342,90,13,403,450,300,95,49,197,111,14,326,22,139,237,10,0,125,873,372,182,751,86,117,615,7,44,195,36,31,83,67,752,356,153,98,46,96,59,252,109,97,800,357,285,774,523,550,208,609,569,546,391,247,116,93,66,24,19,18,17,15,6,5,3,2,30,922,414,373,180,159,147,105,20,485,75,859,854,392,376,317,298,288,277,260,143,134,107,39,28,735,415,409 }, +{ 16,71,24,92,35,60,18,191,167,108,246,126,150,118,140,68,7,273,121,149,212,21,388,14,399,314,517,267,796,230,48,146,823,199,271,458,1,467,189,695,877,674,345,9,55,97,46,702,101,575,602,594,733,841,0,350,22,375,107,95,232,28,236,462,32,566,69,298,50,672,13,2,5,727,133,451,154,302,125,653,463,23,446,124,10,589,223,132,64,67,175,38,37,49,98,65,354,423,398,103,649,172,562,755,75,66,336,221,197,903,508,915,809,363,359,51,380,411,309,608,237,330,412,241,12,640,509,36 }, +{ 2,14,75,46,29,52,1,70,171,69,163,220,58,38,112,24,16,7,409,67,262,223,35,334,155,186,5,151,267,394,54,307,87,140,103,309,50,45,83,189,400,18,71,60,92,335,760,227,502,48,21,181,108,61,96,537,12,820,25,278,6,852,113,549,66,357,366,32,287,587,17,37,19,536,27,606,36,150,135,97,74,64,26,380,486,65,0,552,583,28,230,281,600,294,787,582,849,68,149,133,675,22,315,311,236,72,474,243,114,10,324,571,238,86,226,764,9,55,780,218,145,209,80,462,328,95,430,767 }, +{ 265,166,252,100,404,9,329,382,520,593,812,39,88,274,604,101,264,0,553,1,639,676,438,865,664,333,310,68,30,656,167,821,118,363,411,96,23,682,18,158,48,21,478,364,190,105,286,51,98,49,254,435,759,724,647,13,212,16,907,272,124,921,159,31,223,419,125,905,557,5,447,93,154,321,44,393,83,410,327,455,848,35,67,621,293,271,127,191,71,530,354,344,32,24,60,625,213,418,242,109,131,36,69,33,221,144,765,28,121,806,65,50,777,116,22,238,960,146,834,781,897,697,778,165,163,466,115,948 }, +{ 16,35,60,18,24,68,158,71,0,7,55,118,109,14,49,22,159,105,9,28,329,126,344,92,20,107,10,154,553,265,2,191,101,95,424,23,21,5,327,121,13,1,286,239,43,747,51,419,48,455,108,374,168,39,272,677,137,169,124,149,12,199,310,150,46,128,38,279,320,37,709,170,346,6,64,98,167,202,31,185,252,67,810,65,36,125,917,175,577,97,212,32,942,44,518,29,925,628,86,301,69,146,375,132,538,575,913,890,566,403,634,439,333,61,33,50,326,147,165,120,66,446,102,273,421,96,493,116 }, +{ 13,15,23,51,0,1,2,5,77,4,10,115,3,6,9,197,7,177,202,165,961,22,11,12,457,318,217,180,31,14,21,141,33,403,40,102,32,120,453,269,8,341,59,18,16,28,44,17,64,291,93,210,719,37,352,401,110,569,49,317,134,117,90,203,475,260,365,30,450,386,494,36,730,523,125,326,196,153,24,498,98,275,901,111,99,421,308,137,349,817,48,139,752,82,72,95,116,237,86,304,391,247,873,19,509,76,372,216,482,629,57,342,899,128,373,39,758,89,65,864,351,437,105,55,221,38,144,96 }, +{ 160,251,137,93,317,36,180,1,345,120,352,752,617,318,217,332,202,96,365,531,269,498,10,901,141,83,851,341,55,143,64,816,35,968,679,800,260,473,351,230,144,21,373,244,717,82,831,201,876,533,110,372,295,559,258,418,206,203,50,28,704,855,237,349,22,669,86,261,48,898,115,860,253,926,482,9,406,748,197,67,854,873,574,7,434,564,839,69,661,450,15,902,164,228,2,219,843,268,125,187,98,5,728,16,922,165,221,613,335,109,224,442,367,384,868,77,539,840,871,291,822,417,579,116,31,918,44,326 }, +{ 42,25,6,59,85,611,233,128,122,26,4,27,19,343,91,269,614,318,45,177,283,503,138,680,77,80,73,11,216,573,145,102,445,297,0,402,235,210,275,209,174,63,202,389,84,14,803,311,18,33,529,396,255,648,416,280,40,65,256,483,10,64,2,57,678,1,667,624,250,81,24,348,304,17,196,76,194,5,544,454,114,789,151,285,133,90,74,106,51,29,690,12,349,141,61,123,119,152,137,32,355,9,52,56,7,153,525,21,237,28,13,881,36,22,87,436,111,576,300,117,605,495,370,361,291,53,23,494 }, +{ 95,64,81,91,32,237,51,65,180,7,347,74,343,207,204,511,498,577,38,197,348,56,312,128,23,20,370,49,104,43,18,138,22,233,124,86,0,90,554,28,10,681,453,21,14,280,87,750,202,141,25,546,561,29,306,115,63,16,636,241,172,165,147,85,52,24,11,467,650,580,353,192,84,6,13,713,194,673,500,283,9,368,718,151,145,72,57,33,355,263,19,544,475,412,324,318,269,235,173,140,125,108,102,4,933,174,899,519,42,643,879,751,525,403,278,247,221,217,148,123,120,112,111,97,75,73,61,59 }, +{ 326,202,120,817,260,450,494,318,403,137,704,859,128,523,77,576,5,615,553,51,342,291,23,13,1,421,234,475,142,15,48,269,178,968,147,16,21,33,639,922,285,20,49,351,7,854,682,70,58,363,265,373,198,252,402,401,158,82,416,60,132,55,455,354,0,68,102,24,2,329,141,35,101,64,18,6,121,43,95,325,165,31,9,848,798,341,36,12,805,195,32,14,411,10,146,107,75,912,569,116,96,793,424,374,167,156,98,125,17,670,692,163,604,465,25,965,629,349,115,907,427,61,457,65,621,86,71,520 }, +{ 43,20,107,356,362,595,126,92,7,359,319,858,392,493,422,246,441,259,173,683,414,49,97,5,819,279,21,104,60,16,230,35,945,232,0,612,376,48,660,121,32,199,147,132,649,22,18,71,14,663,61,423,470,13,146,672,81,623,298,1,105,693,125,159,408,58,427,98,51,165,501,263,729,95,68,4,24,12,325,779,140,55,23,953,426,28,560,72,804,715,241,581,767,314,939,885,202,118,40,204,102,33,37,17,955,192,713,70,221,598,397,77,2,387,251,344,64,44,31,10,941,75,6,465,603,534,46,761 }, +{ 42,25,6,77,210,483,690,29,102,33,122,342,0,27,4,59,177,40,19,91,300,680,18,21,648,153,196,26,475,202,117,494,128,119,790,48,22,2,343,85,145,56,36,24,32,757,81,1,684,106,292,269,28,833,9,814,151,402,137,84,45,134,70,64,23,139,112,464,396,73,655,115,52,20,614,14,10,403,50,216,63,13,358,233,370,86,7,5,76,37,17,12,485,72,65,397,476,291,57,51,80,611,209,881,665,491,179,99,74,49,416,11,667,624,453,174,148,113,95,235,138,275,61,742,544,341,510,746 }, +{ 31,44,299,116,599,393,456,144,492,125,22,268,242,367,28,963,9,0,105,489,948,434,301,918,36,127,384,1,95,55,49,170,98,512,109,506,206,51,320,23,940,93,558,13,900,86,203,2,10,239,64,5,959,457,141,395,143,7,293,282,855,911,137,202,12,253,165,8,417,159,338,21,24,332,115,401,498,327,551,286,719,197,352,16,791,617,269,391,128,120,870,37,763,244,180,479,14,168,386,308,65,164,39,421,473,160,6,18,692,841,403,276,499,326,730,48,951,272,685,523,713,365,169,903,450,424,559,35 }, +{ 410,521,686,367,321,662,201,772,144,335,88,934,1,98,96,921,264,284,897,274,435,443,909,744,692,203,225,551,120,393,33,960,639,44,418,382,190,217,260,93,64,523,442,197,77,9,100,30,237,166,102,948,244,31,326,765,475,438,165,615,82,10,791,473,717,351,238,206,125,12,805,498,466,213,69,842,617,268,778,899,576,127,116,110,37,5,318,187,0,699,530,303,230,855,854,822,817,613,403,269,39,35,32,21,2,489,141,23,291,704,160,478,131,966,539,905,851,768,730,352,272,247,180,175,137,36,22,922 }, +{ 878,142,178,234,786,132,195,929,416,202,146,147,77,639,522,259,687,167,271,450,481,771,626,212,590,465,49,534,455,427,198,5,315,407,120,0,360,318,48,640,325,23,125,329,113,35,883,22,562,163,346,70,581,13,21,31,98,591,1,61,121,199,107,798,279,150,818,159,20,260,379,668,354,446,44,557,494,269,43,32,10,371,293,95,55,105,141,386,58,33,344,884,64,158,467,116,768,102,93,18,16,362,342,817,60,30,518,191,296,128,711,99,223,39,535,87,170,291,777,97,51,75,402,783,7,156,363,347 }, +{ 254,39,131,578,272,9,716,310,827,829,0,224,518,166,616,333,613,846,228,938,101,30,49,252,105,890,100,219,404,406,371,159,190,265,98,286,158,688,127,68,438,385,455,931,327,910,31,242,856,1,187,709,28,924,737,258,23,329,16,21,557,51,137,96,656,625,125,44,442,116,24,18,48,35,36,13,160,88,65,731,124,863,274,22,95,293,344,395,5,93,109,553,816,128,299,627,530,916,364,761,836,301,154,284,871,118,202,676,201,144,83,276,165,419,944,456,203,10,55,512,32,7,320,724,520,60,115,593 }, +{ 699,854,230,855,473,450,831,704,351,137,217,498,352,800,251,202,160,144,253,203,201,418,206,317,332,752,443,141,1,96,36,180,326,82,822,372,93,244,83,494,335,35,365,817,69,859,237,689,898,21,617,864,318,901,44,702,384,922,954,349,345,341,98,10,48,64,824,367,187,128,295,873,284,120,67,531,579,269,7,86,5,22,678,268,2,261,840,927,957,930,28,735,13,291,31,559,947,116,143,30,851,669,373,723,23,16,260,959,661,125,868,717,403,9,748,437,110,50,213,115,223,539,55,615,466,946,285,533 }, +{ 403,475,202,453,77,318,51,402,494,23,13,33,342,102,128,576,269,141,137,450,120,899,421,523,196,4,117,40,90,275,153,365,349,59,177,291,65,210,64,217,84,0,134,670,15,372,95,197,401,817,1,180,805,341,237,96,260,194,3,285,139,114,99,18,873,326,615,503,393,386,317,293,125,24,11,389,165,70,216,800,752,745,730,719,697,678,657,277,115,108,87,83,69,36,32,21,859,111,457,901,788,323,300,283,266,130,118,86,76,57,12,352,351,9,7,6,2,842,725,541,361,329,312,221,168,124,49,945 }, +{ 144,23,291,268,197,182,202,116,98,44,264,284,415,96,70,100,125,367,141,203,39,110,905,411,1,107,93,363,120,180,296,658,82,310,447,692,897,238,361,190,77,664,260,105,88,520,48,478,166,682,118,576,340,30,18,966,51,865,13,349,379,403,795,551,921,326,687,301,254,581,223,101,676,457,404,156,812,195,242,213,208,9,165,364,323,269,65,60,615,498,21,765,621,271,137,128,917,914,890,855,481,217,154,102,68,232,883,593,523,354,131,948,318,219,691,842,846,771,272,252,0,626,382,149,87,824,822,791 }, +{ 15,23,13,77,1,51,202,120,180,115,141,260,5,0,21,9,137,450,269,128,12,2,318,365,7,197,326,351,48,102,341,177,342,352,523,217,291,403,165,6,64,110,32,373,4,317,18,16,37,36,901,22,14,10,494,90,457,28,854,453,82,24,386,817,40,33,308,8,11,475,401,752,96,922,93,3,244,30,275,719,730,349,210,421,86,160,99,17,898,285,31,961,50,39,569,44,221,59,704,49,117,35,372,237,153,55,143,899,391,253,203,116,134,615,576,144,196,402,65,125,629,98,247,482,83,20,873,509 }, +{ 0,95,18,577,32,207,64,193,29,784,65,887,715,102,52,61,671,138,49,91,74,180,104,701,237,693,474,280,403,241,804,847,22,263,6,174,344,583,453,862,475,77,85,643,500,397,38,37,306,498,494,312,289,2,81,33,5,681,942,510,370,27,283,422,316,192,25,155,347,84,901,319,113,576,24,835,355,348,43,933,814,353,72,194,240,204,42,19,298,650,523,125,86,718,173,247,51,721,389,124,879,490,707,291,153,141,58,54,17,889,202,97,402,1,546,342,134,482,36,511,421,805,750,673,426,196,120,117 }, +{ 101,9,0,252,68,364,166,100,265,124,154,329,39,16,419,212,24,404,71,118,191,676,158,35,88,18,274,553,520,382,65,562,694,28,962,677,671,60,55,30,333,302,706,149,167,604,375,49,109,857,21,703,967,917,865,1,264,593,806,344,310,438,346,190,48,254,96,105,439,23,22,701,812,13,125,175,777,67,925,51,159,656,83,589,570,7,455,95,628,289,108,98,31,286,10,5,221,429,69,810,826,336,36,435,92,131,557,639,722,32,168,793,638,272,273,14,44,128,126,114,50,170,146,37,238,127,484,634 }, +{ 16,24,191,71,167,118,212,18,35,68,149,375,101,108,302,589,60,92,755,9,21,562,674,124,154,246,0,273,48,1,126,363,398,411,55,7,447,146,458,350,841,695,140,65,818,150,388,199,628,345,478,264,22,13,189,647,95,271,602,32,69,823,267,121,28,50,67,796,14,346,23,650,49,175,5,621,520,664,51,882,399,10,903,133,815,777,252,98,100,197,649,64,115,781,46,847,314,517,566,221,509,223,103,575,12,953,125,114,236,230,354,37,336,2,39,96,107,308,640,749,463,936,165,724,462,886,604,237 }, +{ 69,14,2,24,46,267,71,189,38,29,67,278,52,35,135,1,103,108,149,75,309,16,388,83,150,7,60,87,18,236,74,66,220,335,68,251,262,6,50,140,582,357,451,324,133,28,226,366,238,516,172,565,65,36,25,486,114,70,336,0,92,55,594,5,443,458,273,175,54,418,96,571,97,394,906,733,21,26,124,380,12,118,9,218,58,22,154,641,606,398,86,230,48,222,160,37,19,705,42,596,331,550,112,10,412,138,95,45,399,181,294,171,163,145,64,462,32,831,600,603,345,696,517,711,602,545,894,302 }, +{ 13,23,1,4,115,51,2,0,5,3,11,180,33,7,77,10,15,9,341,141,6,365,12,102,217,202,197,317,40,22,165,269,352,318,59,403,64,901,17,21,14,32,752,28,31,49,177,308,457,401,498,453,8,18,873,128,291,494,16,125,117,475,569,196,95,509,36,93,237,153,24,98,372,137,961,386,349,37,139,44,30,326,210,134,342,48,719,482,391,90,120,55,111,86,110,247,203,39,421,116,523,304,260,450,19,546,629,65,105,734,730,27,275,57,468,402,758,143,35,864,351,38,54,144,244,216,300,373 }, +{ 447,1,698,411,125,98,31,21,363,5,919,848,48,44,691,144,759,293,225,914,459,120,13,478,354,156,4,116,922,32,23,127,64,668,163,857,371,202,49,82,30,839,284,586,70,17,223,93,865,664,621,340,165,87,51,39,421,321,260,897,110,551,88,724,330,264,335,318,221,77,3,2,821,772,744,442,190,242,195,634,520,360,201,682,963,916,898,854,685,372,351,349,291,269,217,180,141,105,102,96,95,75,59,40,686,622,949,938,687,658,228,909,100,931,867,638,446,407,761,558,463,456,342,310,237,197,166,90 }, +{ 854,351,180,82,217,352,141,752,898,317,15,244,1,922,21,36,10,64,77,115,260,372,365,35,110,22,291,28,13,341,40,48,120,901,349,4,864,5,661,32,23,160,304,498,237,137,33,11,269,51,202,143,373,93,144,59,164,96,326,98,83,450,318,165,203,102,253,230,55,45,197,2,247,201,67,128,579,17,125,86,76,177,69,37,57,0,16,31,44,728,117,72,27,9,52,116,251,482,342,817,457,7,523,811,367,559,50,719,12,134,961,717,494,187,127,105,49,3,90,873,569,899,418,196,18,316,6,61 }, +{ 13,1,5,0,4,10,33,23,9,2,82,11,3,77,120,6,21,7,40,12,102,8,51,31,117,260,351,134,22,16,153,110,64,450,93,32,196,457,523,115,28,165,18,719,98,59,44,730,30,17,326,386,116,197,898,854,217,961,180,177,291,36,49,817,125,48,342,95,141,90,317,341,922,57,758,202,373,899,139,14,365,352,349,24,203,37,372,901,801,615,128,275,318,269,210,453,39,144,111,237,401,391,72,55,137,105,752,403,86,38,569,494,642,704,99,475,509,498,661,304,811,277,61,247,65,873,308,551 }, +{ 51,403,475,23,13,21,12,494,453,115,99,1,5,523,817,202,7,421,49,22,64,450,899,95,221,32,0,120,308,730,6,180,37,141,48,2,20,10,401,128,44,31,14,753,125,402,3,719,260,11,9,318,801,40,33,19,17,4,386,289,237,105,90,86,16,457,326,36,361,269,170,102,76,39,38,30,165,26,25,961,341,958,576,365,247,240,197,117,98,89,77,61,35,34,350,137,93,82,805,782,756,701,671,670,569,503,484,468,413,405,352,317,241,217,216,214,208,179,134,8,281,316,270,144,83,68,945,901 }, +{ 752,180,352,864,498,317,141,217,237,39,365,115,901,30,88,930,579,230,0,83,101,9,197,372,482,873,128,546,64,100,341,36,1,166,437,927,702,35,213,96,23,13,175,285,68,252,269,349,251,28,689,190,69,868,443,735,304,21,562,723,457,137,399,759,509,244,831,419,10,274,77,321,539,165,818,67,530,201,219,194,228,840,418,345,410,93,131,144,86,570,143,48,154,854,247,646,224,866,49,12,699,669,402,596,954,212,203,208,160,158,800,31,177,694,358,24,117,51,957,406,382,202,22,346,134,7,438,16 }, +{ 16,24,35,71,7,0,18,28,60,14,22,158,109,68,154,118,124,92,168,2,55,149,159,49,747,185,169,424,1,108,105,95,10,577,150,344,175,126,20,239,65,9,677,69,5,67,302,6,12,21,671,46,329,38,107,97,164,23,51,43,286,375,170,48,50,125,140,327,64,320,39,420,29,36,701,374,289,83,101,273,570,37,143,32,13,347,137,191,345,189,230,86,66,553,265,638,128,202,429,455,252,54,272,703,403,246,535,103,52,221,421,654,398,279,98,194,121,114,310,336,467,584,298,165,25,453,346,96 }, +{ 15,341,23,13,0,51,77,1,141,202,33,4,137,9,260,115,120,180,102,269,351,40,11,291,5,318,82,7,2,21,10,901,197,12,22,128,854,922,165,450,16,117,352,59,8,177,217,28,3,64,457,349,90,32,36,403,37,110,18,30,14,401,6,17,523,134,898,365,317,453,48,752,57,31,719,386,817,308,196,421,153,93,24,244,139,86,143,475,730,569,39,99,494,961,373,55,642,44,49,498,285,96,210,253,275,661,125,342,35,237,203,372,95,899,326,144,402,247,629,160,98,391,61,304,576,221,216,208 }, +{ 68,35,24,149,18,71,16,118,189,108,14,7,101,154,0,9,230,175,140,336,69,124,65,267,28,114,67,133,302,46,55,398,251,375,345,2,1,236,458,103,388,83,399,596,60,50,283,38,92,346,732,412,602,36,22,212,309,21,273,39,650,722,246,12,109,48,172,429,29,218,191,906,75,52,364,443,5,86,167,547,54,10,135,252,49,64,238,95,330,37,278,723,96,23,705,516,520,439,628,891,13,51,366,565,158,915,930,232,562,30,868,6,335,637,696,150,952,194,451,484,32,220,100,694,702,886,389,168 }, +{ 308,13,197,115,391,165,546,509,125,629,569,237,749,468,23,9,558,28,903,338,217,801,734,317,352,401,740,254,341,49,36,180,22,386,51,39,159,901,873,95,365,490,506,530,168,961,498,131,166,438,64,482,247,219,579,83,185,109,93,752,656,187,345,439,613,857,457,688,316,101,86,21,158,627,258,843,89,829,637,312,10,55,679,105,967,224,170,251,574,332,406,221,175,228,48,758,902,261,960,505,44,31,876,116,37,12,868,840,72,424,347,1,371,98,15,518,274,141,32,827,333,77,190,892,816,272,253,143 }, +{ 90,64,217,32,115,128,141,216,77,312,86,597,95,59,4,33,247,875,13,172,352,197,147,237,177,288,102,437,210,22,97,5,194,137,202,37,57,11,182,208,153,76,61,14,304,134,117,65,40,751,23,291,947,10,864,342,196,21,362,49,356,178,60,45,874,317,0,54,48,612,165,107,51,259,588,752,324,192,819,298,275,180,20,678,372,307,289,173,43,120,112,415,285,207,164,72,36,35,707,44,7,359,89,46,802,414,156,2,789,729,579,168,123,104,98,92,55,24,712,524,125,87,70,66,470,387,465,148 }, +{ 1,22,28,105,36,95,67,49,2,55,239,0,5,12,83,170,50,9,159,312,86,109,7,114,320,21,208,14,103,69,35,168,301,31,164,64,194,23,892,127,169,323,160,654,98,51,141,65,48,158,10,96,143,133,37,505,13,218,584,38,6,46,751,54,115,24,185,244,347,128,424,44,824,202,253,503,597,16,269,32,237,238,39,137,286,180,512,420,3,352,740,533,29,646,165,638,251,242,318,338,623,175,125,4,18,776,395,797,480,285,713,197,531,116,532,30,928,68,341,401,293,371,99,933,124,789,844,272 }, +{ 18,9,16,101,159,68,252,35,24,105,0,60,310,265,49,39,890,329,320,71,455,118,272,557,7,55,167,28,327,121,286,1,137,170,98,364,271,100,518,333,301,65,14,520,51,23,254,13,212,553,21,604,5,363,109,202,938,128,239,709,30,31,127,95,419,64,22,125,578,846,132,191,616,2,154,158,146,124,371,48,647,395,92,67,621,12,242,627,10,916,411,344,424,116,44,403,32,108,512,338,446,737,374,326,166,86,149,793,36,120,688,421,69,827,318,93,293,126,346,731,20,463,37,260,639,450,848,264 }, +{ 129,123,249,618,214,257,460,205,769,184,17,5,245,162,106,45,90,119,76,183,128,2,209,229,61,80,179,497,6,59,130,1,64,202,99,3,152,27,77,19,710,14,207,84,4,306,40,12,29,141,255,57,240,62,26,452,10,790,58,25,269,177,619,556,21,524,38,266,32,742,217,56,136,86,102,519,7,137,115,432,318,33,292,311,11,151,22,402,48,0,216,51,37,378,288,23,210,54,655,667,381,95,36,34,113,65,72,194,403,139,50,305,111,361,504,483,46,833,285,15,494,180,475,317,112,16,263,275 }, +{ 51,719,23,453,899,457,730,13,475,386,670,4,523,15,401,961,11,12,196,99,153,403,32,3,57,117,21,33,450,59,775,165,40,115,130,421,236,77,308,202,128,120,134,758,108,6,65,391,217,90,64,125,177,197,0,361,5,34,26,260,2,19,1,629,300,183,47,216,54,141,328,817,78,123,22,17,180,342,801,7,95,27,37,241,210,214,269,221,14,10,56,237,275,242,194,494,317,558,412,25,743,73,38,247,48,102,28,488,381,318,9,82,29,326,291,711,288,137,53,304,129,46,277,402,339,283,266,24 }, +{ 173,192,66,204,104,74,636,560,426,825,20,7,43,289,750,707,712,97,861,414,214,348,65,715,81,500,90,138,95,64,835,32,312,174,319,22,526,194,280,874,792,52,0,580,91,681,232,29,6,819,330,889,673,879,5,888,86,77,568,519,422,85,376,288,135,729,387,172,370,2,222,33,38,61,10,21,784,355,49,353,107,249,324,25,69,24,18,42,36,577,226,28,356,102,140,76,448,59,92,751,207,115,671,40,325,4,237,31,359,247,524,343,180,470,145,147,397,16,392,153,117,13,120,26,177,597,125,45 }, +{ 23,51,13,202,21,141,5,1,165,120,269,180,48,12,197,32,15,2,318,115,403,125,128,260,450,137,7,9,523,64,82,33,352,93,386,326,0,6,308,4,453,401,421,102,237,37,457,475,31,14,110,351,77,16,3,221,341,391,99,494,30,44,18,117,8,11,817,203,98,730,17,217,39,719,153,899,373,40,196,24,10,61,498,402,72,365,509,50,90,854,569,317,116,28,144,247,134,19,291,59,96,752,45,285,961,89,615,922,342,58,49,25,35,36,22,349,177,20,546,241,57,670,88,54,29,105,576,65 }, +{ 200,78,34,322,472,27,390,714,161,136,19,59,283,444,250,128,236,176,862,108,47,62,527,14,57,412,53,813,73,3,381,17,453,711,56,585,210,4,46,361,77,196,440,26,65,129,51,153,130,432,383,730,216,183,297,803,690,402,401,45,469,202,133,389,123,529,5,495,162,33,90,275,318,754,194,12,378,177,291,32,257,102,38,719,117,184,114,789,480,13,23,11,106,48,503,814,2,61,629,84,684,54,475,245,21,657,555,231,40,678,152,99,300,507,137,119,80,0,436,113,457,369,650,6,403,86,22,523 }, +{ 347,33,204,498,66,546,681,56,174,5,426,95,135,348,97,172,353,864,49,22,928,923,873,808,73,263,241,10,568,370,27,197,180,77,554,207,81,580,102,21,42,237,368,32,751,526,192,134,117,19,0,350,86,63,715,448,468,115,380,123,76,25,4,29,64,44,31,36,6,91,7,511,40,645,387,85,74,861,408,298,80,736,69,173,872,13,501,355,108,712,138,153,87,61,28,17,901,18,467,72,673,59,620,343,232,161,233,312,800,509,324,278,125,45,30,14,650,636,90,3,451,415,577,500,65,561,165,70 }, +{ 131,224,219,187,385,442,258,836,371,944,871,908,737,924,254,127,98,31,827,716,574,44,39,829,116,201,613,276,839,616,242,860,926,284,137,228,395,144,293,36,96,9,1,578,406,30,456,0,299,567,93,21,13,125,202,23,843,160,166,49,51,64,856,105,846,902,203,761,935,938,662,492,128,295,159,83,48,916,438,268,190,100,115,269,5,403,310,272,318,489,367,110,77,141,931,28,10,531,55,326,165,918,688,32,86,576,22,699,88,158,120,717,530,261,244,143,421,341,434,217,2,512,197,960,625,164,37,333 }, +{ 1,4,5,2,13,180,3,15,7,141,6,115,23,352,901,341,51,0,217,317,197,77,752,365,33,21,11,165,12,269,32,9,202,102,291,177,318,93,40,372,247,10,137,31,349,498,22,120,8,308,403,44,17,873,569,110,401,203,116,59,125,36,457,28,326,196,48,304,14,37,494,386,373,98,144,134,153,128,90,64,117,475,758,260,509,72,18,16,206,55,49,523,86,237,210,95,961,629,421,482,453,30,391,253,450,951,719,342,139,638,730,576,39,864,275,268,579,241,19,82,221,24,546,437,89,57,127,811 }, +{ 370,91,681,636,397,718,510,74,81,240,355,66,84,582,278,289,214,511,701,138,90,18,782,6,104,288,5,65,64,32,693,117,247,77,63,192,56,0,814,306,750,673,312,233,85,40,453,847,671,415,11,643,317,207,134,72,61,42,33,102,4,942,519,343,123,115,27,1,86,25,348,249,22,580,537,461,396,352,153,75,484,784,475,403,304,194,21,10,235,29,3,193,119,583,287,196,114,76,59,52,319,256,124,26,23,13,874,579,389,610,462,302,238,17,710,817,597,353,129,96,51,48,217,164,89,835,804,751 }, +{ 15,13,23,4,197,51,165,1,2,120,180,341,0,260,386,5,8,82,12,7,9,3,141,450,317,153,11,10,391,217,6,351,326,115,64,77,14,457,117,901,498,523,854,509,291,110,134,730,401,758,33,961,237,352,22,202,898,125,922,93,21,365,308,196,31,44,615,269,128,817,99,453,801,28,203,899,116,373,873,752,629,18,40,55,349,137,98,37,318,372,403,36,49,475,569,32,38,86,546,95,16,661,143,704,102,719,30,342,59,39,35,17,642,304,494,482,48,24,638,144,177,300,194,139,958,29,54,492 }, +{ 32,76,292,2,5,476,45,214,72,29,1,241,14,519,464,460,21,179,48,129,290,90,17,710,148,205,123,263,249,50,58,358,52,425,186,243,408,46,257,618,207,38,245,184,89,227,155,769,162,270,171,350,3,7,288,119,61,6,106,57,316,64,54,400,449,128,183,229,255,75,4,141,36,496,115,152,524,655,99,209,720,334,497,62,12,202,59,77,37,86,10,405,0,56,51,221,130,666,23,25,40,592,22,181,237,269,11,247,432,137,16,217,454,84,27,452,428,24,663,619,19,513,318,177,659,26,67,341 }, +{ 13,569,115,165,509,197,629,734,17,873,23,659,106,457,180,45,41,32,961,901,38,536,341,428,62,155,587,474,391,54,21,431,99,546,255,51,243,528,308,365,136,241,37,468,405,89,123,129,14,482,181,2,211,401,139,72,12,148,542,719,221,117,794,214,76,749,498,449,350,317,292,157,743,540,29,758,328,152,552,188,162,11,3,119,720,488,339,770,413,237,227,179,50,248,48,209,697,313,90,741,476,130,4,477,128,1,773,519,277,40,421,507,46,266,57,249,33,245,361,137,153,202,453,369,471,64,133,358 }, +{ 68,101,24,35,71,16,154,124,9,0,149,118,175,65,302,18,108,375,346,28,55,92,252,364,212,429,191,7,439,60,694,39,520,14,398,806,100,22,109,329,166,167,484,273,336,604,67,69,419,189,562,21,826,589,1,701,695,345,95,140,230,265,246,49,10,264,48,158,114,637,755,168,103,917,133,46,628,2,64,722,126,650,399,88,404,535,125,768,570,289,150,458,13,706,83,30,50,36,388,32,420,23,267,12,185,5,882,674,818,96,169,51,676,925,37,363,283,602,86,105,38,221,333,967,159,286,942,553 }, +{ 0,9,68,35,1,65,101,230,69,28,124,23,83,39,141,154,67,13,21,352,212,217,51,30,100,252,88,16,336,115,48,175,864,103,24,317,189,5,180,114,166,128,96,443,36,399,10,12,238,64,329,562,264,478,177,32,18,265,752,77,55,22,321,372,291,218,160,702,149,520,167,50,437,304,202,498,247,137,118,269,99,49,596,365,589,221,759,486,349,197,165,382,346,323,410,2,664,480,223,419,213,777,604,433,418,75,7,4,335,482,15,547,532,37,251,363,237,125,936,72,143,755,341,553,308,190,109,201 }, +{ 909,265,100,410,382,812,88,593,321,213,1,30,252,166,676,639,264,435,466,230,689,39,9,48,724,352,478,21,98,553,96,329,190,223,101,23,274,51,217,13,83,5,921,310,699,438,795,125,69,821,905,530,293,752,759,35,36,656,557,128,144,393,443,520,0,541,284,521,604,137,419,32,682,831,141,455,317,418,664,855,854,251,31,165,772,221,404,386,93,201,180,203,33,686,421,335,539,840,115,55,127,44,957,160,285,158,28,344,68,67,367,77,202,345,254,697,102,669,562,897,807,702,765,286,10,351,64,269 }, +{ 113,61,904,43,0,204,848,453,389,315,788,198,174,5,22,899,222,59,37,102,33,207,422,290,288,128,97,329,306,46,31,30,77,470,817,523,159,58,40,942,122,65,725,690,324,291,104,64,263,450,426,20,54,337,304,247,205,196,153,134,117,105,95,49,408,757,615,351,326,194,115,82,28,18,9,177,137,32,350,887,372,347,331,325,217,90,48,2,701,344,342,320,316,158,317,804,376,341,819,319,849,685,666,600,580,579,553,501,498,403,392,365,357,349,330,301,270,262,242,237,180,112,76,44,39,36,7,147 }, +{ 24,16,141,71,18,0,352,269,75,108,140,7,60,35,330,12,199,217,65,9,486,28,498,864,87,246,64,92,68,50,318,267,126,954,118,115,55,230,70,5,146,177,950,545,13,372,197,752,271,101,98,14,133,77,180,641,349,317,99,36,726,458,23,537,487,156,102,437,22,10,622,165,21,563,516,832,202,2,341,149,48,565,86,457,304,273,220,237,116,44,674,526,117,100,39,33,120,49,37,365,360,830,58,137,103,291,135,522,181,166,121,88,54,51,412,167,284,223,46,15,394,1,696,852,463,361,264,212 }, +{ 271,167,121,60,199,146,191,463,18,446,212,132,575,126,598,354,407,16,955,727,640,566,1,608,195,35,562,279,522,21,48,107,98,71,118,783,223,493,150,467,68,589,24,5,360,781,92,156,363,591,411,668,447,55,13,534,818,101,23,9,108,346,581,647,777,590,49,621,0,259,125,362,936,664,939,7,273,28,31,22,51,163,32,807,10,535,834,221,147,520,246,33,293,541,50,37,586,14,815,375,393,4,116,12,105,96,70,2,44,77,39,93,252,149,281,441,46,158,75,61,759,154,97,649,95,423,364,140 }, +{ 15,115,217,317,365,341,180,13,141,33,23,260,120,4,82,110,102,351,51,40,854,77,372,137,352,21,64,1,5,32,36,10,197,117,898,752,11,901,269,202,922,165,59,9,160,244,0,661,373,134,2,143,22,48,457,349,153,253,450,12,386,196,318,482,247,326,128,308,177,55,72,90,498,523,7,125,961,719,93,28,37,86,3,291,811,509,237,96,57,873,16,437,89,579,391,730,111,203,31,864,18,6,615,35,468,210,216,14,569,275,139,164,304,17,342,76,899,403,221,494,642,316,817,251,30,531,717,95 }, +{ 173,97,356,7,43,387,107,426,104,729,414,359,232,20,560,392,595,885,858,861,707,819,66,81,612,5,49,22,362,319,126,192,230,32,660,330,0,74,357,92,246,147,784,715,14,324,298,172,470,879,95,263,2,422,259,16,427,38,314,207,683,61,376,140,21,681,35,6,29,397,91,60,18,241,10,279,28,64,52,77,24,830,500,493,31,636,33,37,501,12,4,86,779,204,135,58,577,370,65,568,441,40,199,48,526,13,98,581,712,105,534,125,325,17,102,72,44,347,750,138,120,70,59,46,465,25,45,30 }, +{ 15,23,13,82,120,51,110,93,31,33,116,98,95,196,453,4,165,40,197,260,117,0,386,2,180,217,12,1,351,11,77,5,8,134,102,9,141,326,457,115,341,291,3,153,10,401,21,450,317,523,854,202,352,14,7,391,898,6,64,961,373,719,22,125,475,901,28,922,615,403,18,177,269,498,203,237,44,349,99,758,509,32,30,318,128,37,817,16,899,629,308,57,365,670,36,421,49,139,275,661,730,111,569,137,24,39,48,86,704,144,801,752,35,17,65,109,494,342,558,38,372,551,221,216,170,46,55,638 }, +{ 100,9,120,260,30,827,795,373,264,77,137,166,202,326,127,578,1,39,254,82,704,318,217,450,18,101,395,737,351,0,731,291,265,317,156,349,341,31,716,105,102,93,141,197,55,33,968,98,269,5,296,128,478,131,24,851,252,371,342,32,859,938,921,44,616,411,403,404,385,924,144,109,165,40,456,274,180,10,494,836,16,512,854,410,299,195,110,68,28,7,518,442,36,438,286,242,13,244,167,96,363,850,379,656,521,284,95,523,125,64,658,615,340,116,67,922,212,115,447,187,937,935,916,724,936,856,822,372 }, +{ 14,24,2,67,69,46,28,108,1,65,38,83,7,398,18,0,124,154,114,103,36,50,267,133,29,55,236,189,149,35,16,547,52,68,429,175,283,71,336,9,160,118,309,22,302,251,109,458,646,75,6,140,388,86,135,484,278,412,12,596,5,389,650,172,531,54,238,60,218,420,602,96,21,66,208,87,886,220,194,505,782,150,185,169,92,64,10,891,168,95,25,101,797,335,289,533,570,532,74,906,37,230,48,357,49,273,262,323,366,696,346,158,23,516,443,39,32,799,70,565,58,705,137,51,13,439,418,128 }, +{ 66,69,14,267,189,24,71,7,150,172,140,97,149,16,60,380,135,462,2,35,67,74,388,92,38,18,451,278,108,29,83,68,251,6,357,52,46,1,230,443,36,324,423,298,458,65,273,28,336,517,314,823,232,192,398,330,711,118,594,103,87,0,75,565,175,55,335,399,705,124,22,86,25,21,603,448,906,568,204,95,48,12,50,236,5,563,309,43,550,10,154,138,64,32,345,302,37,596,893,20,133,572,54,467,114,126,607,42,26,160,246,220,733,375,809,226,429,526,501,723,9,895,49,808,70,500,602,915 }, +{ 15,1,13,5,2,341,4,365,3,0,23,10,11,7,12,180,6,117,9,17,33,21,134,115,77,14,32,217,197,102,317,165,8,141,457,153,64,40,901,37,51,128,22,961,569,291,202,196,16,31,269,177,352,36,137,38,873,372,28,93,19,351,59,55,349,111,44,509,342,498,752,18,48,125,318,99,304,139,120,386,143,237,116,308,72,401,86,391,110,30,95,54,629,27,482,210,76,45,164,24,89,79,29,90,453,403,25,719,57,275,203,216,82,98,41,758,579,277,247,46,260,49,523,244,26,285,326,144 }, +{ 1,22,12,5,28,36,0,21,2,86,49,48,95,55,37,23,10,50,9,7,96,13,202,32,51,105,14,54,208,168,65,115,128,24,83,16,160,67,4,141,137,31,64,99,318,3,269,194,109,6,323,18,164,159,17,114,35,170,239,281,402,285,180,11,238,745,69,584,103,197,165,480,158,45,341,181,169,403,77,244,44,46,38,218,143,98,433,289,401,30,223,58,717,125,657,503,352,217,851,33,177,185,29,946,597,757,708,39,365,312,317,72,800,253,75,789,102,678,124,418,133,237,59,956,90,127,308,430 }, +{ 123,605,630,63,108,736,444,65,650,368,19,561,619,389,122,216,25,114,183,177,26,53,27,813,133,42,102,194,84,445,249,899,297,56,421,349,90,401,33,597,588,250,137,78,73,130,11,730,51,283,45,524,629,235,59,57,14,141,196,153,32,40,343,255,719,134,117,467,304,119,76,17,13,0,152,6,436,77,390,4,323,511,678,475,457,136,64,594,342,23,403,256,12,129,503,218,208,86,62,61,5,416,233,372,52,3,469,184,275,381,106,947,789,554,214,112,680,450,95,309,217,18,9,529,432,291,269,80 }, +{ 127,39,9,254,98,395,371,31,0,385,836,44,1,30,299,284,100,166,116,578,131,187,272,28,935,49,293,276,21,125,23,16,219,310,599,51,737,442,827,48,242,616,137,18,101,908,5,144,13,846,24,265,456,716,35,512,96,252,93,88,202,22,201,68,159,36,158,7,77,55,224,924,938,141,492,203,128,190,688,269,518,258,32,333,64,37,318,228,2,65,829,326,274,244,761,165,12,115,67,286,120,931,217,110,367,404,105,839,33,102,567,83,268,916,662,95,438,14,86,50,944,109,574,557,69,327,625,10 }, +{ 2,29,61,45,151,6,655,112,207,52,25,425,32,833,76,7,70,106,1,113,14,119,292,790,58,26,72,5,887,64,209,128,17,90,255,315,66,19,145,4,186,306,243,476,115,59,483,27,77,74,21,177,311,38,87,86,40,263,141,738,46,290,36,23,148,454,287,10,240,95,42,22,84,16,137,0,179,202,135,33,631,491,814,75,227,51,269,89,50,210,65,214,20,12,80,222,48,318,24,28,49,13,11,216,18,155,358,102,99,56,464,122,37,644,138,549,710,54,180,337,205,117,496,67,172,869,123,720 }, +{ 2,17,5,29,27,3,6,401,45,58,51,26,23,19,53,52,14,34,46,453,151,133,366,13,61,236,50,218,114,1,73,99,308,487,603,108,21,25,181,670,287,421,297,80,115,396,48,74,56,137,171,412,764,445,202,38,281,62,457,42,65,629,516,480,475,341,113,549,64,573,600,386,57,403,894,250,106,730,54,436,651,491,86,145,12,334,208,155,832,128,328,614,361,262,165,283,84,32,719,323,745,200,90,141,37,36,711,657,631,67,760,754,583,739,635,569,7,594,488,315,161,10,285,59,402,758,243,430 }, +{ 22,170,31,301,127,1,98,44,28,36,239,105,55,299,512,293,86,395,116,685,242,841,9,95,125,338,599,900,2,0,5,654,23,320,169,109,13,456,870,49,64,185,763,10,51,7,237,12,141,144,164,479,143,965,312,35,137,923,21,268,115,14,933,492,128,168,393,284,506,67,286,762,276,83,37,244,6,114,202,160,180,928,24,352,634,194,367,201,197,159,308,165,269,253,558,844,401,538,48,65,208,272,935,391,584,50,420,341,365,131,203,913,318,16,498,3,489,327,96,4,457,374,317,892,386,32,434,638 }, +{ 14,21,50,48,5,2,1,32,270,46,38,290,76,72,45,155,54,17,263,181,408,207,425,281,29,430,221,67,36,96,292,89,449,52,428,37,476,513,241,83,103,119,0,171,7,541,720,528,3,12,10,24,227,148,22,58,179,61,536,502,214,243,655,6,413,90,9,350,23,218,106,51,16,86,69,129,65,186,28,133,115,114,99,431,552,400,760,794,464,64,474,128,433,123,328,460,18,405,137,487,306,84,108,477,245,710,35,236,205,4,13,787,27,238,308,587,309,770,141,540,19,56,790,240,95,223,488,202 }, +{ 17,119,106,378,240,383,136,84,62,80,306,790,56,504,742,3,440,207,209,128,255,483,123,45,5,667,61,32,57,64,179,27,129,14,205,548,297,4,245,2,214,311,141,90,11,76,833,655,184,151,29,361,59,469,152,249,26,38,162,19,58,215,328,257,73,495,161,34,54,229,269,202,250,266,292,21,12,402,514,555,216,113,194,887,130,1,445,503,507,183,6,25,318,237,186,72,52,814,710,618,99,10,86,211,148,452,263,243,65,137,37,40,41,23,36,33,155,497,13,403,53,95,313,227,494,180,77,738 }, +{ 13,1,2,15,23,5,115,0,51,10,4,3,6,341,7,11,365,217,180,317,9,33,197,77,165,40,22,12,14,102,64,141,117,153,196,453,457,8,202,901,17,21,269,18,128,28,125,569,31,386,59,318,32,16,134,349,352,291,372,237,36,719,403,752,475,873,961,730,509,391,177,137,24,401,95,57,44,37,93,498,120,308,139,494,110,98,326,111,247,86,304,30,482,49,143,48,55,629,210,260,758,523,19,351,65,450,72,116,46,76,342,29,275,203,50,170,105,164,89,82,734,242,27,38,54,144,899,90 }, +{ 772,335,662,96,367,744,897,1,303,686,264,521,652,340,116,520,141,31,919,201,966,260,888,203,44,64,905,321,144,225,269,98,691,658,349,284,410,698,692,363,120,88,848,717,863,187,551,914,909,418,576,202,646,443,850,639,523,69,404,336,958,293,128,730,131,615,274,382,296,166,960,948,258,228,224,219,751,486,160,125,82,411,191,765,244,223,475,453,352,326,100,318,676,596,67,351,899,703,403,389,435,947,589,498,102,33,954,291,265,156,922,854,402,93,759,664,447,372,212,137,478,329,724,562,530,286,217,942 }, +{ 1,28,5,0,36,24,65,83,50,14,67,22,12,96,2,48,218,114,281,54,18,21,9,133,55,103,46,181,37,58,160,38,16,7,99,10,223,69,238,32,430,433,502,45,35,51,766,49,108,86,23,124,361,155,487,765,532,283,75,137,270,115,17,838,163,109,480,95,13,61,6,543,154,754,513,70,29,72,202,540,822,657,323,920,398,89,68,64,141,966,3,221,208,285,128,290,956,547,488,269,236,175,168,429,194,474,800,646,171,39,412,775,52,308,531,866,149,880,389,4,180,403,318,842,834,402,217,185 }, +{ 138,174,348,42,25,6,145,81,544,280,525,74,85,26,648,204,355,287,396,91,233,66,636,104,122,746,416,29,64,65,90,45,0,235,194,681,87,22,222,651,95,624,573,32,214,289,750,415,343,445,192,52,112,86,17,370,19,5,135,881,7,151,2,337,113,605,243,63,389,256,49,59,10,4,673,680,77,18,297,61,312,76,614,97,72,249,283,11,524,70,353,33,27,28,216,114,874,173,311,57,43,21,14,597,36,1,141,102,58,24,217,12,436,665,80,172,715,792,148,123,619,56,133,20,491,75,784,117 }, +{ 24,16,35,68,0,18,158,28,71,159,49,7,55,154,109,14,22,60,105,124,344,65,118,424,9,239,95,1,320,2,149,577,286,170,92,169,108,347,12,21,327,374,175,185,67,10,272,5,101,51,48,23,302,455,39,6,671,301,46,13,69,310,20,518,64,150,36,346,50,32,137,202,709,677,191,126,98,125,375,168,538,289,164,38,37,128,638,30,703,747,890,273,127,634,31,419,140,143,439,86,403,114,252,83,962,654,221,762,623,338,115,29,212,103,701,570,308,107,512,189,54,97,167,230,479,345,312,913 }, +{ 854,699,230,352,93,689,144,251,160,137,855,202,617,203,217,36,669,332,450,120,473,351,831,201,213,752,326,800,206,141,258,748,82,418,345,30,180,816,957,318,840,190,443,228,44,35,863,968,10,1,219,871,704,317,64,22,679,83,466,539,498,224,55,96,898,31,116,406,851,384,86,244,295,28,873,98,817,531,187,822,864,373,494,131,559,372,530,901,574,365,69,127,839,735,128,268,67,533,442,868,860,922,88,269,927,859,9,367,613,253,564,930,567,261,260,728,39,237,109,284,276,164,730,489,166,926,125,776 }, +{ 15,13,5,1,2,23,3,0,317,51,115,4,10,11,7,6,217,33,9,77,17,14,341,165,102,117,180,365,134,40,197,153,64,22,12,32,196,21,8,141,391,110,752,372,291,498,352,125,569,16,36,901,202,59,260,28,237,482,349,18,269,457,961,120,318,93,57,509,37,29,128,177,49,31,19,401,48,453,342,38,719,873,45,55,629,111,24,44,143,468,386,50,54,403,86,139,27,308,304,95,210,546,730,475,46,137,98,247,35,72,90,99,82,76,437,494,864,30,326,758,96,52,275,116,402,277,164,56 }, +{ 104,173,20,319,43,66,426,355,712,77,97,0,202,7,198,74,580,204,174,500,32,5,59,2,102,337,90,325,226,234,4,52,22,450,904,155,222,376,861,21,750,33,192,414,194,147,324,342,889,58,544,636,18,291,416,112,784,81,334,64,707,95,331,422,639,715,36,400,14,141,560,792,65,29,177,49,37,315,427,28,57,10,120,24,1,40,48,107,673,11,494,318,230,61,524,128,196,16,12,470,632,232,138,115,329,455,113,142,186,153,819,294,17,525,526,70,117,87,25,247,587,105,269,583,588,158,171,348 }, +{ 23,13,51,5,15,1,2,115,0,141,12,7,4,21,6,165,32,3,269,9,10,457,202,180,197,14,77,11,37,453,22,318,352,120,33,719,217,341,401,403,16,961,17,40,308,18,31,28,8,48,64,475,569,730,260,365,59,128,247,317,137,177,90,93,237,102,99,629,523,44,386,117,36,450,110,153,30,752,899,38,349,72,134,98,509,498,49,326,901,125,24,391,196,421,57,82,291,76,95,19,203,494,89,45,817,304,468,221,139,210,758,351,546,65,373,372,96,54,482,734,144,116,86,39,29,216,402,288 }, +{ 15,1,13,0,2,5,4,23,3,317,115,51,7,6,341,9,8,365,217,22,10,33,77,31,457,197,44,901,18,12,28,180,11,120,165,202,961,102,719,569,40,308,21,730,20,260,351,523,141,36,401,64,482,82,55,49,450,453,403,269,93,318,16,30,498,468,342,14,372,352,110,61,817,125,137,386,117,70,899,25,898,291,509,196,494,475,24,629,854,39,153,349,98,32,134,221,391,416,234,758,59,37,237,95,86,19,91,48,27,177,247,326,903,17,734,873,89,128,421,105,922,57,111,143,638,300,304,90 }, +{ 15,1,3,2,0,4,10,5,9,13,82,12,8,11,16,7,31,23,351,898,365,93,44,854,22,64,21,457,217,180,6,141,317,730,110,318,18,752,719,352,341,51,961,28,269,33,260,77,922,120,202,291,98,55,372,197,349,901,105,165,30,450,14,817,494,523,403,899,128,125,453,95,36,115,116,475,386,326,49,401,111,94,811,642,758,479,474,391,373,203,201,187,153,144,137,38,37,24,102,59,421,282,206,48,569,342,275,216,210,864,846,801,795,692,661,597,559,442,253,244,230,196,177,170,143,127,123,89 }, +{ 2,5,14,1,29,50,38,46,45,17,52,58,114,133,171,218,21,181,48,186,36,6,155,3,19,65,487,12,32,54,27,61,480,323,70,22,51,86,361,137,96,0,208,115,285,227,283,151,23,412,10,281,28,108,37,236,112,328,7,334,163,400,25,4,223,26,67,75,657,194,18,103,128,24,13,514,745,99,401,202,308,532,59,803,389,16,760,11,243,72,64,80,106,9,502,83,832,113,453,56,552,421,403,503,402,95,735,341,956,69,124,782,366,90,141,409,77,57,160,822,433,89,209,244,40,76,177,484 }, +{ 68,101,0,9,212,124,154,65,149,35,16,100,118,24,252,71,562,589,189,336,114,167,265,191,39,329,398,88,67,429,1,264,478,69,166,484,55,30,777,18,399,520,759,13,818,722,175,419,230,48,283,664,796,115,23,28,375,724,346,140,133,103,410,21,705,806,64,51,701,604,83,246,7,345,158,389,60,36,382,302,14,2,108,50,755,5,695,694,237,702,547,917,92,639,411,936,732,213,267,238,32,404,190,10,364,321,289,49,180,553,447,247,650,72,218,46,593,925,468,458,96,905,596,217,95,546,223,439 }, +{ 14,2,1,22,46,67,7,65,28,24,0,69,38,168,50,36,83,289,5,12,16,29,35,55,86,114,103,133,109,49,18,158,95,108,6,194,584,124,185,610,52,169,10,159,189,105,51,23,21,484,283,9,505,389,236,128,239,267,420,164,13,547,143,54,32,64,71,115,149,75,37,140,251,68,48,208,160,253,708,309,137,96,154,244,312,170,782,202,320,412,577,175,308,424,25,141,336,429,218,344,60,892,835,874,347,398,401,531,230,17,278,388,92,135,797,638,732,237,3,323,39,19,286,99,747,4,66,403 }, +{ 23,13,51,15,0,10,115,1,4,2,9,961,457,77,11,5,453,12,33,3,403,14,40,102,197,7,22,165,59,475,64,6,37,401,21,569,719,18,308,99,32,17,177,730,16,141,523,8,341,31,629,217,180,117,386,202,153,196,120,57,28,49,111,125,95,139,899,494,128,421,72,65,210,317,36,342,391,90,19,93,89,44,86,269,365,24,291,237,110,76,318,873,509,482,54,48,300,498,247,134,450,82,275,260,352,98,27,137,326,38,304,758,148,50,79,817,45,670,372,30,179,29,203,734,152,116,349,216 }, +{ 15,1,0,2,5,9,7,23,13,51,901,8,6,12,3,4,180,10,719,341,120,14,31,115,22,141,898,197,82,260,93,21,28,44,457,37,217,125,202,16,153,351,64,110,401,18,11,33,36,961,569,30,77,49,352,450,98,269,139,317,117,116,32,40,453,326,308,128,523,318,365,203,137,730,24,629,854,403,102,39,237,165,55,291,99,475,95,196,48,86,498,134,752,899,373,391,817,244,38,105,242,421,922,59,144,386,57,143,35,349,54,509,482,372,65,253,221,177,304,494,247,638,17,170,72,127,164,114 }, +{ 100,252,265,88,724,101,382,410,48,676,213,909,321,9,593,0,812,329,553,21,352,1,30,230,166,39,419,520,23,51,264,217,221,478,689,435,190,664,13,604,466,677,694,125,752,777,36,98,699,639,83,759,96,35,158,68,128,562,141,223,344,274,310,69,455,115,5,180,404,317,656,364,203,293,831,438,175,308,443,401,333,31,144,570,154,854,105,137,212,346,49,857,530,418,840,855,201,55,921,28,539,421,557,806,821,127,124,67,251,10,634,165,936,22,610,795,16,286,957,167,541,32,962,905,682,686,241,44 }, +{ 58,171,2,46,5,50,14,29,186,1,52,45,334,151,400,38,155,61,103,96,67,227,502,281,223,54,48,17,83,181,552,69,70,309,75,163,24,549,21,311,27,366,80,536,243,113,218,787,106,36,112,433,765,430,209,133,12,6,236,487,19,780,675,394,766,238,587,545,32,37,767,807,834,7,108,114,328,287,315,65,262,820,16,496,583,26,18,454,513,0,3,600,220,25,474,86,307,73,28,35,255,869,537,631,22,72,89,491,409,357,64,270,828,267,189,845,160,221,10,592,137,119,99,659,128,516,541,785 }, +{ 23,202,120,141,51,13,318,269,21,128,260,165,137,197,180,326,1,450,5,77,33,82,15,48,93,64,110,351,32,125,403,203,102,523,854,386,352,457,922,115,7,2,12,494,373,817,615,196,342,153,221,217,16,6,210,144,402,453,36,4,391,14,37,730,9,96,475,291,98,0,99,901,3,421,551,177,18,40,401,237,752,349,31,898,86,719,341,24,704,285,117,11,308,393,116,35,61,160,8,30,65,90,59,44,275,10,241,50,39,216,509,372,67,72,961,247,22,899,28,569,293,83,139,801,473,76,95,46 }, +{ 341,197,901,10,13,569,23,638,15,479,8,873,961,180,165,143,12,115,0,1,951,509,116,37,642,365,499,498,242,253,5,36,898,4,752,2,352,141,268,206,51,55,401,391,7,22,3,417,506,492,82,9,93,11,137,456,338,244,457,64,317,120,110,629,351,546,431,33,21,386,31,86,260,217,164,6,758,965,28,762,474,918,661,326,551,153,125,14,719,94,421,854,203,801,373,269,44,16,730,202,615,237,844,791,811,523,450,40,18,128,864,282,117,111,148,824,940,102,959,17,139,559,170,32,958,867,968,38 }, +{ 23,1,21,51,202,13,15,141,120,5,180,2,197,165,7,269,352,48,12,318,260,326,137,33,386,403,9,450,0,32,6,4,115,128,14,16,523,457,102,37,110,82,93,18,217,3,64,221,752,421,203,453,237,125,77,153,351,494,308,475,196,401,719,31,44,391,373,817,24,730,8,342,30,36,11,40,341,901,96,50,99,615,98,116,117,39,10,22,498,317,17,59,35,160,144,86,28,20,854,961,291,244,177,899,72,402,509,90,922,350,349,95,29,247,285,19,127,661,365,372,49,569,758,65,576,241,898,61 }, +{ 2,29,58,50,262,52,53,27,6,151,14,1,171,5,287,34,324,17,74,46,26,113,309,396,45,223,61,334,96,73,635,445,583,38,19,75,67,103,80,357,70,331,181,573,112,785,236,675,163,281,549,145,366,54,108,315,760,218,220,491,453,297,12,138,62,250,133,436,51,409,200,487,600,128,502,37,64,106,114,651,48,83,394,24,25,820,433,59,766,307,23,155,36,614,311,65,66,137,57,4,765,186,69,21,99,202,56,13,3,587,165,767,7,631,238,86,780,115,283,161,322,545,221,42,32,739,400,47 }, +{ 13,51,23,141,202,115,217,77,317,4,269,33,5,11,318,352,0,102,180,2,64,10,341,7,21,9,137,3,40,128,32,6,22,291,752,15,197,12,365,1,165,31,125,28,498,177,16,403,494,237,18,308,44,59,372,98,349,24,93,48,244,386,49,90,30,342,95,901,475,39,120,196,14,117,457,116,453,17,247,391,153,8,143,482,36,304,203,110,509,326,450,260,37,134,144,210,468,523,86,221,401,569,873,546,719,139,253,35,160,127,285,105,373,421,961,864,576,82,367,437,351,55,579,19,216,275,402,164 }, +{ 1,24,67,50,69,103,35,18,16,14,28,0,5,83,9,54,223,2,96,58,181,12,46,68,48,65,108,133,71,281,149,22,7,163,21,114,36,75,38,55,155,502,238,37,189,99,70,218,45,10,430,433,236,60,765,251,171,61,175,540,23,820,32,51,418,270,29,487,513,52,124,118,336,17,64,49,920,154,13,141,115,545,72,267,221,537,745,290,86,488,309,128,335,39,227,89,328,160,95,834,361,6,101,486,514,543,112,137,474,394,186,202,388,109,150,140,552,807,412,283,307,345,87,780,366,398,77,516 }, +{ 13,23,51,141,202,115,77,269,64,217,102,128,318,33,32,291,177,180,137,10,352,4,21,16,197,0,11,22,9,7,165,90,40,494,125,2,210,247,14,18,153,49,59,31,6,95,317,28,196,349,24,304,5,341,403,342,98,237,117,120,372,44,3,275,35,86,48,1,93,752,36,15,241,12,475,144,30,19,17,216,110,365,260,116,326,134,82,105,143,308,203,20,39,402,386,221,901,127,72,498,437,244,453,76,401,65,38,96,285,316,160,55,351,25,288,482,164,57,139,457,678,490,201,523,509,391,83,817 }, +{ 115,365,180,317,873,498,752,482,341,569,217,901,13,197,352,546,468,1,864,457,509,579,165,23,143,21,15,2,185,36,7,148,12,134,332,22,351,9,261,51,16,5,10,0,346,93,30,141,868,734,372,253,345,116,474,94,77,876,76,28,896,679,384,203,160,39,37,6,98,14,391,109,530,187,843,177,86,629,33,637,11,437,18,4,902,574,434,144,120,44,31,837,72,613,295,169,349,505,228,190,102,95,49,48,706,406,405,96,83,961,617,597,254,237,926,903,717,531,420,258,164,110,860,268,661,477,464,449 }, +{ 184,257,229,205,432,497,152,619,452,17,381,57,266,524,5,45,90,106,313,471,245,32,128,129,59,4,214,2,339,119,769,77,369,12,123,21,14,99,157,3,202,38,710,1,618,216,64,217,292,188,162,76,11,40,249,141,62,51,33,19,372,102,179,37,209,23,27,6,111,318,29,61,403,86,460,349,207,54,269,402,10,48,139,115,36,136,79,137,7,255,72,80,177,519,291,453,529,401,288,58,0,153,475,377,247,306,25,56,26,94,210,22,84,494,50,790,89,775,474,361,328,148,241,588,285,317,678,277 }, +{ 13,23,1,2,0,51,10,5,115,4,15,3,365,7,9,33,11,180,77,6,217,341,22,102,317,40,197,14,12,64,141,165,18,8,17,31,202,28,125,16,269,21,117,901,453,196,177,59,153,36,128,475,318,569,32,24,352,291,403,134,93,401,98,44,752,237,391,372,49,509,457,961,873,308,498,386,95,342,349,116,137,37,139,494,19,30,210,247,143,304,86,629,48,203,111,55,57,719,523,110,482,546,120,38,65,54,326,421,216,730,50,99,899,758,402,260,105,275,39,72,450,96,144,35,25,242,29,437 }, +{ 626,70,687,771,379,767,846,518,878,49,937,591,82,761,31,786,481,407,845,195,590,351,98,44,163,557,13,120,125,48,87,883,371,623,360,817,450,260,178,522,21,5,854,523,116,326,105,291,142,4,296,929,884,259,127,0,132,922,615,77,713,293,217,197,165,22,146,64,931,608,465,457,349,196,32,941,455,279,621,315,202,201,126,110,40,16,11,10,391,666,467,446,373,354,346,144,58,33,581,763,512,916,535,795,768,658,558,447,344,150,92,51,28,24,9,265,212,93,586,562,180,156,147,134,117,1,783,663 }, +{ 264,909,410,897,772,1,686,521,478,914,98,335,96,284,691,639,382,44,744,321,724,100,765,438,223,662,88,144,475,367,31,265,125,116,921,9,682,656,166,363,127,225,21,201,842,0,447,905,393,48,443,421,5,82,329,252,120,805,411,51,821,13,919,69,834,948,934,664,33,23,435,551,274,77,217,131,268,7,93,854,418,520,340,110,30,213,141,453,165,101,39,791,260,922,403,238,698,898,351,102,593,433,64,203,83,303,286,293,197,32,960,812,697,523,466,285,180,692,404,658,35,873,254,163,352,553,190,615 }, +{ 13,23,2,51,1,5,115,0,10,77,15,217,7,6,180,33,3,317,40,4,141,102,9,197,11,341,14,64,165,22,365,17,117,196,352,202,12,269,153,16,291,237,453,128,457,498,752,32,18,134,177,59,21,125,386,318,901,28,8,372,31,349,391,403,95,475,36,137,569,49,308,961,401,509,482,143,98,19,24,44,730,86,120,719,139,546,402,342,93,304,110,29,210,421,873,45,38,629,57,260,326,247,111,275,30,523,55,90,450,46,116,253,50,37,244,65,494,105,99,468,758,25,170,48,96,216,35,27 }, +{ 141,269,77,115,13,318,64,202,177,23,137,217,102,291,5,51,180,48,21,352,0,32,7,16,33,18,11,2,197,128,4,15,10,752,24,244,14,494,317,165,96,9,3,349,247,237,93,35,28,12,6,49,44,1,421,60,20,403,36,341,95,22,475,372,326,308,275,196,144,90,40,39,117,31,678,954,901,304,284,268,221,203,110,98,89,68,46,365,153,134,76,67,576,453,432,402,381,342,179,125,118,86,72,71,69,59,50,17,498,160,164,143,120,111,82,961,873,851,401,377,335,288,285,241,223,210,121,116 }, +{ 23,13,51,15,0,10,453,2,115,1,40,961,33,11,14,4,9,457,22,401,77,102,7,5,569,475,197,12,64,3,21,165,59,719,629,6,196,18,177,386,37,32,17,111,153,57,117,139,31,16,28,730,308,99,38,141,8,403,36,128,670,391,217,210,72,90,523,79,482,341,202,237,45,474,180,125,41,509,46,494,304,275,48,54,95,94,899,29,277,134,120,76,758,65,44,247,498,24,291,148,89,468,86,49,437,82,56,30,402,143,365,349,317,93,50,757,269,734,342,98,352,450,678,62,188,19,152,328 }, +{ 1,22,36,5,12,0,105,28,2,49,31,21,95,9,86,55,13,23,128,7,141,98,170,44,115,239,37,48,127,83,160,4,180,164,51,202,10,269,3,96,6,208,159,137,67,244,320,197,312,50,318,352,64,285,301,125,341,32,165,109,116,217,14,402,708,39,365,16,654,35,30,242,317,169,168,99,253,15,678,11,69,751,569,54,531,752,8,901,947,338,735,77,24,238,143,323,498,158,65,401,17,237,293,946,18,717,740,597,93,512,638,286,403,395,892,144,347,308,299,424,457,185,418,713,194,221,757,824 }, +{ 100,252,101,88,265,0,382,759,329,410,724,9,213,321,553,676,419,593,166,39,21,30,909,694,562,520,68,13,812,264,23,435,677,230,35,352,478,48,190,154,689,212,1,217,570,51,466,639,124,221,83,806,604,175,699,826,703,125,98,308,55,671,664,346,32,158,610,706,96,962,274,31,317,289,344,69,438,49,28,831,917,115,656,455,936,404,165,116,208,777,310,5,854,857,16,201,128,36,333,752,194,67,24,71,144,795,391,197,589,141,818,364,921,131,44,86,203,251,748,105,65,22,180,557,149,702,350,855 }, +{ 539,88,840,190,30,321,530,957,435,748,669,778,144,96,100,466,418,863,410,203,228,213,382,274,795,406,613,131,36,822,1,83,326,856,219,352,335,494,251,230,679,31,224,960,44,831,855,141,859,944,217,9,367,160,201,137,625,128,166,93,258,662,120,800,521,403,116,98,187,28,576,752,816,269,202,557,39,931,21,265,686,616,443,921,10,180,731,438,689,523,473,125,871,317,23,77,197,457,827,55,264,35,617,692,254,33,719,260,86,966,817,32,829,206,276,127,242,475,456,284,51,615,252,0,854,238,492,551 }, +{ 7,97,173,92,232,298,314,107,387,729,356,43,359,126,246,140,362,441,501,426,392,230,560,20,14,707,885,104,16,66,330,779,172,683,60,35,493,861,22,357,5,0,95,49,81,279,38,568,18,24,21,74,192,71,414,830,423,259,324,2,121,526,125,633,508,32,672,52,319,10,150,48,28,147,29,46,715,347,6,64,199,380,189,61,858,69,660,33,204,135,649,713,36,595,1,98,37,879,31,146,132,75,819,263,784,443,408,607,448,54,12,663,138,68,58,117,102,70,13,267,65,55,712,91,153,77,399,165 }, +{ 14,2,7,58,46,1,38,16,75,5,70,52,24,155,29,45,220,35,163,69,50,171,61,186,87,227,18,270,92,48,666,54,32,290,140,21,278,592,513,307,67,112,262,6,66,60,37,0,135,243,97,767,64,425,23,89,71,72,409,400,128,845,113,17,334,36,22,172,20,10,315,115,76,141,9,28,51,12,663,103,86,74,77,207,13,65,177,90,263,83,267,202,95,137,43,269,151,294,189,27,25,431,19,49,428,430,68,40,223,536,852,106,738,324,226,655,474,780,96,502,496,217,114,108,318,230,102,148 }, +{ 447,411,664,363,647,478,936,621,354,271,98,781,759,223,777,1,360,18,691,668,48,932,167,156,132,121,212,195,591,446,724,520,118,586,146,21,23,463,31,293,9,264,393,16,51,914,13,125,807,101,49,834,284,44,60,163,682,24,848,221,658,541,5,105,35,71,68,55,96,39,116,562,407,32,905,604,22,252,772,0,33,765,522,69,10,963,67,191,93,100,638,676,166,340,364,120,281,698,608,286,821,404,50,115,382,144,77,265,697,64,28,102,909,126,410,30,628,82,662,95,367,7,815,127,744,335,40,296 }, +{ 14,2,1,46,67,65,38,7,69,36,29,24,83,28,50,0,22,103,5,6,52,124,108,484,168,12,114,86,75,133,208,18,16,194,236,283,289,389,21,610,10,109,756,160,25,267,35,23,51,95,32,309,55,505,135,584,71,13,185,66,547,128,189,37,732,54,96,48,169,646,17,49,412,9,420,158,87,19,323,115,278,244,26,64,218,164,70,143,253,74,238,140,220,398,172,45,137,58,149,262,797,141,92,308,4,3,503,302,202,60,68,388,154,159,118,650,531,90,516,366,150,285,105,532,335,251,671,429 }, +{ 386,421,51,791,801,697,958,730,453,23,165,551,221,670,615,326,401,391,576,403,13,102,457,899,805,523,842,770,48,24,450,475,0,203,125,949,541,719,566,77,193,115,308,197,21,18,398,153,33,393,317,117,43,202,137,743,120,300,7,124,467,5,91,108,9,71,342,291,118,42,32,16,97,134,85,279,61,260,173,289,281,517,494,14,859,817,196,60,809,758,692,36,882,25,194,388,20,373,302,140,65,547,429,273,851,717,458,412,122,104,95,509,233,81,492,318,6,362,832,110,69,92,49,149,35,508,298,608 }, +{ 319,104,945,422,81,20,715,43,0,879,693,681,306,32,784,74,173,207,61,7,22,52,397,671,414,904,887,636,240,95,6,115,835,192,5,707,91,426,18,2,76,29,819,330,316,263,56,85,138,84,28,232,577,365,64,97,113,519,312,725,526,17,344,370,204,49,202,214,329,24,38,560,403,750,66,494,10,804,500,241,25,105,289,718,159,587,523,77,124,102,86,861,348,498,450,292,51,288,90,117,140,65,899,510,168,347,158,16,448,45,817,237,107,324,33,9,194,180,120,318,92,790,40,280,21,356,320,874 }, +{ 2,29,70,1,52,262,75,112,26,145,46,163,309,58,103,67,14,50,6,220,96,74,38,394,223,331,357,19,83,238,45,69,5,324,171,287,27,409,151,25,366,307,24,583,218,537,133,108,632,42,267,396,451,53,36,433,87,114,65,17,113,635,186,764,550,545,226,600,138,675,388,66,37,21,61,54,48,181,122,516,12,606,294,236,418,73,35,189,18,80,86,486,487,651,135,155,150,7,32,16,137,644,243,739,281,222,445,334,278,380,573,149,849,106,227,582,603,160,335,34,64,283,696,648,765,624,297,72 }, +{ 771,929,786,941,590,626,687,259,465,178,70,878,132,5,13,21,534,146,49,518,279,48,107,35,379,493,31,147,125,767,105,108,87,195,221,481,126,121,44,163,22,623,23,127,61,581,407,362,0,150,165,937,427,58,846,345,371,142,883,467,167,761,120,33,10,640,98,75,591,360,391,1,663,271,77,30,199,4,845,763,522,60,185,102,291,7,349,159,156,137,128,246,450,51,308,296,293,239,32,11,158,423,558,557,342,269,116,39,260,112,17,386,232,502,441,281,196,153,95,37,12,783,953,612,535,401,197,15 }, +{ 5,186,45,2,61,29,50,227,52,400,58,17,46,48,171,209,14,155,106,243,80,311,38,1,430,281,496,54,502,536,255,181,454,513,103,96,592,760,3,334,207,21,666,67,119,552,587,869,663,62,151,541,27,32,583,309,721,84,56,37,659,218,133,366,221,136,83,540,6,834,72,36,223,270,667,290,738,12,328,34,807,89,287,263,474,19,487,262,236,238,148,76,75,115,69,425,240,378,99,26,394,24,766,433,483,128,114,306,70,549,65,324,600,675,828,357,764,53,780,108,545,73,137,161,7,833,90,765 }, +{ 0,95,453,64,32,389,494,817,342,207,312,365,577,690,194,403,848,237,202,316,180,671,5,482,757,61,490,805,102,289,498,450,65,835,475,523,113,49,401,241,887,115,120,263,329,21,17,18,576,22,33,137,344,13,318,402,77,3,1,899,11,48,347,4,210,141,118,125,90,51,933,942,260,23,645,874,306,308,117,134,597,615,27,247,12,196,24,122,19,6,2,751,697,743,317,468,291,25,16,288,177,730,629,28,86,84,349,770,421,214,611,592,491,483,476,425,56,36,670,300,275,153,217,621,350,39,71,60 }, +{ 23,13,51,202,115,15,5,1,12,4,2,77,403,165,0,21,10,318,453,32,457,33,128,197,9,7,6,141,3,401,475,59,177,269,14,11,308,17,37,494,22,719,217,99,64,341,180,117,18,90,40,102,120,16,317,730,961,569,48,365,28,421,386,352,629,8,38,523,450,237,31,291,402,125,210,137,247,216,19,391,196,57,221,36,509,326,260,72,93,24,349,275,153,203,49,95,498,342,304,899,45,134,82,44,76,30,468,372,98,89,27,817,25,139,86,110,758,65,54,29,111,546,351,116,241,46,20,482 }, +{ 119,292,6,45,306,76,2,26,240,209,17,61,710,106,5,80,29,19,27,25,214,84,113,32,64,618,129,255,128,1,179,90,123,4,59,65,151,14,58,667,40,460,311,56,504,22,245,86,249,207,0,790,141,205,42,95,77,483,3,833,10,112,115,202,7,23,18,887,137,194,11,52,57,36,62,136,269,476,177,162,99,33,491,21,257,73,769,454,12,70,38,28,402,13,24,145,16,48,72,318,184,196,614,445,122,102,53,114,49,117,153,210,548,285,46,54,464,134,180,51,396,50,297,183,315,243,237,814 }, +{ 15,1,23,13,51,180,0,2,120,5,7,9,450,523,33,12,115,260,197,102,165,6,202,77,4,21,141,14,457,351,403,341,16,901,8,326,82,308,365,3,269,18,36,137,93,318,48,128,386,59,31,37,719,217,453,401,730,817,22,110,44,475,64,317,352,10,32,28,421,494,40,30,116,98,11,899,342,373,752,196,24,615,221,177,86,291,49,854,203,39,99,509,961,96,144,569,898,55,922,35,391,210,117,244,629,125,349,498,873,17,50,247,372,89,160,758,237,670,300,95,127,143,153,65,285,402,90,801 }, +{ 6,138,222,280,74,85,66,226,337,87,42,204,235,25,145,294,64,135,95,174,609,29,112,415,256,26,65,525,792,461,353,32,580,22,52,122,746,172,630,544,289,70,348,416,237,287,19,278,49,312,86,605,75,10,108,2,0,194,192,5,620,1,7,21,90,97,736,220,63,45,632,500,114,214,61,355,36,151,233,396,180,58,69,24,125,141,72,28,14,648,27,519,673,889,872,624,241,444,288,18,343,197,48,426,33,207,133,665,263,232,113,73,347,91,17,568,448,316,103,208,165,128,104,53,380,12,81,498 }, +{ 39,9,333,101,0,158,252,310,254,272,455,68,49,627,327,344,159,286,419,364,18,16,709,329,24,688,105,265,109,30,100,553,118,374,35,557,166,124,190,154,677,131,65,578,912,71,793,22,60,518,1,538,51,167,21,23,98,28,212,48,95,438,604,625,13,404,7,31,96,55,127,520,36,731,137,890,857,125,320,616,694,656,806,716,44,577,5,10,170,224,67,83,829,168,242,219,228,128,191,14,239,827,88,86,301,347,12,424,160,747,810,37,115,64,846,346,371,613,69,910,221,33,149,2,562,93,865,676 }, +{ 21,32,14,5,38,76,72,2,48,54,270,45,1,17,50,46,221,290,181,89,36,430,3,741,428,218,408,155,37,540,281,449,0,10,12,148,179,90,241,263,207,29,292,133,425,22,477,328,99,51,697,421,114,67,770,487,115,476,23,4,528,227,65,28,6,119,243,488,214,720,552,541,86,474,24,52,350,64,61,7,9,137,11,186,103,536,401,129,745,413,106,58,83,96,920,308,236,13,57,513,123,18,842,464,543,361,245,160,56,59,205,128,655,431,514,358,828,285,171,108,460,55,62,400,27,16,453,40 }, +{ 137,269,141,202,120,450,260,318,494,922,351,291,82,77,5,326,678,352,704,854,128,523,1,817,21,15,64,349,180,342,32,13,475,90,177,947,7,372,61,51,49,23,165,95,403,11,207,115,312,102,4,48,197,237,402,719,203,50,96,308,30,6,2,373,205,86,70,69,67,36,210,241,105,898,859,851,615,208,194,217,901,285,25,453,216,182,163,87,65,46,45,8,0,457,31,307,139,112,58,37,12,3,117,968,158,127,20,730,386,273,199,178,168,147,126,108,60,28,18,16,10,9,954,795,597,275,247,134 }, +{ 16,68,71,24,149,35,191,118,167,212,18,124,101,246,108,399,189,154,140,92,267,9,60,375,65,458,695,302,133,67,398,589,7,0,69,230,114,562,702,1,705,103,55,915,755,283,21,388,602,336,412,48,14,175,273,650,50,345,823,674,411,46,121,146,28,363,236,447,39,10,64,672,22,664,478,796,264,309,330,271,100,520,647,37,95,218,199,12,906,23,565,36,83,32,252,777,88,166,628,126,5,13,125,49,75,2,366,51,96,86,732,486,346,54,815,237,733,429,98,891,759,516,633,818,115,443,723,238 }, +{ 28,109,0,9,67,22,1,55,65,114,103,158,39,49,83,194,168,69,35,420,185,289,36,50,208,24,68,133,2,124,505,251,218,175,86,46,7,389,323,154,95,169,159,346,12,547,160,14,756,101,646,16,570,105,30,5,799,189,533,38,531,96,503,238,596,252,128,64,535,23,54,18,21,344,51,782,272,336,610,419,283,532,345,333,480,13,100,10,37,137,327,115,789,584,149,286,230,361,48,75,285,181,202,29,254,374,164,429,927,141,71,896,143,671,930,577,32,237,140,810,244,484,439,31,131,601,402,310 }, +{ 325,147,427,198,178,142,798,202,259,5,376,20,534,43,465,450,904,318,234,470,858,77,725,455,120,581,329,315,70,97,61,929,105,878,786,113,362,260,58,557,481,403,660,14,639,21,173,0,170,49,159,494,319,269,48,23,344,523,128,107,301,595,1,845,518,416,33,884,817,324,320,13,326,199,22,475,402,158,37,270,132,51,40,38,35,32,7,95,590,121,4,422,239,141,104,66,146,347,112,615,263,230,126,75,28,307,76,207,704,414,360,265,195,9,922,453,356,298,19,17,137,72,342,290,102,87,50,395 }, +{ 28,0,65,109,9,35,55,175,124,67,289,24,39,158,68,114,14,336,570,154,420,1,22,7,185,168,69,133,194,49,16,429,149,101,46,505,83,189,2,103,169,346,50,252,484,36,547,389,610,283,18,756,95,419,159,535,345,251,86,71,105,12,108,398,100,230,30,140,439,333,533,344,584,703,21,208,272,286,671,38,5,327,267,10,64,601,23,254,51,96,118,810,13,48,164,782,265,128,364,236,29,143,160,837,329,374,677,54,577,218,310,637,399,60,37,962,797,92,166,323,793,705,32,238,732,75,6,596 }, +{ 141,180,115,269,13,23,4,1,352,5,202,318,51,317,2,217,21,752,365,77,3,7,165,341,197,32,0,137,11,308,10,6,901,33,15,9,31,22,177,102,403,64,401,12,30,44,498,237,291,48,143,244,93,349,569,40,494,28,8,39,253,125,372,90,221,98,453,304,386,482,873,509,203,120,110,116,18,59,49,457,144,128,95,247,391,864,326,117,16,421,37,153,14,196,475,36,629,134,105,210,260,961,373,160,164,55,719,546,437,216,734,35,468,350,201,17,139,96,89,579,473,72,758,717,111,24,743,275 }, +{ 15,0,1,2,9,23,13,5,7,51,12,6,4,8,901,33,3,180,14,197,10,341,115,77,102,93,120,141,260,22,21,82,457,899,31,64,453,28,165,16,450,18,523,11,351,475,202,36,110,37,203,401,125,30,352,898,44,403,961,730,719,116,128,569,421,365,196,49,40,98,32,217,137,373,269,39,308,237,144,55,854,153,177,318,317,86,326,24,17,117,95,629,48,817,752,59,300,670,498,99,391,494,386,291,134,38,105,90,642,342,922,873,242,143,244,253,393,473,221,50,247,372,57,349,139,65,111,661 }, +{ 6,74,138,66,75,85,25,324,42,380,278,135,26,500,87,226,1,220,97,29,70,222,889,841,52,350,19,606,280,235,294,394,112,571,69,853,21,38,145,335,903,267,582,65,550,309,14,172,792,509,32,2,197,64,749,262,237,174,241,357,122,95,461,486,808,67,337,256,537,96,103,115,774,546,204,63,125,27,391,22,558,462,83,451,7,238,630,5,308,802,24,165,108,734,46,163,140,950,648,641,92,45,13,298,189,416,583,525,150,72,923,86,600,48,114,53,331,221,388,12,620,180,468,426,90,645,746,348 }, +{ 51,23,13,453,12,115,403,4,457,401,165,475,15,0,1,10,11,37,59,5,99,40,3,308,64,197,32,2,33,202,17,9,719,77,629,730,7,14,22,6,961,21,569,523,494,421,386,18,120,141,57,125,177,318,269,102,237,128,90,38,899,341,45,450,111,76,16,180,217,19,36,31,134,117,247,49,670,139,72,28,210,95,291,196,402,153,41,25,24,89,509,216,260,8,93,86,54,221,82,137,65,468,758,26,56,44,27,317,391,352,817,365,304,62,349,123,498,48,34,326,275,152,775,342,114,372,300,757 }, +{ 788,180,319,21,5,83,32,498,314,547,526,17,693,61,817,48,11,4,27,510,33,12,848,77,288,104,24,160,0,814,120,64,3,804,153,134,117,56,519,342,196,102,207,725,326,690,523,450,69,40,403,95,899,306,60,958,90,961,942,801,500,232,174,155,84,43,38,115,945,457,386,595,475,615,247,887,715,671,494,482,365,192,13,312,202,165,10,9,81,51,566,65,71,119,113,861,835,802,770,757,756,746,743,729,692,683,629,587,583,551,550,525,449,426,422,418,414,324,291,260,221,217,214,186,170,125,123,105 }, +{ 15,1,2,13,23,4,5,3,0,11,7,6,341,51,10,9,12,33,77,115,102,40,217,14,197,180,165,141,365,901,569,317,202,64,22,21,8,16,59,32,403,31,17,269,177,318,475,18,196,386,453,352,153,93,401,120,125,117,28,291,457,494,210,110,37,961,629,326,128,391,237,260,139,49,44,349,98,372,116,719,342,36,523,95,57,86,111,137,19,24,203,450,308,752,30,275,38,730,134,90,509,304,373,247,300,144,48,498,27,45,20,758,482,65,143,72,105,55,99,35,670,76,873,402,615,82,29,817 }, +{ 101,520,18,167,9,118,212,604,364,16,60,694,252,0,264,917,24,68,146,55,363,562,35,191,121,166,265,806,329,71,676,100,706,647,411,271,39,907,628,621,925,21,419,848,404,28,682,942,48,1,815,154,777,446,109,132,124,5,821,463,49,857,158,13,199,905,967,23,932,126,88,108,22,346,96,382,354,759,51,575,125,105,98,92,32,522,65,286,333,31,447,812,639,7,553,664,566,727,159,593,30,10,149,150,865,156,375,14,897,936,407,95,638,274,478,175,439,272,67,221,634,302,50,2,12,107,144,44 }, +{ 13,23,51,115,33,217,77,141,64,0,4,102,202,9,2,317,7,269,180,21,291,352,32,16,10,128,5,11,318,6,177,40,341,3,18,22,165,197,137,15,14,153,247,237,125,28,24,12,59,196,365,31,349,117,90,752,49,403,372,98,221,93,308,17,95,44,86,342,48,494,1,304,210,498,30,36,120,475,453,134,401,241,244,386,143,275,19,391,901,72,110,35,39,203,144,326,116,260,164,20,437,457,37,402,509,421,8,482,55,523,105,82,96,316,65,350,450,864,139,216,25,76,285,29,27,579,546,127 }, +{ 66,135,172,74,97,204,278,6,138,324,7,174,87,192,85,448,140,353,29,232,280,620,500,52,802,92,25,226,808,38,568,380,889,42,298,69,426,580,792,314,571,330,2,872,348,95,26,572,622,43,563,32,526,222,893,22,14,65,5,21,81,64,673,24,75,267,950,1,355,885,220,70,357,726,145,104,86,63,888,91,387,189,16,46,289,294,126,71,501,20,107,233,60,19,48,462,335,67,173,28,45,194,49,241,550,235,230,37,90,0,312,83,18,415,61,58,36,262,12,35,112,633,10,72,150,712,246,343 }, +{ 97,24,2,69,66,423,298,6,29,7,267,172,74,52,14,189,67,38,25,607,150,149,83,71,1,36,18,135,733,467,81,0,140,501,426,517,21,33,398,278,160,118,124,809,42,154,26,19,462,85,273,92,20,46,429,302,117,60,336,568,508,204,28,192,715,232,770,705,165,65,16,153,596,173,102,279,50,91,87,175,43,23,448,45,12,861,386,37,13,63,421,104,48,51,324,388,314,197,134,711,138,32,531,526,330,681,712,443,22,451,75,608,185,4,949,697,221,882,636,236,370,196,54,420,5,309,885,458 }, +{ 158,9,0,68,39,333,109,344,49,101,24,16,35,159,124,154,254,272,310,793,252,327,419,65,627,286,71,22,364,455,18,374,105,577,747,28,30,168,912,118,677,55,95,185,709,100,688,149,60,175,671,131,190,346,166,265,518,578,538,329,169,703,424,1,212,21,7,694,962,289,706,439,347,191,108,51,810,48,10,634,23,67,14,616,239,553,170,302,557,98,320,125,31,2,570,857,625,13,5,36,12,128,375,86,137,127,114,37,716,88,44,913,420,92,83,69,96,50,64,224,638,562,242,301,167,890,731,160 }, +{ 15,10,33,77,197,4,180,341,365,115,13,102,23,317,141,117,217,5,1,901,11,40,253,165,134,153,21,752,244,143,32,36,352,51,638,137,2,196,12,55,3,873,569,951,372,82,37,120,291,177,7,59,898,349,6,64,0,509,31,342,269,22,351,498,260,308,160,93,14,139,854,44,202,28,86,48,457,17,482,90,401,450,111,719,72,203,116,125,110,523,128,961,164,57,304,922,9,386,318,38,730,326,49,762,535,275,391,277,629,210,242,373,99,98,148,96,734,817,95,89,237,717,247,30,479,45,758,54 }, +{ 100,382,166,478,265,264,404,274,593,438,88,39,921,724,98,639,682,1,363,411,447,435,905,812,821,9,96,30,897,31,656,23,223,252,254,310,664,410,116,190,51,144,329,691,21,520,393,44,127,101,0,909,48,125,13,625,333,284,557,521,418,286,367,960,772,5,93,77,321,118,105,83,948,616,272,530,33,293,203,795,156,765,676,68,466,102,67,551,131,604,120,242,49,35,16,354,848,421,18,32,553,730,69,158,335,165,759,791,110,731,914,163,613,115,10,386,217,24,36,778,834,65,395,4,578,238,842,364 }, +{ 19,283,436,297,53,26,813,122,42,432,503,27,605,25,736,73,390,235,389,128,630,123,250,529,63,256,80,65,108,445,690,573,269,789,106,444,216,113,381,412,6,14,133,680,200,78,318,650,177,678,17,194,57,114,619,368,4,349,84,472,59,112,85,275,291,56,18,378,714,40,32,396,183,102,11,152,33,416,136,495,341,236,46,403,130,161,90,243,561,255,184,24,129,597,0,742,86,45,469,76,145,862,401,285,249,141,947,665,13,179,51,524,287,50,527,803,233,117,684,12,304,148,205,29,719,23,139,61 }, +{ 202,128,494,141,33,269,137,36,51,0,13,102,180,6,2,1,196,65,4,86,402,95,29,475,403,117,59,40,457,77,450,15,210,177,134,49,22,21,18,291,153,12,3,260,114,90,817,386,318,120,25,16,32,23,11,961,922,576,237,217,194,165,115,14,7,453,342,216,873,373,197,164,64,719,874,854,789,757,751,704,597,558,391,372,326,304,301,300,285,275,208,203,159,133,110,105,99,93,81,74,70,66,52,48,47,42,41,34,28,26,24,10,5,139,76,958,899,801,730,615,551,160,963,901,752,657,584,421 }, +{ 18,60,16,68,167,191,101,118,212,121,35,126,199,146,647,520,55,9,628,806,621,71,24,107,575,346,363,132,446,463,364,0,566,92,562,28,271,411,252,1,727,21,815,48,407,150,604,598,694,279,932,154,608,49,589,493,439,22,5,7,124,109,706,419,10,108,13,354,14,23,640,467,375,96,273,158,848,302,955,535,175,195,39,818,534,105,125,100,221,522,676,264,777,12,51,50,159,329,32,265,65,259,362,917,98,20,826,783,149,37,31,581,95,169,43,67,97,46,223,147,907,166,590,64,246,36,2,967 }, +{ 352,230,531,160,36,748,854,217,93,669,728,137,251,689,871,699,373,228,574,258,202,752,351,717,559,120,144,268,318,260,219,203,269,201,831,533,839,567,539,187,840,213,816,489,679,82,1,317,851,332,44,141,55,295,855,206,30,617,863,418,83,345,926,96,180,443,800,473,116,10,143,326,110,35,406,564,131,224,22,860,276,64,450,28,98,9,21,190,957,466,822,968,244,164,31,434,856,169,384,661,69,898,0,365,730,824,284,125,442,253,128,372,873,498,88,530,922,776,261,165,494,48,237,86,68,940,39,927 }, +{ 15,13,51,23,0,77,8,4,5,1,202,33,10,11,165,180,82,22,12,110,115,9,102,197,40,59,342,403,141,120,351,326,7,453,373,31,386,18,93,457,260,49,2,401,318,475,128,28,901,64,269,291,44,494,36,854,719,21,177,14,30,523,961,16,898,57,3,349,421,125,922,99,341,32,730,98,137,203,56,217,317,450,117,615,76,391,498,275,24,352,569,365,95,6,105,308,144,37,196,899,86,65,153,39,535,90,210,629,116,704,247,83,208,237,48,661,752,638,509,17,758,402,133,551,143,139,817,244 }, +{ 101,9,100,0,88,217,252,68,39,35,352,562,175,30,115,230,752,213,265,419,13,317,197,166,180,154,237,83,212,158,124,64,321,777,382,208,1,336,410,65,194,759,329,694,36,141,826,289,23,365,724,341,429,128,509,69,165,570,345,676,909,96,346,756,553,21,593,372,854,702,498,55,799,264,399,67,86,190,28,468,589,768,51,364,308,144,677,755,689,323,125,722,482,610,547,391,333,137,789,812,189,546,49,32,952,310,247,274,131,72,16,344,98,10,671,351,435,386,251,478,285,244,901,167,160,484,706,164 }, +{ 32,64,95,147,288,21,90,5,519,470,49,61,0,356,22,48,115,362,97,247,312,141,835,387,560,57,56,4,182,128,20,237,178,59,18,11,31,13,107,214,202,28,298,217,44,76,316,7,65,43,259,180,819,874,51,40,23,216,173,330,207,197,150,25,2,86,729,324,120,376,279,140,612,392,595,104,72,66,29,6,352,37,12,210,678,414,172,683,779,490,482,317,291,165,134,102,81,77,58,45,33,26,195,864,795,304,858,87,54,660,427,707,878,465,357,501,232,753,205,177,170,126,117,91,19,597,230,212 }, +{ 24,16,14,35,7,18,69,1,50,46,75,5,2,58,163,67,54,70,0,71,28,502,60,48,38,12,9,21,189,103,155,140,267,223,281,108,37,171,430,92,394,65,68,22,230,513,83,10,23,181,32,55,309,87,45,36,236,51,335,29,220,149,307,99,61,133,112,141,64,13,780,52,6,115,128,227,270,72,89,852,537,328,17,150,474,49,807,114,767,221,552,186,96,290,95,20,246,366,77,443,400,137,202,86,269,486,666,388,237,540,25,278,477,217,4,536,218,765,177,19,125,180,165,126,113,318,251,39 }, +{ 195,18,354,265,252,411,39,9,363,167,101,682,271,132,16,121,60,1,333,203,21,593,35,146,520,156,364,447,639,360,212,5,24,327,23,627,668,310,404,13,48,664,286,100,538,0,777,676,77,98,821,7,68,33,692,446,865,93,158,576,159,116,115,647,966,883,551,407,272,107,51,687,120,591,463,326,137,71,102,857,31,781,421,37,586,30,44,329,379,50,125,374,166,105,92,96,621,709,142,65,36,127,562,475,109,403,14,126,118,70,346,223,479,199,805,450,190,43,20,191,936,905,656,128,254,12,2,202 }, +{ 4,115,13,5,23,141,51,180,2,202,1,217,11,269,318,3,77,341,7,6,317,33,365,0,21,12,352,197,165,9,15,32,102,10,137,40,177,308,752,64,291,31,22,901,349,372,28,8,59,403,90,494,30,237,498,44,120,37,401,125,48,39,14,93,17,244,49,457,569,509,128,153,16,386,98,143,304,36,873,475,196,391,117,482,342,253,210,203,110,453,116,247,373,221,326,24,216,18,144,72,421,260,95,55,134,450,961,576,86,139,719,105,468,35,164,82,57,127,160,89,864,678,523,351,88,275,67,546 }, +{ 7,71,24,14,16,35,140,92,189,149,150,108,60,68,18,230,97,69,66,314,273,267,458,388,336,38,2,65,232,251,175,399,118,46,398,172,67,28,462,246,36,55,375,0,705,1,21,124,154,22,298,302,135,95,451,733,877,126,594,423,345,64,467,101,9,74,517,29,48,86,602,236,6,20,191,83,596,52,330,380,32,357,43,5,10,674,50,12,133,49,429,723,103,711,212,346,104,192,114,107,915,278,167,387,37,443,324,309,121,823,54,125,96,633,566,221,796,484,75,109,547,603,13,23,289,809,575,607 }, +{ 1,22,31,5,36,12,105,44,2,86,21,170,98,180,127,0,55,141,64,23,7,352,28,13,9,128,116,125,164,239,4,301,49,115,6,95,197,37,3,160,242,51,341,752,202,83,498,269,10,96,165,144,48,253,244,285,901,217,320,365,208,569,137,8,338,299,318,395,293,109,654,93,512,143,32,50,237,317,159,402,864,11,15,708,776,873,169,201,14,35,99,67,638,39,954,30,844,531,479,678,77,69,456,740,533,16,403,308,268,120,751,951,33,961,947,457,286,238,401,276,482,203,601,17,506,312,509,18 }, +{ 97,66,192,7,560,52,20,2,135,74,43,173,324,104,580,414,226,107,147,750,204,29,174,500,376,568,825,707,222,0,356,470,77,32,331,194,90,38,729,16,819,298,92,172,355,22,60,64,888,595,357,232,24,18,259,712,202,353,426,81,5,362,262,874,427,526,49,330,14,178,95,885,387,21,10,138,37,861,142,6,33,102,86,28,889,4,359,65,312,36,858,126,59,1,112,792,673,337,58,151,177,91,35,113,319,291,9,115,145,13,636,501,342,448,278,140,12,87,61,71,583,294,751,465,237,246,230,325 }, +{ 101,0,68,9,252,154,124,364,166,35,39,100,24,65,419,16,149,694,71,265,329,28,439,158,346,88,30,289,212,404,55,671,703,562,570,49,274,175,429,21,706,589,18,553,118,375,1,722,67,302,22,109,962,190,677,382,333,108,60,23,967,191,168,95,69,48,344,14,51,336,114,254,676,610,484,13,398,10,83,92,7,520,310,857,818,125,264,593,189,64,133,185,826,438,12,32,159,865,221,103,535,50,194,695,286,105,36,604,96,169,420,230,283,701,167,241,131,140,2,350,656,115,272,5,239,637,812,98 }, +{ 39,100,127,30,737,836,9,254,856,31,166,190,98,395,0,385,88,931,625,131,373,264,863,93,827,28,228,120,44,125,1,219,116,137,110,382,371,55,13,101,731,299,260,144,908,442,21,77,265,23,456,64,203,51,599,242,33,187,49,326,310,22,224,578,202,5,450,36,272,18,276,16,141,795,82,105,916,284,165,274,318,48,95,512,944,35,716,478,24,935,258,201,616,851,32,557,102,96,252,109,68,269,128,351,435,492,83,724,86,67,7,10,704,293,772,59,921,329,846,156,924,286,197,438,717,4,291,244 }, +{ 17,5,45,21,14,2,48,3,32,155,106,227,186,181,38,29,61,209,62,46,328,133,400,80,243,54,56,52,1,51,832,23,19,50,99,255,114,430,6,84,543,536,12,514,401,236,27,37,137,361,552,453,119,136,34,311,58,218,171,221,76,72,4,754,115,281,108,13,285,496,65,454,629,513,41,207,308,540,421,26,290,805,480,541,488,920,657,202,475,11,487,787,128,86,89,569,57,739,151,838,502,36,270,59,403,208,90,10,7,721,474,22,263,745,576,0,457,659,592,663,587,25,161,129,165,148,323,894 }, +{ 131,613,224,856,228,406,716,274,827,30,100,219,190,39,166,863,88,931,924,829,944,31,382,44,625,9,127,1,442,385,187,98,116,731,93,578,258,254,530,816,137,871,160,616,960,96,36,310,265,0,371,203,144,836,410,438,264,435,795,125,242,21,202,120,201,531,276,908,51,921,128,23,101,49,839,846,28,737,48,326,5,83,778,13,110,64,284,105,35,492,272,10,557,33,938,115,299,77,252,158,456,367,268,669,217,260,165,213,32,177,22,800,352,473,86,102,69,197,574,321,318,567,141,935,593,403,55,180 }, +{ 352,217,752,141,954,864,64,372,437,269,180,115,498,177,349,776,579,291,317,247,77,947,873,717,304,318,678,237,179,23,197,128,33,0,35,165,13,275,68,210,457,341,459,1,67,410,32,143,365,65,244,202,102,9,160,140,137,225,569,12,36,18,125,75,51,134,93,751,15,546,509,86,708,482,928,713,330,312,201,164,144,100,10,7,521,268,246,44,24,21,16,558,55,851,682,342,260,245,120,117,101,98,39,28,726,363,116,110,30,189,72,478,83,744,953,841,824,672,563,358,166,114,92,31,5,386,196,69 }, +{ 141,217,317,180,352,365,115,341,5,372,23,13,752,498,253,244,15,1,237,2,51,197,269,0,7,165,21,202,64,349,77,6,137,143,4,318,3,9,901,546,33,177,308,11,32,10,160,12,48,49,482,30,22,125,468,98,373,247,120,102,93,31,864,110,509,260,531,391,28,44,494,437,36,450,569,90,40,490,453,326,221,219,203,16,128,96,82,386,164,72,873,291,251,224,187,117,403,401,210,39,131,116,24,18,473,144,86,37,629,148,105,8,533,951,758,475,421,228,139,170,134,95,65,35,734,704,617,275 }, +{ 5,2,45,17,14,29,48,1,21,58,181,38,50,61,3,133,46,155,171,186,114,52,32,27,281,19,6,218,36,236,227,54,361,151,51,12,754,209,26,400,80,137,514,243,65,106,552,487,13,480,334,23,285,86,412,328,108,4,283,502,311,37,56,99,323,22,745,657,208,202,25,62,72,128,115,838,760,59,430,7,34,536,64,70,454,11,76,10,28,255,119,803,194,787,67,421,0,576,57,859,496,103,84,96,18,532,75,165,89,453,389,223,24,920,401,163,73,113,308,828,402,77,739,112,287,148,457,513 }, +{ 6,25,2,119,790,29,113,76,26,45,483,655,887,42,292,32,19,4,59,128,151,207,1,306,84,17,814,106,240,90,112,209,61,145,14,177,77,64,65,52,833,27,122,179,491,210,70,0,72,311,115,40,255,58,614,74,85,7,214,202,710,22,5,57,469,21,141,11,123,80,476,95,36,396,33,10,24,269,38,137,102,66,425,23,416,49,194,86,275,504,13,216,56,402,318,46,358,18,138,28,460,624,9,148,50,174,129,618,51,205,87,75,16,291,186,12,217,249,222,287,454,20,54,99,243,315,117,114 }, +{ 23,13,51,115,2,4,5,0,141,180,7,11,217,3,33,77,6,1,202,9,317,10,102,197,21,269,40,352,341,32,12,165,16,22,64,318,365,28,128,15,14,18,752,137,59,24,291,308,177,125,31,17,403,498,237,494,372,98,49,117,901,93,196,30,44,153,457,349,8,37,120,134,90,116,247,342,36,482,401,95,35,210,143,221,86,453,260,203,144,475,326,48,244,110,391,386,55,304,873,39,19,569,961,421,105,509,719,546,450,253,864,216,20,402,29,523,72,57,50,139,25,730,373,468,275,65,82,629 }, +{ 23,13,51,202,21,141,115,5,165,318,32,12,403,197,453,1,269,120,15,128,2,180,475,125,308,457,401,7,48,6,386,64,0,730,4,9,719,137,3,10,523,18,33,14,37,421,16,99,494,11,260,450,341,221,17,196,93,77,40,217,22,117,90,95,237,899,31,391,247,352,326,28,24,102,19,153,30,317,98,961,817,134,82,44,25,59,8,241,110,49,365,20,26,291,27,61,177,36,351,498,509,569,402,39,203,758,29,72,57,45,629,350,54,116,316,96,752,50,38,349,67,901,65,46,342,304,285,58 }, +{ 39,9,100,30,31,127,44,88,0,276,98,131,1,385,254,442,166,49,908,116,187,190,935,201,219,284,827,299,28,224,144,829,96,228,36,274,137,141,101,21,265,716,382,48,51,258,202,23,352,22,125,836,93,856,613,128,839,158,371,264,578,395,13,55,16,180,662,244,269,252,217,567,492,110,310,35,64,77,272,83,268,410,931,203,120,326,752,260,68,318,18,489,293,574,625,160,24,33,115,86,159,924,242,373,165,438,5,450,69,102,616,213,197,918,863,321,295,717,291,82,731,177,456,317,406,7,599,737 }, +{ 22,109,49,28,159,185,95,9,105,131,272,327,254,1,67,286,36,39,35,168,86,83,55,169,505,158,224,374,420,127,584,208,98,219,312,578,31,347,535,601,0,69,708,424,44,114,103,30,160,627,346,64,141,194,187,518,175,96,239,345,616,892,10,128,164,143,896,116,137,50,12,5,2,237,228,913,533,13,23,7,21,371,320,597,688,538,751,912,352,323,251,716,165,115,634,202,258,197,498,637,37,531,180,797,874,51,253,768,48,101,890,16,189,201,613,24,752,385,14,170,285,365,406,789,244,218,46,133 }, +{ 5,1,21,2,48,14,12,32,22,36,0,17,54,10,38,3,37,86,50,45,65,28,51,72,281,23,4,6,58,114,89,46,137,13,270,99,430,11,7,208,76,202,181,61,115,403,155,9,133,18,540,64,361,221,29,323,480,285,49,128,24,95,218,401,657,421,290,745,19,576,194,55,308,754,59,453,513,920,148,880,96,341,502,52,16,475,77,67,842,40,283,859,318,197,402,494,171,141,474,244,543,757,165,775,90,541,27,33,180,57,83,227,164,217,428,328,269,108,425,569,514,207,31,143,223,263,102,503 }, +{ 141,352,269,372,351,217,854,82,752,922,318,180,954,260,317,15,349,1,202,64,120,898,717,77,67,35,864,291,69,365,137,851,437,36,22,5,247,86,373,4,457,450,244,165,189,163,140,68,13,237,901,115,128,304,23,21,341,98,125,177,51,93,32,28,961,160,96,494,110,95,10,0,719,253,661,523,326,767,83,730,275,44,386,418,40,12,817,210,19,17,102,59,45,18,7,579,498,482,615,403,144,11,6,3,203,76,822,475,49,48,31,33,293,284,268,228,187,947,899,587,583,485,474,367,342,300,245,100 }, +{ 144,613,203,406,669,418,326,137,228,96,679,494,160,816,1,817,822,36,317,859,840,83,856,876,855,217,251,219,863,831,944,202,576,365,539,93,800,5,345,530,77,128,44,957,748,141,13,839,30,23,21,778,143,341,115,473,617,332,201,64,120,190,492,456,829,531,349,367,716,258,98,48,578,116,180,924,403,187,31,515,966,692,574,442,131,86,931,719,523,450,9,224,351,291,860,752,82,28,10,244,51,371,22,276,39,102,125,730,261,50,851,269,827,335,318,871,704,916,268,110,898,69,67,498,206,177,615,805 }, +{ 15,13,23,51,202,1,21,33,5,269,180,217,2,141,308,12,120,0,352,165,7,221,115,117,48,32,6,318,197,4,153,9,260,82,196,453,134,457,3,40,386,523,730,110,14,719,77,401,31,403,450,137,326,37,102,341,16,351,8,128,752,11,475,93,18,498,317,44,365,391,64,30,98,237,72,10,49,421,22,99,901,494,509,28,24,899,17,125,39,817,569,854,116,203,59,291,177,89,961,95,36,244,629,758,105,247,90,372,241,864,349,546,670,898,96,35,144,86,65,350,373,50,139,304,61,160,922,615 }, +{ 23,115,13,51,77,0,1,4,365,2,141,341,5,33,202,217,11,10,317,9,180,102,3,7,6,15,269,22,318,197,165,64,352,12,40,494,403,21,28,31,128,308,125,32,453,59,177,18,291,16,401,475,901,14,24,752,237,93,342,137,349,457,569,44,8,17,372,30,153,98,120,95,49,210,36,498,196,117,110,509,482,421,116,247,386,873,304,37,391,39,629,244,48,139,90,203,143,134,55,86,523,450,326,468,260,275,57,961,144,719,65,35,402,216,730,221,19,253,373,46,50,734,437,678,576,96,758,551 }, +{ 531,373,728,160,943,559,717,260,93,269,332,206,533,244,352,384,261,535,964,564,120,110,10,253,295,137,365,318,141,55,143,959,180,617,36,851,911,843,197,661,203,873,282,1,752,940,341,699,968,901,82,417,326,21,144,450,22,351,317,434,64,642,498,855,824,574,202,926,473,860,800,896,164,23,28,898,8,51,13,258,15,116,35,816,567,127,187,237,776,9,951,219,601,268,115,811,165,217,201,864,131,98,406,854,77,523,251,902,922,125,48,345,44,31,16,704,96,499,86,83,489,871,367,299,177,72,817,24 }, +{ 1,31,36,170,55,86,22,44,127,5,301,64,98,164,2,116,740,105,299,12,10,242,237,125,169,338,9,0,21,143,7,512,23,28,841,128,141,13,239,51,395,180,654,208,293,685,37,144,870,923,95,6,599,83,197,115,160,456,900,352,67,498,506,253,165,638,49,3,268,492,137,965,4,479,14,393,763,269,244,185,708,202,109,341,96,365,50,762,509,933,276,48,284,951,367,505,864,751,752,546,320,601,597,35,8,569,634,312,323,217,558,201,391,824,317,401,308,901,457,32,837,159,535,93,873,54,114,38 }, +{ 13,23,51,115,32,141,77,177,202,33,64,269,90,4,102,318,128,40,59,21,217,291,137,180,352,210,95,10,48,0,22,5,196,494,15,7,237,14,117,49,18,216,197,125,342,98,31,304,11,9,165,153,403,317,247,86,752,134,28,275,349,16,24,116,365,341,2,6,76,44,453,17,12,20,30,300,221,93,39,143,139,36,1,19,3,402,326,386,312,105,457,65,244,194,8,576,498,901,308,110,99,72,372,120,401,203,50,25,241,144,61,26,208,96,678,450,160,615,421,391,127,114,57,46,37,35,27,947 }, +{ 202,260,120,450,318,51,269,77,23,351,1,326,13,128,82,137,141,21,33,15,494,102,523,817,5,403,110,165,922,64,203,197,854,125,32,180,177,291,48,153,196,93,116,12,342,98,352,373,40,4,115,7,475,144,349,36,217,90,2,453,386,16,0,285,95,44,615,237,59,49,31,457,704,6,898,210,24,58,96,86,30,719,117,70,401,216,8,275,28,22,14,402,17,247,50,39,29,18,421,99,37,20,10,391,304,61,25,752,341,730,678,9,365,859,576,473,139,45,961,692,293,661,498,317,214,132,35,76 }, +{ 899,523,102,730,33,153,196,117,719,23,475,51,817,13,15,403,453,5,4,342,77,494,40,120,21,11,450,202,421,59,457,12,805,165,139,134,26,19,17,2,1,300,386,801,961,57,32,27,7,6,3,880,197,99,361,757,670,128,125,72,58,56,48,45,37,14,318,64,49,770,247,82,576,260,884,775,635,629,509,485,456,402,391,180,177,141,95,89,84,76,70,62,61,54,53,38,29,25,758,401,269,262,583,409,365,341,331,217,52,968,967,966,965,964,963,962,960,959,958,957,956,955,954,953,952,951,950,949 }, +{ 202,494,403,475,523,817,453,450,318,402,120,576,51,13,77,102,342,33,260,128,23,326,5,899,15,4,82,615,210,269,137,351,291,59,196,141,90,730,40,386,285,214,177,21,719,389,49,11,65,61,30,22,48,153,117,457,114,12,3,98,64,44,39,31,1,922,194,216,84,349,165,95,32,28,10,0,757,197,421,474,123,38,961,887,805,690,629,551,401,283,263,207,139,125,118,113,58,848,503,115,41,880,275,24,17,422,319,104,942,854,803,801,704,671,509,416,400,393,372,334,237,227,217,186,180,151,134,96 }, +{ 2,29,52,112,66,87,70,151,135,7,226,58,74,307,186,294,172,287,549,644,334,5,14,97,46,75,45,227,331,113,145,6,25,315,155,337,222,1,61,192,32,243,163,311,64,220,38,400,43,262,171,69,21,37,624,461,20,884,122,849,631,416,26,16,48,86,27,24,42,22,19,278,138,92,353,635,36,609,232,0,128,140,10,500,324,50,17,72,665,59,12,95,76,90,409,209,35,4,396,491,614,65,18,496,415,54,721,568,592,67,632,49,141,137,80,28,89,204,659,869,60,106,426,845,448,177,207,115 }, +{ 1,349,652,5,260,291,77,39,342,102,120,23,100,888,13,269,64,340,264,51,303,698,32,2,202,21,9,296,33,98,70,385,163,638,318,137,327,87,293,58,50,128,48,195,69,774,141,75,121,7,658,850,326,96,371,284,223,931,863,759,132,126,92,71,221,678,203,82,551,961,937,615,575,563,494,105,83,67,55,36,30,27,17,771,418,115,90,31,156,914,687,393,372,12,6,3,622,144,922,898,854,731,691,478,403,367,363,352,351,333,310,286,170,146,0,950,230,93,95,49,919,954,947,865,851,765,751,719 }, +{ 1,5,2,13,4,23,7,3,15,0,6,180,51,115,141,341,11,77,269,352,21,901,217,197,365,33,12,32,177,752,317,202,318,165,40,9,102,10,291,93,8,569,31,22,349,498,120,90,44,110,401,59,37,304,326,48,17,64,372,403,196,117,16,98,14,457,210,453,125,494,28,36,386,629,247,153,475,30,308,260,134,873,116,253,373,450,18,237,244,482,203,49,128,961,275,137,144,216,509,523,421,391,143,342,24,719,105,95,55,72,76,89,39,206,437,139,468,57,579,758,730,864,546,127,221,96,734,19 }, +{ 14,7,16,46,24,2,35,70,92,58,5,38,75,18,140,60,87,171,220,54,1,155,69,307,50,21,163,48,71,32,112,0,37,12,23,278,780,502,126,9,61,52,186,172,135,45,28,64,29,13,67,227,20,107,22,115,97,513,51,10,267,72,68,270,128,230,89,314,666,330,246,141,6,232,189,36,400,177,66,108,290,150,17,309,55,430,767,334,77,474,121,262,103,243,409,223,137,83,43,113,202,592,217,461,49,95,315,571,65,181,394,357,86,281,269,774,294,236,380,852,536,845,76,90,25,102,298,4 }, +{ 226,135,87,2,29,66,74,52,294,278,222,220,75,7,172,145,331,70,324,262,97,6,25,112,138,337,140,42,1,92,461,26,69,38,500,58,357,14,380,5,287,16,46,571,232,620,808,632,267,163,32,24,35,583,853,774,43,314,609,45,171,21,298,353,19,802,95,60,151,415,448,409,64,67,192,22,65,396,85,525,37,174,307,189,872,204,20,71,50,12,330,86,622,61,48,27,36,186,394,764,113,54,335,563,114,108,309,150,950,72,155,17,10,572,122,635,544,103,96,606,28,893,246,18,49,230,550,334 }, +{ 15,33,77,117,134,153,102,4,40,196,1,217,21,11,291,13,32,5,177,202,51,115,3,23,141,2,48,317,180,349,59,137,372,128,17,269,342,210,341,96,57,64,365,120,247,12,216,82,221,139,318,110,6,260,93,90,352,898,450,165,10,36,31,197,275,523,7,37,72,494,203,244,457,56,351,579,125,111,8,237,817,326,403,0,752,98,922,961,901,160,277,22,719,730,144,44,206,14,386,899,86,45,482,437,304,373,143,89,569,9,116,453,475,308,253,300,61,105,76,27,638,164,95,49,490,29,18,94 }, +{ 217,15,317,82,351,141,260,854,13,77,23,120,51,922,115,1,21,180,110,137,365,341,352,372,269,349,898,291,36,202,64,5,32,450,318,373,177,128,752,96,304,523,93,33,48,165,326,197,102,0,10,40,901,9,4,160,498,247,90,244,661,203,22,59,482,2,12,143,86,437,386,83,457,28,473,30,67,253,961,11,35,16,221,24,125,117,391,39,7,811,494,88,37,8,69,719,559,3,817,704,237,216,76,72,61,14,873,403,18,642,31,899,210,164,45,730,717,49,179,134,139,851,475,421,251,223,100,56 }, +{ 190,373,31,30,863,908,120,88,110,127,260,442,856,318,254,326,717,935,131,836,228,93,187,44,39,385,299,100,968,274,219,203,82,166,224,197,116,661,276,77,1,217,98,269,86,450,51,371,36,128,180,829,137,372,341,160,704,141,64,435,202,49,395,144,9,23,918,410,28,22,730,778,244,242,351,55,737,523,827,177,851,625,944,382,272,530,317,115,349,352,96,613,492,291,954,32,931,744,716,125,268,258,284,253,686,48,615,515,367,247,21,33,456,165,321,795,10,567,466,225,83,839,102,578,521,919,817,752 }, +{ 23,13,308,115,401,51,165,9,391,21,629,254,125,159,773,197,743,569,49,468,217,438,656,627,341,89,39,558,221,32,317,386,509,350,28,95,530,228,457,867,109,166,406,876,679,48,669,364,219,158,365,346,168,912,810,101,170,105,72,64,143,30,347,258,967,237,148,90,688,274,131,670,272,185,749,843,33,12,10,187,55,175,141,4,734,758,242,196,190,453,261,518,224,902,747,286,482,164,37,333,241,2,439,102,0,424,420,393,706,613,338,153,67,372,351,68,40,310,120,816,634,332,840,35,320,88,86,289 }, +{ 49,5,165,125,21,173,43,623,104,193,422,102,13,7,91,18,95,22,0,192,141,64,48,197,900,693,558,105,51,23,32,232,391,387,314,98,31,116,20,779,202,81,92,319,408,241,16,126,38,77,494,33,784,643,474,11,221,28,712,138,862,663,595,403,350,879,29,510,27,633,180,140,729,397,172,44,4,858,237,61,718,501,359,71,60,58,511,448,370,356,750,490,482,347,344,316,308,177,93,37,24,498,293,115,158,145,72,65,40,830,246,207,63,85,107,841,569,468,414,352,330,326,269,203,69,14,12,2 }, +{ 4,1,2,5,13,23,77,115,15,3,0,341,7,51,6,11,33,202,102,217,12,180,10,141,9,40,21,317,318,177,365,901,197,269,32,165,352,22,59,8,64,31,291,14,494,403,93,17,401,569,752,16,453,37,342,210,110,153,275,18,475,44,125,196,90,28,308,326,386,98,120,117,116,372,457,128,349,36,95,629,49,421,237,48,139,203,260,244,450,498,137,134,304,30,24,523,144,247,373,719,216,57,391,961,86,482,143,253,39,730,509,111,55,221,817,96,615,19,65,873,351,758,99,670,437,72,50,734 }, +{ 843,295,93,120,160,206,36,943,567,373,384,332,268,137,261,141,201,260,110,10,203,531,144,55,64,617,434,197,258,699,116,244,282,318,559,564,964,269,44,352,911,717,253,860,202,180,31,326,533,1,940,143,473,417,728,661,276,351,959,187,22,341,851,535,219,871,855,28,367,127,82,131,115,450,365,86,901,752,926,317,21,51,98,48,77,165,23,105,224,96,968,662,217,13,8,902,574,489,125,817,349,800,523,515,83,704,49,811,498,164,494,39,5,393,228,32,291,251,128,9,12,898,642,576,69,67,873,839 }, +{ 192,81,43,173,426,879,7,715,172,104,712,91,636,526,232,825,568,97,560,5,95,681,448,66,49,861,32,387,370,74,20,750,33,204,22,707,330,729,718,140,61,0,6,356,397,69,21,117,422,347,135,359,38,92,102,263,348,64,174,4,414,153,888,237,138,319,76,298,40,13,885,52,2,107,86,500,25,196,784,595,77,819,241,180,134,58,207,85,29,42,31,355,65,577,72,18,28,1,343,48,392,23,10,126,89,324,37,189,344,233,314,11,933,71,14,511,16,408,115,673,46,498,45,671,125,60,858,246 }, +{ 475,453,403,51,33,102,494,23,196,13,202,153,117,77,0,318,128,757,421,22,342,730,899,817,18,805,65,15,958,450,134,4,402,24,36,59,269,40,49,210,401,1,28,697,670,64,11,114,291,48,16,576,108,386,523,141,57,177,21,20,86,165,133,95,90,120,365,317,326,260,221,180,137,31,10,115,752,99,498,457,391,361,352,70,50,32,349,113,9,6,3,864,838,770,743,373,351,338,268,242,206,125,116,109,5,283,253,84,17,961,801,791,546,372,341,237,217,214,205,194,163,143,96,72,69,68,30,14 }, +{ 141,269,260,318,352,202,120,351,349,137,854,82,1,851,922,5,180,372,291,64,32,947,21,15,77,752,494,13,48,304,33,7,51,717,23,217,373,177,128,678,450,115,237,326,317,70,898,36,403,704,312,4,160,102,86,90,968,247,95,87,196,49,275,244,197,22,165,67,96,35,288,203,817,735,125,342,144,93,16,216,163,50,44,31,83,954,2,69,597,457,241,59,153,961,221,110,98,39,30,9,8,901,661,551,523,473,442,386,367,284,210,864,117,61,12,0,800,29,24,863,859,730,719,475,453,385,316,293 }, +{ 15,1,2,13,5,4,3,0,23,7,6,115,217,12,180,197,9,51,21,11,352,961,8,10,165,77,457,341,141,317,22,14,901,33,31,117,93,120,102,40,134,752,202,365,260,16,37,269,569,110,32,44,28,318,64,18,17,48,351,719,153,386,177,82,291,36,196,98,873,450,49,125,59,128,498,403,72,326,482,30,523,401,99,854,308,203,24,453,89,137,95,391,509,111,349,86,139,730,372,247,144,55,210,116,342,494,304,475,373,148,922,90,105,237,661,35,579,899,143,221,39,19,629,275,421,758,244,468 }, +{ 166,39,274,404,101,252,190,30,333,9,310,0,100,438,656,158,265,88,68,625,329,530,553,419,344,539,254,455,793,778,21,48,759,228,49,676,1,131,960,709,557,731,520,154,23,35,382,13,124,364,604,677,105,28,272,16,286,406,212,31,51,18,613,219,224,593,840,159,167,125,36,98,65,518,669,865,118,24,44,96,213,748,264,777,115,83,175,22,86,538,957,221,127,55,327,5,10,857,694,688,137,258,795,410,806,627,95,71,577,289,128,116,578,32,50,242,69,724,187,352,562,346,616,67,33,827,846,191 }, +{ 28,0,1,9,22,65,12,83,67,55,109,50,24,5,36,49,39,96,35,238,16,18,2,21,114,124,69,37,7,218,48,23,323,103,158,160,168,54,51,128,285,154,13,68,185,137,14,194,99,141,95,169,181,418,289,420,202,433,86,30,75,480,175,10,505,105,115,32,402,281,223,389,269,208,646,503,745,64,133,46,180,657,159,800,531,547,251,956,101,429,4,318,31,149,163,6,70,346,58,532,822,533,484,757,797,165,335,252,197,735,570,352,283,361,71,98,29,118,3,535,927,766,221,217,38,799,237,308 }, +{ 15,1,2,0,23,5,51,13,7,6,9,180,12,115,3,457,197,8,719,21,730,4,120,901,141,202,14,33,165,341,453,386,82,31,260,351,365,523,308,64,37,22,44,401,450,32,40,16,93,77,110,10,98,961,403,36,102,899,28,217,11,125,352,196,48,318,137,317,475,153,18,269,49,569,116,752,99,326,421,128,24,30,221,95,39,203,117,873,144,817,629,758,177,55,86,35,494,509,898,373,59,237,105,17,65,291,96,89,498,670,482,247,253,134,468,38,143,54,244,72,90,372,160,854,127,210,391,801 }, +{ 9,39,166,49,30,28,0,158,22,131,180,100,219,35,352,190,345,228,1,752,88,498,115,141,274,317,482,109,83,128,930,251,36,217,95,365,230,55,101,160,254,165,224,159,168,23,341,13,105,31,208,187,258,927,310,197,69,137,333,901,864,364,64,185,269,86,344,505,252,202,404,68,286,873,539,372,96,669,438,51,10,164,67,5,868,346,244,637,439,2,7,21,748,323,406,418,318,16,237,44,272,50,840,706,420,265,127,98,946,735,793,768,579,169,125,530,175,148,327,857,702,678,238,116,12,48,533,335 }, +{ 61,311,80,209,45,29,5,58,2,151,454,106,17,6,667,243,255,738,52,592,70,496,287,74,483,1,833,112,549,27,207,119,26,315,32,227,378,19,64,14,138,25,84,263,75,742,186,631,113,21,136,56,48,887,62,46,7,307,469,72,76,145,50,73,66,161,128,495,148,425,814,651,491,4,34,95,869,86,38,87,3,334,240,290,383,396,845,42,53,306,12,171,65,141,163,445,135,155,11,22,54,297,90,331,179,226,655,632,37,614,361,292,663,89,400,13,36,23,10,137,294,666,99,659,250,220,59,51 }, +{ 31,44,299,22,125,599,456,685,116,28,492,393,170,242,144,268,963,301,506,1,95,558,367,36,239,55,489,0,918,109,206,23,105,9,10,434,127,51,384,13,86,286,948,479,98,64,841,5,338,2,93,12,203,498,164,7,512,168,21,143,165,49,903,940,320,401,391,137,8,37,900,417,352,141,115,128,546,253,457,959,237,14,185,197,308,169,202,395,282,421,374,911,180,740,332,293,386,6,24,312,855,538,509,752,244,473,65,928,762,160,638,617,341,269,961,867,949,730,654,403,551,499,965,523,48,719,16,453 }, +{ 187,258,926,839,574,860,93,219,406,228,871,160,531,224,137,902,201,120,116,36,203,144,669,268,318,131,442,567,816,295,434,908,851,533,843,44,384,717,202,800,559,373,489,728,141,332,855,206,261,276,1,260,269,251,617,326,180,317,385,940,110,613,217,959,55,31,450,352,968,365,115,197,64,699,679,244,98,96,83,23,10,840,341,77,492,473,899,856,831,661,143,127,190,918,752,539,21,284,418,13,943,748,662,911,564,530,48,165,291,523,704,128,345,125,822,351,169,954,86,349,367,922,776,535,177,51,824,22 }, +{ 7,107,24,60,349,232,16,51,18,75,9,291,87,23,71,269,92,126,77,141,64,177,307,150,461,35,220,14,330,37,121,0,46,279,115,947,267,132,32,12,172,55,112,309,13,10,448,278,202,21,70,2,372,210,852,678,441,304,28,5,328,217,147,54,48,786,660,259,359,318,155,90,334,181,180,140,872,108,22,163,102,31,409,314,394,352,341,281,171,68,30,622,294,236,20,423,49,626,33,760,186,58,687,415,50,39,407,128,43,771,365,197,237,1,400,929,498,888,590,366,342,270,192,135,100,76,61,675 }, +{ 15,1,2,0,5,7,13,6,3,4,115,23,180,9,12,901,51,8,341,961,365,197,10,22,141,82,33,351,14,11,77,120,31,202,457,165,217,308,134,352,28,64,102,21,269,569,110,18,260,317,40,44,16,37,36,719,93,318,237,137,99,177,450,30,125,128,386,49,752,153,55,498,17,391,98,24,111,403,32,39,196,117,401,116,373,898,730,523,326,291,453,95,854,509,304,247,86,59,372,546,482,349,143,475,899,203,244,494,139,35,48,642,253,811,144,105,210,160,758,421,148,629,221,90,164,65,127,638 }, +{ 23,51,13,202,5,12,115,21,141,165,1,32,2,318,197,15,403,180,453,7,6,3,401,269,4,0,128,308,37,10,99,457,14,11,9,120,64,22,48,341,137,386,494,475,17,77,16,719,18,217,125,31,352,523,421,59,730,90,153,365,391,237,177,317,28,40,450,260,72,221,33,8,569,93,509,961,44,19,89,102,291,98,247,326,498,76,24,49,817,899,758,30,57,629,117,61,36,110,29,139,20,210,402,82,304,196,95,45,351,752,38,285,116,203,134,349,241,96,468,54,216,901,482,576,546,25,275,342 }, +{ 2,1,77,64,141,6,3,7,0,33,102,5,10,13,23,14,269,180,15,217,165,291,352,40,117,197,9,17,115,202,237,4,128,11,22,341,12,153,349,134,196,16,51,86,318,317,177,36,19,125,137,95,342,18,752,8,28,365,31,210,457,143,139,498,59,372,21,164,57,304,82,120,45,27,38,275,24,49,110,391,386,111,93,44,244,901,260,509,56,32,41,450,98,65,99,351,30,961,55,37,403,864,569,494,29,678,48,312,453,326,546,203,20,25,54,401,79,373,402,61,253,26,46,62,719,76,475,523 }, +{ 345,531,332,269,717,260,373,317,728,752,351,180,352,533,10,365,498,82,160,251,244,36,901,143,217,341,93,55,1,141,873,864,898,35,9,564,64,851,951,22,417,137,253,28,617,169,67,69,120,559,854,197,601,482,318,546,115,768,21,96,83,661,261,876,110,372,237,164,144,535,574,203,800,206,16,86,349,31,824,699,50,48,831,902,843,860,434,968,125,221,579,959,700,679,384,926,922,295,7,51,23,291,148,418,230,202,24,943,468,896,201,5,776,77,839,44,258,954,816,13,811,940,844,637,473,868,822,308 }, +{ 92,16,189,7,77,60,388,140,33,153,196,102,35,117,230,134,150,733,314,24,14,71,21,915,246,1,108,594,97,18,32,48,66,126,69,517,121,462,64,22,98,13,399,723,65,5,291,95,67,451,674,267,232,217,653,298,2,23,38,223,104,711,83,0,197,90,36,423,149,46,125,49,167,28,68,195,20,172,120,251,86,12,289,214,508,115,386,633,236,133,10,50,350,458,191,165,330,51,107,31,260,6,43,55,271,841,450,457,273,326,796,146,509,221,603,380,237,702,9,194,4,202,199,103,52,357,29,74 }, +{ 234,142,178,202,639,5,49,450,77,70,786,416,929,48,259,455,21,0,878,147,315,481,198,329,427,318,1,125,105,120,61,113,534,846,58,344,12,132,95,518,465,325,22,31,163,557,494,13,159,32,761,767,128,590,146,37,798,195,269,711,371,291,402,265,50,342,158,362,260,39,98,221,884,687,30,64,279,137,2,167,10,771,165,35,640,626,522,403,293,310,102,23,817,347,170,28,581,99,141,236,127,150,107,51,271,87,763,623,845,212,7,18,423,194,17,523,112,345,663,33,44,376,239,199,121,391,116,75 }, +{ 81,715,192,712,681,636,104,173,750,91,370,0,74,95,718,22,7,825,879,20,49,560,43,64,422,172,397,66,138,207,784,86,500,65,28,18,707,204,414,10,577,174,319,426,32,38,312,888,348,330,4,526,289,52,355,61,511,861,347,33,237,29,102,232,448,31,21,353,835,124,5,6,2,643,36,85,97,117,153,77,306,59,134,819,140,673,671,24,40,120,165,729,568,240,135,280,324,105,125,98,344,519,233,387,194,288,196,693,474,241,214,701,44,107,583,314,260,450,847,287,69,25,872,587,343,84,9,817 }, +{ 23,13,51,15,0,10,9,115,1,453,2,961,4,77,5,457,11,40,33,401,3,7,475,197,102,14,569,165,22,59,12,6,719,32,64,21,99,730,57,17,177,18,37,8,217,386,403,196,28,308,16,31,141,139,111,341,523,120,125,180,153,117,629,391,210,36,45,237,72,899,49,95,202,128,485,247,38,482,24,48,44,509,90,27,260,450,30,19,137,317,365,41,65,291,269,89,134,342,86,304,498,421,474,82,79,148,54,277,758,318,352,46,76,56,326,468,29,670,349,494,275,351,437,300,93,50,546,873 }, +{ 64,297,5,80,445,61,95,311,250,312,472,34,237,86,45,180,738,17,151,58,22,667,869,247,2,14,73,288,194,106,53,21,128,197,352,177,32,29,186,469,833,217,52,165,287,255,3,1,495,391,396,243,176,137,90,546,125,56,161,141,136,592,48,10,209,27,0,231,76,751,62,11,437,315,119,887,115,334,179,23,84,498,651,207,77,4,864,145,13,881,389,304,6,59,49,631,372,65,16,240,164,153,112,378,549,483,584,527,316,26,862,509,409,752,51,7,875,18,933,171,269,196,457,283,241,148,114,19 }, +{ 71,16,24,18,60,35,7,92,118,154,68,149,150,14,175,28,124,55,302,97,0,273,126,108,429,140,230,43,375,2,246,109,20,628,69,336,189,9,101,66,38,298,107,65,439,22,517,346,279,345,1,467,419,21,67,169,399,49,48,46,158,847,420,10,570,185,232,251,6,653,29,252,441,314,12,566,637,191,5,535,51,52,508,423,364,533,722,36,32,398,172,74,882,674,917,168,121,95,39,23,621,83,221,159,105,50,505,356,13,701,96,37,484,694,962,173,608,604,135,267,86,104,64,826,329,103,363,236 }, +{ 195,360,156,591,687,771,163,626,70,883,146,132,586,371,407,379,354,48,846,121,293,13,296,31,761,98,18,767,49,105,385,44,279,522,668,21,1,55,23,125,5,518,271,127,590,878,340,10,608,223,33,807,167,259,77,447,116,75,60,658,102,446,265,929,115,128,35,457,221,87,786,301,284,4,95,51,165,395,39,481,698,541,781,291,178,664,372,150,252,777,557,64,30,24,16,153,120,40,558,535,281,107,465,763,638,783,663,393,101,937,9,467,58,196,217,144,61,401,212,199,117,299,941,32,22,817,260,118 }, +{ 13,77,202,318,23,51,33,269,141,64,137,32,291,0,128,102,21,494,4,10,403,217,177,342,5,196,9,349,117,22,153,134,98,95,115,59,31,120,753,18,16,40,48,28,90,372,317,237,49,450,1,86,65,244,352,475,194,11,180,165,142,100,44,125,76,12,116,93,523,275,127,121,24,20,453,365,341,61,859,401,144,82,402,210,198,178,147,88,37,17,2,351,234,105,7,6,326,197,110,30,203,143,922,854,851,427,325,259,118,107,101,72,55,704,421,260,114,36,208,329,265,38,164,864,752,717,558,498 }, +{ 192,81,172,173,7,426,97,232,861,715,43,879,66,712,104,387,330,568,825,74,526,893,91,298,885,448,636,681,140,92,49,560,6,204,135,779,22,5,95,572,707,64,125,726,750,14,370,563,314,20,324,10,362,29,174,246,713,0,718,356,107,138,25,397,85,63,18,38,71,21,441,98,58,500,230,31,501,32,319,408,69,729,189,13,663,422,24,784,359,241,858,165,16,347,263,77,357,623,673,26,888,2,12,353,900,141,23,52,126,37,33,622,278,830,197,48,233,40,443,355,44,42,45,267,87,19,61,237 }, +{ 14,2,1,67,6,24,69,66,7,29,38,108,36,65,83,124,46,52,150,86,25,484,28,302,74,22,388,18,267,398,458,103,50,273,168,97,12,5,16,0,650,135,26,19,160,75,172,189,21,10,208,32,289,278,380,547,782,95,610,140,51,602,118,37,114,451,429,357,42,71,23,324,505,194,149,420,48,143,92,87,17,13,421,115,60,54,70,35,584,96,756,462,20,238,109,185,27,164,64,45,220,244,253,594,3,154,99,138,43,283,335,701,133,309,128,403,797,49,58,732,550,202,169,646,401,423,218,847 }, +{ 202,77,33,13,102,128,51,318,23,64,494,4,141,196,59,0,115,403,40,22,269,210,177,153,137,117,32,90,10,475,291,21,342,15,11,134,352,95,18,65,49,217,86,16,180,36,197,453,165,275,57,1,402,9,194,48,24,341,719,28,105,6,123,349,317,285,523,125,120,372,216,14,450,365,20,457,2,237,7,82,576,98,3,421,12,5,260,247,110,730,116,44,31,901,752,300,8,208,30,498,351,96,93,139,678,164,46,19,401,386,308,817,615,214,50,35,961,758,859,326,160,17,503,391,293,242,221,203 }, +{ 269,318,141,291,77,202,349,217,947,304,33,177,102,851,372,352,753,260,351,120,5,137,854,752,494,275,403,32,922,22,180,475,21,317,30,10,954,31,64,44,418,365,49,28,48,450,39,23,95,341,661,373,82,11,4,36,93,13,61,128,7,6,3,2,859,51,0,197,800,576,96,59,901,69,165,40,203,144,247,1,86,898,12,76,70,160,498,437,342,237,125,9,433,330,238,103,98,67,453,312,864,367,326,253,87,58,361,221,99,90,888,717,678,579,65,774,241,115,50,45,968,951,704,615,473,277,244,134 }, +{ 6,74,138,85,66,222,226,87,42,280,25,135,26,792,500,294,75,29,220,204,70,174,337,145,235,112,278,853,461,19,52,97,1,32,122,38,808,2,256,172,64,889,69,324,380,774,620,802,609,525,7,95,353,21,571,5,65,580,746,415,22,108,140,96,14,241,24,331,63,45,544,287,67,237,267,125,262,92,394,348,103,86,163,632,426,90,194,606,165,630,550,58,53,36,233,76,91,192,357,28,27,17,141,189,416,72,582,486,81,49,114,16,622,113,78,115,10,928,444,537,872,462,46,605,83,350,335,150 }, +{ 9,0,105,39,101,18,16,68,310,24,333,272,252,127,98,518,846,938,28,371,1,916,35,158,286,737,455,31,301,265,327,100,395,616,49,30,254,7,578,55,23,827,118,512,109,44,329,21,65,890,60,320,242,116,137,709,170,51,385,125,48,761,166,14,5,2,13,71,293,131,520,627,154,159,299,557,344,836,12,553,364,93,202,96,167,88,623,128,36,124,506,64,688,212,604,924,910,931,120,284,50,144,10,419,22,165,219,32,37,95,77,110,121,326,67,403,404,203,442,197,187,6,141,676,276,260,239,33 }, +{ 2,14,1,67,46,24,108,29,83,69,38,103,52,75,133,236,309,36,114,28,50,18,398,135,6,65,458,278,7,238,74,87,160,388,124,220,732,0,267,262,66,149,532,335,283,25,412,71,16,5,96,55,547,154,70,273,602,22,302,418,429,218,516,366,646,150,336,357,86,696,175,26,189,54,12,68,118,226,35,172,324,58,582,19,486,208,9,21,594,109,531,112,45,484,140,596,251,60,95,323,17,10,32,37,194,394,64,650,48,168,163,145,42,571,97,222,23,420,505,27,181,92,389,533,606,171,361,99 }, +{ 22,1,28,105,95,49,55,67,168,159,239,65,14,320,35,0,2,36,114,7,170,109,83,12,169,347,194,50,301,69,9,24,5,424,164,185,103,133,654,46,312,420,16,127,10,175,158,64,584,21,23,51,98,143,31,86,286,13,37,289,6,336,389,48,283,505,96,124,68,38,638,512,160,208,39,141,202,18,115,189,128,395,54,623,874,338,237,713,547,44,272,308,740,137,149,371,125,251,32,30,518,345,844,535,180,3,293,928,756,916,242,29,835,165,538,708,327,323,99,737,484,221,401,218,533,374,253,154 }, +{ 66,7,172,97,232,92,314,568,298,140,192,380,135,330,74,324,387,448,357,14,526,126,38,246,204,16,426,107,572,52,2,808,712,501,359,60,69,278,29,230,32,779,441,500,35,95,550,5,21,22,893,6,43,563,392,104,267,46,24,150,362,64,726,138,189,423,173,65,825,71,356,20,462,279,0,12,802,87,86,174,560,10,81,18,49,37,226,830,633,25,48,28,90,289,58,36,67,75,707,91,508,241,312,1,85,451,220,70,443,199,214,636,50,108,83,61,54,222,121,353,262,125,280,45,888,750,237,42 }, +{ 2,29,52,50,46,14,5,1,6,26,133,114,58,218,366,70,19,108,45,27,112,17,236,137,75,38,262,25,74,13,283,516,53,487,223,151,145,208,96,48,51,412,287,163,23,401,323,285,86,65,171,61,36,220,21,480,594,42,128,80,331,73,103,754,181,115,12,739,113,3,532,4,194,54,602,635,186,99,202,409,675,745,34,308,583,307,357,309,782,396,150,57,361,7,764,165,66,735,324,64,966,457,453,90,24,386,445,11,484,32,389,141,629,59,87,244,155,956,711,670,421,22,545,832,758,334,328,573 }, +{ 6,74,1,25,29,42,75,26,66,2,70,138,52,19,85,220,103,388,38,14,226,87,135,324,67,357,108,112,235,69,238,603,222,65,550,500,96,114,145,594,83,309,133,516,27,36,150,451,218,294,262,532,711,51,58,602,280,13,163,50,380,63,137,97,802,23,122,278,458,64,5,394,86,53,606,696,792,46,891,160,461,267,732,12,7,204,582,113,308,453,24,545,889,386,331,45,21,165,398,486,391,115,32,423,401,95,337,758,632,17,208,3,853,457,256,537,335,78,630,141,307,189,641,37,646,444,670,10 }, +{ 22,28,109,49,9,105,131,95,1,36,159,39,86,185,31,98,272,169,127,55,44,219,83,254,286,208,141,128,35,327,352,160,30,180,116,0,224,202,115,239,13,535,187,137,5,96,12,374,317,269,2,244,837,312,752,67,531,21,533,253,237,197,64,217,51,143,168,158,578,251,318,505,37,258,165,285,365,10,7,23,498,201,538,371,901,320,50,424,347,678,48,69,164,776,708,601,341,597,616,345,228,170,873,238,634,6,947,3,482,93,125,144,518,242,946,913,864,716,4,735,8,627,221,420,100,688,910,323 }, +{ 203,822,77,859,326,576,23,403,39,817,137,100,102,473,494,691,411,291,33,177,704,70,523,217,447,96,367,478,18,373,160,13,363,265,1,658,615,93,966,520,310,144,30,692,800,578,342,182,180,120,90,51,254,296,40,158,682,333,9,354,921,349,273,127,121,101,83,60,59,24,731,264,115,821,364,44,268,260,752,857,810,272,196,166,153,131,118,48,16,457,156,67,506,481,450,415,230,15,0,593,968,914,212,937,829,395,365,351,242,225,224,219,194,187,146,141,132,117,110,87,82,49,36,35,22,905,901,656 }, +{ 15,33,117,77,102,134,153,4,115,196,13,40,217,11,341,1,23,5,317,3,365,2,291,21,32,12,349,569,177,59,51,342,0,6,372,139,7,37,901,210,10,180,17,90,111,629,48,275,82,120,197,244,304,141,14,253,137,57,202,247,401,638,308,457,300,9,719,961,482,437,260,277,351,579,31,44,93,216,165,269,36,125,450,72,523,453,89,38,352,94,730,854,116,752,18,817,143,98,386,64,56,96,318,16,468,55,160,148,61,54,76,22,485,110,403,99,221,922,19,474,79,127,65,105,588,27,490,128 }, +{ 15,13,23,51,0,8,197,180,1,165,9,33,82,5,4,10,12,120,2,102,386,115,260,475,7,351,141,110,77,901,326,14,21,18,11,217,22,457,40,93,898,453,373,36,450,341,16,6,125,31,64,854,352,719,401,117,32,391,28,730,3,291,177,203,24,961,523,202,59,317,403,99,49,44,210,922,615,37,237,111,801,153,128,498,342,629,196,509,30,899,569,421,304,752,48,758,817,276,258,137,269,130,393,98,365,57,116,65,338,144,55,704,134,349,105,206,661,551,308,242,38,95,494,275,86,546,318,56 }, +{ 23,51,1,13,21,141,5,180,15,2,202,120,7,0,197,165,12,9,6,4,115,48,352,269,403,386,33,260,318,93,3,32,457,110,37,137,14,450,475,16,82,128,523,453,102,31,18,326,125,421,8,77,44,221,391,64,308,730,401,341,217,153,11,117,98,719,498,196,30,351,99,40,752,494,24,237,317,39,72,28,36,10,59,203,49,373,365,17,116,817,90,899,96,509,22,569,35,901,144,961,615,342,45,50,291,177,86,89,244,661,55,134,20,54,854,65,873,801,402,160,95,247,38,350,629,127,105,29 }, +{ 15,1,0,2,5,3,7,4,6,8,13,10,341,23,9,180,51,82,120,77,31,110,197,961,115,12,22,93,116,165,569,730,202,217,365,752,457,453,719,102,125,44,351,30,403,141,352,33,11,260,523,317,291,475,28,21,450,318,64,898,401,98,899,386,203,36,210,40,177,269,16,242,638,421,326,153,349,372,18,817,494,854,117,275,342,59,111,196,14,137,629,49,134,55,39,143,70,32,37,139,95,758,244,958,498,801,87,24,922,86,670,253,144,170,208,90,105,615,373,873,391,308,247,482,216,61,393,304 }, +{ 559,661,922,564,373,141,260,533,244,332,269,317,642,143,10,93,110,295,728,752,261,876,531,351,206,843,120,345,203,352,180,811,253,384,36,197,144,434,717,901,959,365,498,55,567,1,943,9,28,535,817,22,160,873,902,341,326,911,35,82,601,860,39,30,679,489,824,704,417,926,217,251,940,699,574,137,617,854,201,816,116,282,898,115,44,372,855,258,237,473,662,509,187,169,864,499,349,284,125,31,468,224,21,669,64,318,164,546,951,395,964,839,450,8,219,170,776,638,268,851,23,131,98,482,367,299,48,730 }, +{ 15,5,1,2,23,0,13,7,6,51,3,180,12,9,115,4,165,719,197,457,21,8,14,33,901,22,37,141,82,341,120,10,730,202,16,110,32,31,386,11,317,153,117,752,260,351,453,217,401,18,352,77,308,93,961,48,28,44,134,569,365,391,49,269,102,523,128,98,196,758,137,99,498,36,40,30,450,64,403,17,318,509,24,89,95,475,629,854,72,125,221,177,86,873,326,247,96,899,817,898,39,38,421,139,116,291,482,55,494,59,105,203,210,144,65,468,29,20,237,54,349,90,148,372,244,734,35,45 }, +{ 198,234,5,0,325,77,416,202,61,32,147,49,639,20,43,21,315,22,450,113,95,455,142,427,48,318,230,207,342,18,494,13,329,904,577,725,64,178,28,10,70,58,344,470,102,403,128,819,269,263,104,137,1,319,112,246,17,23,347,291,7,65,887,798,120,12,241,4,36,858,107,237,66,76,376,817,39,422,316,72,97,33,945,105,2,51,90,141,290,884,557,253,9,518,251,671,11,173,86,31,115,27,159,158,194,3,24,312,289,362,260,259,192,859,414,426,16,14,306,523,37,126,475,712,576,310,453,98 }, +{ 447,411,264,363,676,9,664,682,821,812,759,478,897,166,100,905,354,156,1,382,98,265,404,724,39,18,691,656,668,101,520,195,23,144,223,21,865,271,438,593,639,848,31,96,13,5,51,521,125,586,48,127,604,88,909,30,252,765,393,647,360,77,33,121,167,781,367,44,621,132,93,0,410,274,286,118,203,254,120,272,105,102,293,446,116,83,32,658,68,310,371,284,777,115,364,4,834,16,948,69,333,163,24,212,190,40,340,329,551,914,117,49,110,153,70,165,146,35,296,418,10,67,842,242,883,326,697,238 }, +{ 24,71,35,14,69,149,189,108,267,18,16,68,46,2,7,67,140,388,60,38,236,83,251,1,133,118,309,230,336,52,103,29,458,278,28,65,135,175,150,50,172,92,273,55,0,154,75,443,114,9,705,124,399,66,565,398,412,602,335,366,36,101,823,596,451,74,220,345,375,302,594,330,696,516,87,238,6,891,21,218,5,12,54,262,48,96,86,22,582,357,324,97,191,64,70,232,283,723,95,10,37,160,831,732,346,246,563,380,32,25,109,486,733,167,58,314,212,226,192,13,429,99,547,23,181,126,572,394 }, +{ 35,16,7,20,14,60,18,121,24,43,107,68,109,126,199,326,92,120,77,907,2,147,23,259,265,475,403,260,553,1,13,523,132,252,12,146,71,55,33,0,5,859,191,318,817,38,6,450,82,202,848,402,9,286,51,21,329,159,494,419,351,28,465,327,102,10,158,48,682,374,576,198,178,97,32,421,272,621,269,615,590,279,128,941,22,446,167,142,70,493,4,310,604,581,19,105,640,463,453,407,153,694,64,810,150,108,90,29,534,427,212,346,285,709,638,557,793,137,101,39,37,25,291,165,118,95,58,50 }, +{ 30,131,276,187,613,442,44,228,39,31,190,839,935,224,116,856,219,88,9,406,201,908,574,100,244,127,567,137,36,98,110,144,1,93,202,863,258,141,829,260,28,284,49,120,269,295,318,716,21,827,254,661,166,160,203,373,299,55,385,0,22,662,352,944,13,82,530,77,253,86,268,23,96,717,489,64,180,51,326,274,778,918,48,351,492,5,752,197,83,206,125,128,450,115,217,860,32,242,931,578,293,213,102,924,494,851,95,10,101,836,105,272,143,291,699,371,35,434,165,843,864,367,403,159,177,418,261,68 }, +{ 98,223,1,393,812,834,421,697,48,265,593,791,51,410,100,382,293,284,21,23,125,96,541,213,656,88,801,453,31,321,230,765,730,386,221,13,689,9,264,670,639,163,217,5,909,807,116,144,44,352,137,286,551,30,770,948,39,252,699,466,438,435,201,401,127,93,842,475,203,165,921,478,180,166,949,724,367,963,831,36,128,404,455,805,141,676,854,202,2,317,318,772,840,190,32,418,251,855,105,35,333,77,83,197,102,335,49,443,576,957,719,351,539,308,69,55,82,310,115,0,120,557,752,329,403,269,686,669 }, +{ 98,616,219,127,258,51,293,512,924,395,105,421,272,276,308,201,401,284,763,116,23,301,295,453,327,567,944,31,44,843,261,13,268,860,170,770,115,125,569,900,131,386,144,935,456,55,578,365,320,143,341,662,137,109,28,403,434,242,221,36,203,670,286,141,10,326,22,629,21,943,95,9,743,685,160,384,623,506,180,49,206,949,533,719,734,558,282,299,185,159,244,64,8,0,110,964,911,169,32,4,224,551,202,475,11,187,910,617,492,901,253,841,716,1,489,269,959,940,165,699,615,374,855,332,237,713,86,371 }, +{ 253,951,352,110,811,244,206,141,180,332,854,642,282,10,143,638,417,752,559,533,498,260,55,661,911,499,717,120,351,959,535,93,564,160,873,531,137,341,269,197,922,384,373,36,365,728,261,617,901,8,44,82,116,165,31,317,164,64,203,217,509,824,115,473,968,268,144,326,704,434,295,601,86,15,345,851,864,237,943,251,450,169,1,843,201,762,13,372,22,258,308,125,898,96,98,202,127,855,940,4,33,489,21,28,187,9,40,546,117,83,569,338,964,51,5,32,479,391,276,23,102,318,482,349,219,153,128,817 }, +{ 49,254,272,159,9,158,627,131,327,39,518,688,0,578,28,286,68,105,101,347,424,374,364,344,224,22,333,616,65,219,35,24,124,154,716,890,95,577,16,371,320,109,810,538,228,168,310,793,747,127,30,419,613,829,827,187,385,239,846,170,21,1,98,18,36,71,938,406,910,252,31,55,346,175,23,289,51,190,48,258,86,185,83,912,100,623,301,166,128,149,125,67,118,96,44,137,64,242,13,737,857,160,703,671,694,455,202,442,169,60,856,10,761,924,221,709,116,706,634,114,439,677,194,479,7,570,212,395 }, +{ 105,272,22,131,327,374,286,320,109,224,219,28,239,95,98,578,9,169,127,185,538,159,187,616,55,258,371,36,634,623,64,164,49,713,86,910,716,385,168,654,170,420,424,1,10,312,737,201,35,254,293,284,51,23,143,13,44,761,141,601,160,395,933,31,928,916,276,301,924,208,116,67,346,237,137,347,535,39,194,242,0,442,836,83,762,165,584,338,638,505,892,244,251,175,345,114,740,12,391,944,837,128,158,202,197,479,261,253,908,352,912,125,531,96,308,295,144,24,21,30,7,269,268,2,751,323,228,5 }, +{ 691,478,340,724,658,914,698,1,363,744,772,156,411,447,682,909,303,284,296,264,354,919,335,98,9,21,686,664,225,223,586,410,96,367,382,897,51,13,521,33,848,765,125,144,23,32,120,759,48,69,662,260,44,31,166,40,93,77,5,4,551,668,35,269,30,82,191,167,163,318,351,87,404,371,102,88,812,438,165,70,39,385,293,116,65,64,656,364,101,622,286,948,254,271,393,321,141,75,11,18,854,842,443,433,118,100,418,274,202,421,16,774,67,326,329,905,149,922,110,37,386,201,12,197,127,49,203,907 }, +{ 16,24,71,35,18,108,140,60,92,267,68,189,7,230,191,246,149,14,118,167,388,46,69,236,458,399,674,212,273,9,133,1,126,150,21,309,330,50,101,55,2,48,103,121,67,906,602,5,0,345,723,412,232,172,271,146,565,28,516,696,702,75,38,65,451,32,796,12,37,594,633,154,278,733,375,366,302,199,124,314,36,336,398,443,823,64,695,54,135,114,29,22,95,97,462,915,52,563,582,251,705,575,891,589,175,10,83,566,572,13,220,223,23,467,218,830,163,363,394,66,562,132,107,446,641,517,49,86 }, +{ 9,127,0,395,371,98,39,31,737,49,512,385,272,44,28,293,299,101,836,761,158,252,254,16,242,1,284,159,116,18,518,35,276,310,333,24,187,51,21,599,137,30,301,22,131,68,286,23,219,327,202,125,616,48,442,935,456,578,916,5,109,166,685,55,265,7,65,100,688,128,105,201,36,890,623,96,144,13,908,329,12,2,846,64,37,95,141,344,455,318,403,124,93,492,154,32,404,14,203,269,938,627,67,258,118,224,170,244,326,110,268,165,367,763,86,60,827,221,716,160,924,553,120,419,489,662,10,83 }, +{ 16,18,60,68,35,101,252,0,118,71,9,419,28,24,109,364,604,191,126,167,55,121,329,92,7,158,265,39,212,22,346,14,925,49,107,694,185,169,621,146,439,333,628,124,199,150,942,21,159,48,154,553,706,175,108,575,279,677,10,962,20,105,100,848,917,815,446,363,907,132,5,793,271,375,43,149,23,65,13,566,520,806,286,810,95,166,593,374,302,51,1,97,634,598,404,407,12,31,2,272,493,46,467,727,463,562,589,125,326,327,310,50,967,96,682,168,37,344,140,608,203,98,38,647,821,246,362,67 }, +{ 116,268,203,692,93,206,940,31,551,417,473,959,499,44,202,523,559,8,137,120,450,728,299,494,403,22,51,576,0,326,564,817,10,260,13,23,127,1,899,9,105,421,125,615,318,492,638,98,386,918,141,393,341,269,253,21,791,5,28,2,144,64,37,244,805,762,373,180,128,401,599,55,901,242,352,30,165,197,453,642,730,475,170,873,115,49,569,36,4,859,7,143,391,498,86,48,308,922,752,39,395,719,365,951,285,6,457,456,958,12,110,402,704,384,367,95,512,949,18,24,16,515,479,221,32,948,801,282 }, +{ 225,1,744,919,21,459,914,48,691,284,330,385,141,96,98,335,622,166,5,293,51,32,120,223,948,340,260,443,203,88,286,30,831,372,50,363,351,269,82,64,934,70,658,9,190,127,403,352,13,115,418,682,411,371,87,144,393,909,521,922,776,272,102,90,69,61,839,23,15,4,774,349,327,321,268,244,202,197,100,93,551,75,848,662,523,442,478,373,206,201,180,125,76,59,45,36,33,963,538,410,274,237,163,156,821,303,264,772,724,450,382,367,958,954,899,898,865,864,859,854,765,752,730,668,664,506,494,475 }, +{ 5,1,14,2,36,50,28,0,48,281,21,67,24,54,22,83,218,12,38,18,65,181,58,45,114,133,430,32,17,46,96,10,9,103,37,160,502,61,155,99,270,55,72,7,29,513,16,51,108,487,238,23,543,745,86,3,290,6,540,89,361,115,834,13,52,202,221,433,842,488,480,137,236,76,69,328,223,64,838,514,49,532,35,666,474,128,171,283,766,536,227,124,966,308,75,19,552,95,70,208,775,141,323,27,541,822,697,425,754,765,4,285,207,148,657,109,318,412,243,263,77,592,832,663,741,186,807,576 }, +{ 450,137,704,202,326,120,260,851,318,968,494,269,457,182,403,291,351,349,77,678,141,23,310,373,817,859,576,661,523,890,60,128,82,64,795,110,854,70,515,96,5,159,285,1,961,341,13,717,9,421,922,203,180,95,244,55,379,658,577,347,105,75,49,822,687,115,363,800,557,301,160,36,937,475,411,67,59,237,18,16,354,195,864,771,615,520,424,415,344,83,68,51,28,0,805,447,386,364,268,232,107,101,90,86,7,668,481,342,271,786,735,901,897,846,751,731,719,638,621,586,518,402,296,253,239,208,197,165 }, +{ 15,4,33,77,40,13,102,11,23,5,32,21,110,117,217,51,93,153,59,1,247,134,115,141,64,202,120,2,372,180,3,10,12,31,82,291,177,116,139,125,269,196,8,317,17,22,165,137,98,351,318,36,44,128,203,197,28,0,352,48,260,349,898,144,57,403,450,437,6,719,341,72,304,111,457,523,90,365,494,9,326,49,37,730,7,210,39,86,30,14,475,95,386,453,638,961,854,373,76,401,901,237,277,752,56,342,160,817,244,758,402,308,899,143,89,206,216,569,421,615,61,535,45,18,316,127,164,579 }, +{ 23,13,51,1,15,5,2,7,0,21,180,6,115,12,141,9,165,3,197,457,202,4,14,352,719,48,33,32,16,269,37,217,341,8,11,401,120,318,308,22,386,77,31,18,128,752,40,110,260,10,28,453,730,93,137,498,901,403,391,153,82,317,365,117,44,102,17,523,961,569,64,450,509,72,99,49,134,36,758,59,98,221,24,629,89,30,351,475,196,326,125,177,494,291,90,421,237,29,899,203,35,39,247,864,817,95,372,96,349,20,116,854,65,38,54,160,19,144,57,86,244,304,873,55,342,373,350,316 }, +{ 219,258,127,276,98,944,395,201,567,284,131,293,935,860,116,662,295,224,737,105,578,576,242,403,843,371,137,551,272,456,512,421,51,31,475,716,202,697,261,44,492,187,924,616,268,9,49,326,206,254,144,299,39,385,22,115,916,805,93,699,308,203,943,159,160,761,836,763,125,23,13,0,332,453,401,386,36,120,28,770,141,21,327,615,384,393,282,958,221,318,494,367,365,143,217,128,434,64,692,940,10,244,110,617,301,911,55,964,730,320,5,966,523,86,719,623,457,96,95,1,269,317,24,670,197,859,842,286 }, +{ 16,35,60,18,7,92,20,14,118,0,68,126,71,28,154,24,107,43,158,22,2,109,49,55,677,168,185,1,97,6,121,12,21,175,10,5,159,703,150,202,169,105,191,942,95,38,494,279,37,137,23,13,9,329,230,140,628,48,69,420,32,553,124,326,167,128,403,120,747,101,74,51,108,925,31,65,66,621,246,475,29,46,36,576,344,52,419,25,575,81,104,98,375,173,570,421,346,39,265,199,86,50,671,269,424,362,58,318,54,450,44,149,125,143,817,566,615,302,61,439,260,453,170,165,493,402,67,286 }, +{ 728,531,160,93,373,574,559,717,120,860,533,295,269,260,318,926,851,434,258,36,902,137,251,332,617,141,201,261,699,206,384,940,968,843,816,345,352,244,187,839,564,55,776,661,144,959,871,116,64,219,44,473,489,268,535,203,10,567,31,911,498,143,896,110,679,450,523,224,824,800,197,164,276,601,351,406,662,202,873,752,943,317,365,169,855,228,704,282,131,180,253,326,82,954,1,669,125,854,367,901,98,217,165,417,817,341,86,442,831,8,898,642,284,13,127,876,237,21,947,864,22,83,23,748,418,115,946,868 }, +{ 28,9,0,1,22,109,49,39,83,55,12,158,67,96,69,5,35,30,65,95,168,323,159,128,185,238,21,50,36,105,2,24,160,23,194,208,103,7,68,169,137,218,141,48,37,505,16,420,51,13,202,86,251,285,18,114,269,31,115,531,124,10,289,418,101,402,797,154,433,32,99,175,64,866,735,252,180,646,272,100,286,480,503,54,333,533,244,14,344,131,352,44,318,127,98,327,6,346,217,570,254,197,237,181,657,800,535,745,374,4,335,75,317,165,281,756,143,584,757,419,532,345,956,365,223,831,403,341 }, +{ 23,13,51,5,141,21,1,165,202,2,15,32,12,197,115,180,7,0,48,269,120,9,4,6,453,386,403,318,308,401,37,457,14,33,137,352,64,3,125,128,523,16,31,93,391,260,730,719,475,341,450,237,99,18,82,110,11,10,44,77,326,421,8,217,494,40,28,90,102,59,49,17,117,36,98,30,221,899,247,317,22,365,72,153,509,24,196,351,39,498,569,629,752,89,116,134,177,203,20,373,96,961,901,372,817,241,291,45,86,57,19,61,76,304,38,758,468,65,50,160,105,144,55,402,29,244,216,25 }, +{ 5,21,13,49,23,43,32,20,48,14,7,51,97,107,61,115,38,713,22,24,1,12,70,46,652,955,142,58,16,590,0,626,197,2,640,147,64,279,581,132,350,150,121,303,81,493,340,888,841,104,36,60,87,18,95,345,92,241,296,221,509,98,349,308,622,91,202,195,638,141,125,35,408,407,156,379,269,146,66,28,31,9,786,534,17,128,903,929,518,178,29,354,4,108,105,263,522,687,247,65,259,850,126,44,237,199,54,774,535,271,260,50,347,6,446,318,159,90,10,288,137,783,360,76,223,598,55,586 }, +{ 9,28,0,39,101,30,49,35,68,1,67,166,175,83,22,158,100,252,141,55,88,69,65,109,36,23,180,217,323,115,64,269,345,103,21,124,10,317,13,251,289,238,364,498,154,352,230,5,419,131,190,31,7,51,194,48,50,346,96,128,439,159,160,105,202,24,274,143,952,95,16,168,12,646,341,254,752,86,868,365,637,596,244,127,137,570,433,318,418,114,864,208,44,37,503,404,372,2,32,344,197,333,116,532,218,77,98,187,237,164,125,723,535,443,480,169,265,901,189,310,221,420,336,873,797,403,402,285 }, +{ 260,120,450,351,854,82,269,922,5,32,13,125,33,23,202,291,165,21,197,77,137,1,141,203,457,349,318,51,4,326,177,144,180,753,730,275,386,523,817,237,116,217,373,719,403,98,93,31,372,128,241,115,12,899,704,341,304,196,102,44,40,391,48,2,342,898,954,864,801,494,284,208,110,76,59,365,352,285,207,947,758,663,615,599,558,492,482,456,408,350,263,210,201,160,64,57,49,19,18,16,14,11,10,7,6,961,317,361,37,968,752,661,498,475,453,402,323,300,293,268,244,206,154,127,105,96,95,86 }, +{ 121,16,60,363,35,18,101,20,167,68,621,259,126,191,604,407,107,132,55,364,43,146,199,252,7,14,92,419,590,147,212,71,9,326,5,463,520,21,694,917,465,24,354,446,647,109,13,271,682,0,28,22,120,203,10,97,806,821,48,169,77,279,727,362,2,346,195,581,640,932,178,589,118,115,102,125,23,202,51,439,941,907,848,411,450,33,857,427,634,165,553,154,108,180,39,494,325,185,373,329,142,475,265,818,49,342,158,967,403,260,1,523,374,612,110,962,12,534,955,37,615,376,150,793,692,562,137,638 }, +{ 23,13,51,5,1,12,2,141,15,21,115,0,202,180,7,32,197,3,165,4,6,9,401,37,269,14,308,457,352,11,318,453,341,386,120,403,10,16,33,22,8,719,18,391,40,31,77,48,217,28,137,17,99,365,523,475,569,128,730,44,93,59,260,102,498,450,90,49,509,24,30,317,110,752,421,64,247,125,72,82,237,221,177,629,98,351,961,494,153,36,89,19,117,758,901,373,196,326,57,38,39,29,20,65,546,304,134,116,95,899,817,139,372,76,35,55,316,241,291,468,482,203,144,210,45,402,873,54 }, +{ 201,144,206,443,418,332,203,435,330,96,494,326,473,372,335,269,434,268,187,403,822,817,459,1,88,498,564,384,839,382,617,367,83,264,934,559,295,276,30,717,344,521,141,831,321,274,44,202,576,31,9,686,709,190,100,921,699,812,252,55,230,137,208,450,616,457,284,180,128,93,28,859,410,966,228,265,456,352,69,523,318,244,158,795,961,747,735,662,584,531,421,855,213,168,960,225,478,959,899,716,578,577,489,466,417,291,223,116,36,35,23,13,954,952,237,800,349,285,77,744,866,772,752,346,197,166,98,32 }, +{ 44,276,31,116,201,284,662,567,131,489,144,268,295,434,918,30,9,39,98,137,187,219,299,224,935,367,49,384,385,36,93,22,28,0,244,261,141,127,492,699,203,110,254,1,202,836,160,442,253,258,105,96,64,269,393,51,128,371,125,23,716,55,86,318,326,10,908,120,206,940,855,77,143,190,827,13,21,228,352,217,100,35,159,48,165,115,272,944,373,948,260,617,829,102,197,166,578,737,83,16,332,293,95,717,450,24,523,88,180,403,109,18,177,37,164,5,899,242,959,613,730,285,291,15,7,616,911,473 }, +{ 520,664,478,604,759,264,167,1,724,777,9,647,411,682,48,806,936,363,101,223,265,410,21,68,118,252,100,907,848,447,772,98,96,781,0,212,88,905,16,821,39,925,917,23,329,909,382,18,166,942,125,191,639,221,13,24,51,676,124,932,321,897,621,628,521,5,71,154,69,31,30,815,686,293,541,49,284,271,60,593,765,55,32,812,404,435,335,807,163,562,44,834,553,35,691,65,364,763,105,83,146,28,127,274,10,158,7,190,393,64,238,213,419,67,701,421,354,33,22,254,697,149,116,272,201,115,77,144 }, +{ 70,23,87,21,60,75,163,182,267,92,291,120,115,107,146,379,195,132,446,795,937,246,126,121,98,71,48,18,7,415,931,566,232,133,51,24,125,371,821,668,535,411,401,340,318,308,221,116,77,44,40,32,11,4,281,850,293,563,537,407,273,217,202,199,140,13,5,807,197,64,591,385,33,883,386,626,687,891,823,820,770,766,754,743,670,658,633,627,615,598,551,545,541,458,394,392,372,364,361,349,342,338,333,330,301,284,283,268,264,216,210,105,102,100,95,49,35,31,866,774,597,498,335,244,238,237,160,2 }, +{ 39,9,737,127,0,310,98,827,846,395,100,31,371,916,254,265,385,105,272,101,18,30,836,1,242,166,44,512,16,557,116,252,131,578,938,931,137,93,301,28,125,68,333,24,51,404,23,120,286,616,856,35,299,21,55,13,64,202,593,716,128,96,48,326,329,219,260,158,7,110,264,5,688,456,77,203,165,36,49,373,924,761,442,118,863,639,144,518,318,284,228,88,438,60,327,363,12,450,33,190,82,10,411,731,102,65,293,109,455,95,170,908,599,623,494,115,478,187,141,2,14,22,812,685,276,403,935,197 }, +{ 107,126,279,20,43,356,362,92,7,359,16,598,683,595,246,441,60,392,939,653,399,35,199,319,125,649,97,259,414,71,0,493,121,858,230,150,140,232,191,49,48,14,702,173,575,68,95,298,422,674,18,64,98,590,147,22,612,118,28,24,672,104,819,407,345,146,77,660,695,10,953,375,423,31,945,314,293,33,640,23,115,93,158,44,167,149,132,154,21,783,5,175,81,807,508,376,105,109,59,13,40,391,779,387,237,124,589,470,55,426,4,330,727,729,51,427,169,120,796,223,91,189,446,117,153,541,463,755 }, +{ 206,417,93,959,728,559,499,473,137,120,8,564,141,244,260,253,450,31,44,10,269,352,203,318,373,143,341,202,638,116,752,180,144,951,940,326,268,110,160,851,115,922,127,98,717,642,384,125,434,64,13,36,51,282,615,704,365,661,197,498,23,901,817,30,762,489,276,692,15,873,165,22,968,86,1,854,128,386,299,105,217,811,55,0,242,421,317,351,523,367,237,911,21,170,49,77,9,349,391,28,509,479,187,177,82,201,576,285,164,898,492,401,96,456,291,569,494,800,338,33,32,5,39,372,12,261,295,293 }, +{ 259,465,147,581,534,132,590,687,146,427,199,279,941,195,955,640,786,107,178,522,798,121,929,362,150,5,198,325,626,142,35,21,878,376,407,360,167,48,271,1,423,10,70,20,13,126,77,22,125,98,493,31,163,49,212,202,0,105,43,783,33,14,156,345,883,102,612,767,28,97,660,346,470,221,23,450,60,12,771,92,191,379,120,591,2,467,318,9,32,61,4,535,128,153,117,116,562,95,953,18,115,595,159,494,17,371,44,293,818,58,165,51,7,68,354,246,234,269,87,342,845,196,260,72,6,134,481,93 }, +{ 523,403,817,202,15,899,450,120,51,730,494,421,719,128,457,453,13,23,342,102,401,318,64,196,33,475,141,180,49,5,115,269,308,197,237,221,165,99,4,77,137,95,260,0,757,576,365,316,125,65,48,615,402,326,159,11,671,389,312,289,283,247,210,203,194,148,105,22,21,386,835,12,958,749,743,697,485,351,350,341,300,177,170,158,155,151,136,133,129,127,123,122,119,114,113,112,90,89,86,84,80,72,62,61,59,58,56,45,40,34,27,26,25,19,17,3,901,752,153,134,117,961,922,968,967,966,965,964 }, +{ 141,559,244,10,661,253,564,143,365,533,752,180,55,110,901,160,922,373,341,317,93,535,352,206,260,531,36,197,332,269,82,642,282,964,498,911,717,261,728,873,943,351,137,64,417,854,120,1,811,9,115,217,898,169,28,165,164,86,384,203,22,15,83,21,959,96,617,499,318,35,31,8,482,295,951,251,345,372,902,5,473,601,144,32,546,843,800,237,13,864,44,509,109,824,67,268,450,391,308,349,23,258,77,567,851,291,202,116,949,615,125,926,954,638,551,569,457,51,326,102,59,434,817,418,958,719,629,523 }, +{ 219,258,276,127,284,98,935,860,943,843,567,201,137,295,964,131,261,116,535,662,492,44,160,253,31,224,206,203,615,268,144,332,293,692,924,918,384,244,944,326,36,128,434,395,716,737,367,456,187,55,96,403,551,318,911,617,616,385,699,533,115,940,202,365,10,836,576,966,523,959,77,28,21,93,9,120,489,141,1,260,48,371,531,217,143,282,564,125,110,180,51,704,421,299,39,23,64,363,254,393,442,242,494,417,105,822,473,317,578,269,272,697,22,855,102,805,475,13,948,341,0,373,33,285,82,49,932,574 }, +{ 422,319,0,945,207,887,32,577,693,43,95,20,904,22,344,61,104,804,707,306,426,124,5,725,7,241,153,325,316,49,715,414,113,671,115,239,263,329,173,198,180,102,18,365,117,83,24,33,376,510,942,355,324,140,134,69,21,9,232,77,76,240,237,196,189,177,160,158,36,10,835,292,341,526,334,331,168,92,48,16,330,37,25,204,64,901,595,416,392,347,858,362,244,174,164,149,97,86,60,58,39,31,30,27,790,770,222,65,2,350,40,11,4,814,13,933,424,159,819,397,697,623,610,491,403,387,315,288 }, +{ 1,22,12,5,2,0,36,21,28,49,105,86,95,37,55,9,7,48,23,32,141,83,10,51,31,96,269,202,13,128,50,4,160,239,208,318,14,168,159,6,3,170,44,67,164,54,115,285,99,35,109,98,244,180,64,137,402,65,24,127,16,352,320,18,197,69,323,30,158,11,169,301,708,678,238,312,77,717,597,39,143,165,584,194,125,17,253,217,341,735,281,114,116,218,654,181,403,237,424,103,347,317,185,177,45,401,15,776,242,29,46,72,68,418,498,433,223,33,569,901,480,531,40,657,365,800,851,8 }, +{ 15,13,2,1,5,23,3,0,4,7,6,51,115,9,10,12,197,11,14,77,22,217,269,8,165,31,961,33,457,202,32,180,21,341,40,141,120,102,177,16,569,453,28,318,317,17,260,291,44,365,93,37,117,134,110,30,352,18,403,719,153,196,49,401,59,475,901,36,326,98,64,304,386,125,498,730,349,494,450,137,48,247,95,90,523,308,752,24,128,210,111,99,82,139,372,203,873,57,76,116,237,342,72,629,351,391,482,509,19,421,39,144,373,35,55,216,89,275,437,817,20,86,105,56,899,96,38,65 }, +{ 23,202,77,51,13,120,1,141,15,326,180,269,260,137,318,102,450,5,128,21,110,217,291,165,197,352,115,317,373,2,48,12,7,0,523,33,203,386,403,9,96,64,93,342,6,351,82,32,14,457,177,16,36,817,349,752,494,37,4,475,18,210,901,854,341,285,615,59,275,90,498,50,244,160,391,453,99,308,86,10,365,401,22,719,922,8,221,24,730,28,704,402,421,11,125,83,67,237,661,3,40,898,30,153,35,961,31,196,65,253,39,576,17,44,247,20,569,300,95,49,899,116,509,473,117,143,70,372 }, +{ 15,1,0,2,51,13,23,5,7,180,9,6,12,4,115,33,197,523,21,120,3,8,14,457,901,165,351,141,450,260,202,365,341,82,16,40,730,31,37,308,719,401,77,93,44,18,10,899,64,453,403,32,137,317,386,217,36,48,110,98,475,102,326,28,22,128,269,30,11,116,569,421,352,318,817,752,24,196,961,221,99,39,49,144,153,629,55,89,17,203,373,35,117,59,801,898,291,873,177,509,86,125,494,244,95,160,482,134,498,854,72,372,96,615,349,468,38,143,758,237,90,127,391,54,342,670,734,247 }, +{ 16,24,18,92,35,71,60,7,75,126,140,108,50,132,9,21,68,121,14,246,13,0,146,23,267,1,48,128,220,107,70,230,150,32,12,199,118,5,394,181,87,137,537,46,51,133,269,189,141,54,64,22,167,10,202,156,2,330,281,820,171,77,99,291,774,217,177,349,58,65,39,37,103,69,163,28,95,67,622,318,309,633,86,236,101,360,372,6,563,36,195,304,115,38,641,314,30,149,522,232,20,591,352,354,49,545,55,98,571,271,317,526,649,407,672,278,100,640,586,45,223,114,31,678,430,237,388,328 }, +{ 180,341,901,15,1,365,5,2,0,4,197,7,6,115,3,752,10,13,569,317,873,23,165,8,55,141,9,143,44,93,31,498,217,51,638,253,509,308,352,12,22,401,244,36,116,33,21,11,206,64,144,120,951,391,629,137,16,37,86,372,269,125,734,386,134,110,351,40,18,421,417,260,77,282,117,326,268,499,102,457,164,318,482,373,237,203,170,127,95,35,661,642,82,202,177,559,468,261,201,196,160,221,105,961,32,28,854,551,169,24,349,811,762,728,564,338,14,242,717,291,109,153,139,128,403,312,437,851 }, +{ 9,0,101,175,35,28,39,68,364,67,252,65,124,289,158,336,154,166,83,55,100,114,439,103,419,69,49,194,346,1,30,189,345,570,109,88,22,168,64,251,50,16,547,36,230,96,596,133,389,868,265,238,149,86,429,24,694,21,128,218,637,399,141,703,23,646,51,826,13,671,208,677,159,706,95,137,7,797,505,610,237,12,115,756,10,404,484,131,768,190,2,333,71,164,18,799,329,323,344,967,283,202,274,420,782,930,48,105,286,140,217,702,480,143,212,54,37,962,269,14,857,185,310,532,254,535,197,118 }, +{ 64,115,180,197,237,165,391,247,217,352,386,316,304,32,90,13,21,468,95,72,317,76,498,89,365,291,141,125,22,509,349,288,179,86,546,98,372,23,341,225,437,49,116,569,645,796,923,459,18,411,308,177,48,640,4,586,490,16,5,12,591,312,241,933,117,35,65,134,482,752,672,269,164,77,156,302,71,1,264,873,410,321,118,446,360,271,102,59,24,682,674,246,953,88,149,751,51,521,92,823,447,144,864,37,7,11,695,702,166,781,698,662,647,588,458,403,401,363,283,239,216,194,170,153,57,33,28,17 }, +{ 15,13,1,2,5,23,0,4,7,3,115,6,51,217,9,12,197,10,141,21,11,961,457,180,8,341,77,33,165,22,14,32,31,317,202,120,40,37,177,260,16,102,93,269,901,365,352,28,44,247,318,18,117,110,134,17,386,569,372,719,752,59,48,30,36,498,153,403,196,49,450,291,82,72,125,137,304,128,453,391,730,401,98,873,349,373,523,139,509,24,64,326,39,90,475,111,99,308,899,116,95,89,482,203,351,210,86,922,144,437,629,55,57,421,148,494,342,854,35,76,20,179,817,758,275,105,237,216 }, +{ 132,199,146,121,407,60,493,279,640,598,534,522,195,581,590,955,360,126,259,107,150,783,362,35,5,156,271,49,167,939,18,16,147,612,941,591,467,21,883,465,423,929,0,10,191,92,22,298,441,356,446,13,70,48,7,31,626,28,77,24,32,296,178,97,212,33,354,108,608,786,623,68,345,586,727,660,71,649,687,102,163,125,9,105,115,683,55,4,463,379,75,23,87,98,14,953,293,159,50,44,818,761,376,61,246,40,64,763,713,109,1,120,117,767,359,427,95,101,20,344,58,43,607,221,575,663,142,153 }, +{ 28,1,0,22,9,24,65,55,12,5,67,36,96,83,114,50,18,16,2,14,35,103,21,109,49,48,7,218,160,54,124,69,433,37,281,238,23,10,181,51,223,133,137,39,154,745,68,58,99,480,202,13,95,86,75,128,657,115,283,532,285,46,32,163,323,822,38,141,175,547,30,389,766,108,45,168,646,64,194,429,158,169,185,155,361,505,418,70,420,800,531,105,208,502,269,430,6,101,71,866,318,765,402,289,838,398,149,31,251,221,29,484,757,543,4,3,533,308,180,503,17,403,72,754,171,118,217,89 }, +{ 93,120,957,968,459,77,30,160,352,217,473,110,102,372,202,373,613,291,137,260,190,752,704,141,317,31,318,961,269,177,44,326,203,851,330,863,450,264,321,265,228,224,728,457,304,247,88,924,116,840,800,219,144,365,197,115,28,187,717,49,39,944,559,908,829,406,934,349,213,916,244,131,64,225,36,98,82,9,730,403,342,275,210,180,109,100,86,33,938,855,55,831,521,367,719,10,661,165,922,935,258,839,918,864,669,539,351,274,196,166,83,341,15,959,822,772,617,285,897,531,410,716,686,478,871,251,901,836 }, +{ 15,0,1,13,9,23,2,5,51,7,12,6,120,4,8,10,102,197,523,901,341,180,14,260,115,82,77,141,33,165,3,16,21,18,202,59,450,22,403,457,110,817,93,475,64,351,11,453,137,37,401,898,421,128,28,40,719,31,961,217,36,48,569,365,30,125,269,308,318,373,203,317,210,32,494,98,44,116,730,352,24,86,49,326,386,117,342,854,95,629,17,153,144,55,391,177,922,899,99,291,237,143,196,39,805,134,221,96,576,670,139,752,35,253,244,661,300,50,57,127,402,65,638,349,19,498,206,111 }, +{ 206,417,244,499,141,253,44,8,137,559,269,144,93,10,31,728,116,564,203,143,268,352,717,638,120,260,160,752,951,318,384,341,180,282,373,959,434,127,110,197,202,125,851,64,36,901,642,473,498,326,332,115,165,450,201,98,873,242,922,23,22,1,217,261,276,762,55,13,489,86,661,51,317,365,15,940,898,811,82,855,854,704,9,295,0,21,372,164,351,37,968,187,569,367,911,5,576,28,617,401,237,170,615,128,776,349,7,48,105,421,479,386,16,96,95,299,291,12,258,824,864,308,817,2,30,403,49,692 }, +{ 530,840,254,228,406,829,669,613,827,96,21,48,51,31,23,960,137,224,219,1,166,131,39,98,679,438,310,190,856,116,93,403,274,221,144,127,258,846,36,202,180,938,688,120,318,765,421,385,931,317,475,13,77,816,876,105,44,187,30,115,5,625,203,125,752,442,141,32,367,308,365,284,924,778,341,842,50,28,268,333,242,217,860,160,100,22,616,539,110,88,871,159,37,345,9,165,33,839,332,299,83,574,351,576,68,404,371,10,518,326,82,748,716,863,492,578,401,265,456,102,269,656,128,926,557,531,498,494 }, +{ 15,13,197,457,0,719,23,8,730,1,82,165,120,134,9,10,5,180,2,141,7,12,115,51,260,901,4,899,351,6,341,352,33,3,22,386,450,31,202,11,117,569,523,269,128,93,18,898,44,110,758,28,752,373,365,64,30,961,77,318,498,873,21,854,111,49,102,37,16,40,99,137,125,317,14,391,55,217,24,116,326,98,403,474,36,494,482,817,509,372,35,661,277,139,922,615,38,801,203,143,291,153,475,86,453,253,629,304,148,437,402,237,48,642,954,564,864,431,244,95,242,144,39,401,72,17,196,958 }, +{ 68,0,16,24,35,9,252,71,419,18,28,101,154,158,109,124,118,60,39,55,329,364,333,49,65,677,346,22,7,185,265,149,159,175,169,191,810,302,553,100,14,92,344,108,439,671,703,793,105,168,375,286,272,327,374,21,694,706,1,10,67,126,254,634,310,95,212,289,701,2,48,167,150,455,166,627,577,30,747,420,273,538,46,69,962,36,51,23,424,13,12,64,857,429,593,96,5,570,925,86,917,199,37,125,31,50,83,826,628,912,107,239,604,98,638,114,688,221,967,131,709,137,170,336,140,20,589,128 }, +{ 51,1,23,13,15,180,120,2,5,197,202,141,0,7,21,165,260,326,33,9,4,450,12,102,6,523,48,386,115,403,269,137,457,352,14,318,16,128,110,77,3,40,196,32,18,93,217,82,317,37,341,8,817,96,31,401,453,36,752,342,351,44,719,203,901,28,475,221,421,365,64,730,24,391,153,30,308,373,10,494,22,615,59,11,39,99,98,50,116,899,498,961,177,372,291,35,854,144,509,244,17,49,922,569,86,72,247,65,349,125,160,210,482,127,117,402,20,134,55,873,38,83,898,67,275,88,285,237 }, +{ 535,253,352,564,365,110,82,180,854,341,10,533,873,55,244,898,752,901,143,642,141,559,498,36,317,951,638,160,661,115,197,282,964,137,531,165,634,351,64,308,164,217,169,206,417,811,86,499,482,28,332,125,373,509,15,569,93,546,83,728,762,965,96,22,468,601,260,911,269,717,9,349,922,109,345,800,291,734,31,23,338,170,1,372,538,629,221,943,8,120,261,44,21,318,743,494,401,128,202,13,822,579,403,251,617,959,719,770,67,864,219,185,127,117,48,286,384,299,730,475,453,402,350,69,421,391,98,39 }, +{ 31,125,44,116,338,64,242,55,164,36,299,1,237,86,456,10,22,558,506,144,498,492,13,23,762,170,268,143,206,685,93,870,180,844,599,5,903,546,197,115,2,203,509,169,479,165,141,127,0,352,253,393,752,740,8,137,12,51,28,341,391,638,7,244,9,457,160,367,873,98,128,384,951,963,282,202,918,21,269,37,6,105,569,629,434,365,120,301,4,708,318,3,417,489,923,901,719,317,308,401,386,535,217,14,15,734,95,551,634,499,208,533,332,49,758,83,824,961,730,239,776,601,523,751,948,717,864,654 }, +{ 23,13,51,4,115,202,5,217,77,141,1,11,180,2,317,0,3,7,33,269,318,15,365,341,21,9,102,10,32,6,40,12,352,177,22,197,403,64,165,128,901,59,752,31,494,137,308,291,28,8,18,349,401,16,125,90,30,14,237,498,372,93,37,98,153,49,453,17,36,873,44,475,457,39,342,196,210,24,48,326,569,386,421,247,95,203,120,509,275,116,391,961,117,244,110,143,57,304,144,216,482,719,134,139,468,86,523,55,221,450,67,76,50,253,35,105,260,65,437,373,730,72,859,629,46,692,576,111 }, +{ 39,9,166,364,68,101,158,333,0,154,562,28,404,30,65,124,252,35,175,274,344,310,190,706,49,289,570,100,168,438,694,419,346,703,826,55,16,24,439,21,88,149,254,577,429,810,109,159,962,857,671,212,455,656,22,610,95,194,1,336,747,13,125,67,36,131,530,865,48,71,625,793,10,589,23,64,841,265,484,539,86,224,558,350,69,237,420,51,835,96,272,105,406,677,165,228,83,18,31,141,114,286,557,221,347,115,137,169,722,778,7,391,98,818,816,308,840,634,709,44,185,128,960,777,143,258,669,60 }, +{ 15,341,13,33,23,77,141,4,0,351,1,260,102,51,82,9,40,349,854,11,115,217,269,137,180,202,922,5,901,22,10,117,21,365,318,197,120,352,64,12,7,153,177,59,291,32,128,2,165,196,372,36,403,317,457,28,18,8,16,304,30,14,450,31,898,37,3,752,48,134,139,494,421,6,453,401,719,90,86,569,523,110,24,55,475,210,49,44,386,17,730,95,247,244,961,143,125,308,342,817,629,98,498,93,96,76,39,275,509,326,99,285,373,57,237,35,402,160,111,253,105,391,221,116,899,72,127,661 } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9c3c24ac27..413f16e7b8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -57,6 +57,7 @@ option( LIBKTX_FEATURE_KTX2 "Enable KTX 2 support." ON ) option( LIBKTX_FEATURE_VK_UPLOAD "Enable Vulkan texture upload." ON ) option( LIBKTX_FEATURE_GL_UPLOAD "Enable OpenGL texture upload." ON ) option( LIBKTX_FEATURE_ETC_UNPACK "ETC decoding support." ON ) +option( LIBKTX_FEATURE_BCN_DECODER "BCn decoding support." ON ) # Intentionally override the BASISU options, only making them visible # when they are useful. @@ -135,6 +136,10 @@ endif() set(LIBKTX_MAIN_SRC include/ktx.h src/astc_codec.cpp + src/bcn_common.h + src/bcn_encoder.cpp + src/bcn_decoder.cpp + src/multithreading.cpp src/basis_sgd.h src/basis_transcode.cpp src/miniz_wrapper.cpp @@ -709,6 +714,26 @@ if(LIBKTX_VERSION_READ_ONLY) add_lib_dependencies(ktx_read ${ASTCENC_LIB_TARGET}) endif() + +#################################################### +# BCn Encoders/Decoders + RDO. +#################################################### + +# Force static build for bc7enc +if (LIBKTX_FEATURE_BCN_DECODER) + set(BUILD_SHARED_LIBS OFF) + add_subdirectory(${KTX_ROOT_DIR}/external/bc7enc_rdo bc7enc_rdo) + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_RESET}) + + if (LIBKTX_VERSION_FULL) + add_lib_dependencies(ktx bc7enc_rdo) + endif() + if(LIBKTX_VERSION_READ_ONLY) + add_lib_dependencies(ktx_read bc7enc_rdo) + endif() +endif() + + # Install if(LIBKTX_VERSION_FULL) diff --git a/lib/include/ktx.h b/lib/include/ktx.h index c29c3f40a4..2beec10e4f 100644 --- a/lib/include/ktx.h +++ b/lib/include/ktx.h @@ -193,7 +193,8 @@ typedef enum ktx_error_code_e { KTX_LIBRARY_NOT_LINKED, /*!< Library dependency (OpenGL or Vulkan) not linked into application. */ KTX_DECOMPRESS_LENGTH_ERROR, /*!< Decompressed byte count does not match expected byte size */ KTX_DECOMPRESS_CHECKSUM_ERROR, /*!< Checksum mismatch when decompressing */ - KTX_ERROR_MAX_ENUM = KTX_DECOMPRESS_CHECKSUM_ERROR /*!< For safety checks. */ + KTX_DECOMPRESS_FAILURE, /*!< Decompression failure */ + KTX_ERROR_MAX_ENUM = KTX_DECOMPRESS_FAILURE /*!< For safety checks. */ } ktx_error_code_e; /** * @~English @@ -1406,6 +1407,262 @@ typedef struct ktxAstcParams { */ } ktxAstcParams; +/** + * @~English + * @brief Options specifiying BC1/BC3 encoding quality levels. + */ +typedef enum ktx_pack_bc1_quality_levels_e { + KTX_PACK_BC1_QUALITY_LEVEL_FASTEST = 0U, + /*!< Fastest compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_FASTER = 2U, + /*!< Faster compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_FAST = 5U, + /*!< Fast compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_MEDIUM = 10U, + /*!< Medium compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_THOROUGH = 15U, + /*!< Slower compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_EXHAUSTIVE = 19U, + /*!< Very slow compression. */ + KTX_PACK_BC1_QUALITY_LEVEL_MAX = KTX_PACK_BC1_QUALITY_LEVEL_EXHAUSTIVE, + /*!< Maximum supported quality level. */ +} ktx_pack_bc1_quality_levels_e; +typedef ktx_uint32_t ktx_pack_bc1_quality_levels; + +/** + * @~English + * @brief Options specifiying high-level BC7 encoding quality levels. + */ +typedef enum ktx_pack_bc7_quality_levels_e { + KTX_PACK_BC7_QUALITY_LEVEL_FASTEST = 128U, + /*!< Fastest compression. Very weak particularly on alpha, mode 6 only + for RGB/RGBA. Maps to: cPackBC7FlagDefaultFastest. + */ + KTX_PACK_BC7_QUALITY_LEVEL_FASTER = 176U, + /*!< Faster compression. Mode 6 with pbits for RGB, Modes 4,5,6 for + alpha. Maps to: cPackBC7FlagDefaultFaster. + */ + KTX_PACK_BC7_QUALITY_LEVEL_FAST = 179U, + /*!< Fast compression. Maps to: cPackBC7FlagDefaultFast. */ + KTX_PACK_BC7_QUALITY_LEVEL_MEDIUM = 255U, + /*!< Medium compression. Maps to: cPackBC7FlagDefault. */ + KTX_PACK_BC7_QUALITY_LEVEL_THOROUGH = 1023U, + /*!< Slower compression. Partially analytical BC7 defaults (slower). + Maps to cPackBC7FlagDefaultPartiallyAnalytical. + */ + KTX_PACK_BC7_QUALITY_LEVEL_EXHAUSTIVE = 3967U, + /*!< Very slow compression. Non-analytical BC7 defaults (very slow). + In reality the encoder is still analytical on the mode pairs, but at + the highest level is non-analytical. + Maps to cPackBC7FlagDefaultNonAnalytical. + */ + KTX_PACK_BC7_QUALITY_LEVEL_MAX = KTX_PACK_BC7_QUALITY_LEVEL_EXHAUSTIVE, + /*!< Maximum supported quality level. */ +} ktx_pack_bc7_quality_levels_e; +typedef ktx_uint32_t ktx_pack_bc7_quality_levels; + +/** + * @~English + * @brief BCn compression kind. + */ +typedef enum ktx_bcn_compression_e { + KTX_BCN_COMPRESSION_NONE = 0, + KTX_BCN_COMPRESSION_BC1 = 1, + KTX_BCN_COMPRESSION_BC1A = 2, + KTX_BCN_COMPRESSION_BC2 = 3, + KTX_BCN_COMPRESSION_BC3 = 4, + KTX_BCN_COMPRESSION_BC4 = 5, + KTX_BCN_COMPRESSION_BC5 = 6, + KTX_BCN_COMPRESSION_BC6HU = 7, + KTX_BCN_COMPRESSION_BC6HS = 8, + KTX_BCN_COMPRESSION_BC7 = 9, +} ktx_bcn_compression_e; + +/** + * @memberof ktxTexture + * @~English + * @brief Structure for passing extended parameters to + * ktxTexture2_CompressBCnEx. + * + * If you only want default values at a minimum you must initialize the + * structure as follows (you have to explicitly the target BCn compression via + * the @e bcn field): + * @code + * ktxBCnParams params = {0}; + * params.structSize = sizeof(params); + * params.bcn = KTX_BCN_COMPRESSION_BCX; + * // If targeting BC1 or BC3: + * params.bc1CompressionQuality = KTX_PACK_BC1_QUALITY_LEVEL_XXXX; + * // If targeting BC7: + * params.bc7CompressionQuality = KTX_PACK_BC7_QUALITY_LEVEL_XXXX; + * @endcode + * + * When targeting BC1 or BC3, @e bc1CompressionQuality has to be explicitly set + * because 0 is a valid value. + * When targeting BC7, @e bc7CompressionQuality has to be explicitly set because + * 0 is a valid value. + */ +typedef struct ktxBCnParams { + ktx_uint32_t structSize; + /*!< Size of this struct. Used so library can tell which version + of struct is being passed. + */ + + ktx_uint32_t threadCount; + /*!< Number of threads used for compression (only encoding part, not + RDO). Default is 1. + */ + + ktx_bcn_compression_e bcn; + /*!< BCn format to compress to. Only options related to the provided + target BCn format are used. + + Since BC7 encoding is performed using basisu's analytical encoder + which encodes so rapidly (on average), that apart from lower VRAM + consumption (4bpp vs. 8bpp) and better GPU texture cache efficiency, + there's little need to use BC1 now. BC3 still has an advantage vs. + BC7, because it very strongly separates how RGB is encoded from the + alpha channel, in a predictable way. + */ + + ktx_bool_t normalMap; + /*!< Currently unused (added for same code structure convenience with + ASTC encoder). + */ + + /* BC1-5 params */ + + ktx_pack_bc1_quality_levels bc1CompressionQuality; + /*!< BC1/BC3 compression quality. Range is [0,19]. Lower values give + faster compression speed but potentially lower quality. Higher values + give slower compression speed but potentially better quality. There + is no default. Callers must explicitly set this value. + */ + + /* BC7 encoder params */ + + ktx_pack_bc7_quality_levels bc7CompressionQuality; + /*!< BC7 compression quality. Lower values give faster compression speed + at the expense of potentially lower quality. Higher values give + slower compression speed but potentially better quality. There is no + default. Callers must explicitly set this value. + + This maps to an OR'ed set of lower-level flags which can also be set + directly for advanced use-cases. + */ + + /* RDO params */ + + float bcnRDOQualityScalar; + /*!< RDO quality scalar (lambda). Controls rate vs. distortion tradeoff. + Lower values yield higher quality/larger LZ compressed files, higher + values yield lower quality/smaller LZ compressed files. A good range + to try is [0.25,8]. Full range is [.001,50.0]. Default is 1.0. + + The post-processor tries to minimize: + distortion * smooth_block_scale + rate * lambda + (rate is approximate LZ bits and distortion is scaled MSE multiplied + by the smooth block MSE weighting factor). Larger values push the + post-processor towards optimizing more for lower rate, and + smaller values more for distortion. + + Currently, HDR formats (i.e., BC6HU/BC6HS) are not supported. + */ + + ktx_uint32_t bcnRDODictSize; + /*!< The number of bytes the encoder can look back from each block to + find matches. The larger this value, the slower the encoder but the + higher the quality per LZ compressed bit. Range is [64,65536]. + Default is 4096. + */ + + float bcnRDOMaxSmoothBlockErrorScale; + /*!< RDO max MSE scaling factor for blocks considered to be smooth/flat. + A value of 1.0 means no smooth block error scaling which may cause + very noticeable artifacts for smooth/flat blocks (e.g., kodim23 test + image). @e bcnRDOMaxSmoothBlockStdDev is used to compute, for a given + block, the MSE scale factor in the range: 1.0 (i.e., not a smooth + block) up to this max MSE scale factor. + + As to why an MSE factor has to be applied to smooth/flat blocks, the + MSE for these blocks is too low relative to the visual impact they + have when they get distorted. The solution implemented here is to + compute the max std dev. of any component and use a linear function + of that to scale block/trial MSE. + + Range is [1,300]. Default is to automatically compute a decent + conservative smooth block MSE max scaling factor. + + If this is set to 0 (default), automatically compute a decent + conservative smooth block MSE max scaling factor. There is no single + calculation/set of settings that work perfectly on all input + textures, but the formula in the code works OK for most textures at + low-ish lambdas (For an example of a difficult texture the currently + formulas/settings doesn't handle so well, try encoding kodim03 at + lambdas 1-3). Smooth block handling is tuned so lambdas at or near 1 + look OK on textures with smooth gradients, skies, etc. + */ + + float bcnRDOMaxSmoothBlockStdDev; + /*!< RDO max smooth/flat block standard deviation. If the std dev. of a + block exceeds this value, then it won't be considered as a smooth + block (i.e., the smooth block MSE scale factor will be set to 1 for + this block). The smaller the ratio of the std dev. of this block to + this value the more the smooth block MSE scale factor approaches + @p bcnRDOMaxSmoothBlockErrorScale. + Range is [.01,65536.0]. Larger values expand the range of blocks + considered smooth. Default is 18.0. + */ + + float bcnRDOMaxAllowedRMSIncreaseRatio; + /*!< How much the RMS error of a block is allowed to increase before a + trial is rejected. 1.0=no increase allowed, 1.05=5% increase allowed, + etc. Range is [1.001, 100.0]. Default is 10.0. + */ + + ktx_bool_t bcnRDO; + /*!< Enable Rate Distortion Optimization (RDO) post-processing step on + BCn-encoded blocks to reduce entropy with Deflate/LZMA/LZHAM + optimizations. This is primarily used to reduce size on disk by + applying a further compression, mainly: Deflate, LZMA, or LZHAM. + RDO parameters are only used if this is set. Setting this might + result in significantly slower encoding time at the benefit of + potentially significantly lower bit rate (i.e., number of bits per + encoded texel). Default is false. + */ + + ktx_bool_t bcnRDONoUltrasmoothBlockHandling; + /*!< Disable the detection of extremely smooth blocks and encoding them + with a significantly higher MSE scale factor. When disabled, a + per-block mask image is computed, filtered, then an array of + per-block MSE scale factors is supplied to the ERT. The end result is + significantly less artifacts on regions containing very smooth blocks + (e.g., gradients, faded background, skies, etc.). Enabling + ultrasmooth block handling hurts rate-distortion performance. + Default is false. + + This only applies to BC1, BC3, and BC7's RGB blocks (alpha is + ignored). For other formats, this is silently ignored. + */ + + ktx_bool_t bcnRDOTryOneMatch; + /*!< If disabled, inject up to 2 matches into each block as opposed to + just one match. Enabling this results in faster but but noticeably + lower compression. Default is false. + */ + + ktx_bool_t bcnRDOSkipZeroMSEBlocks; + /*!< Skip blocks that have zero mean-squared error (MSR). Might result + in faster compression speed but potentially lower compression. + Default is false. + */ + + ktx_bool_t bcnRDONoMultithreading; + /*!< Disable RDO multithreading (for BCn, results are always + deterministic with or without multithreading). Default is false. + */ +} ktxBCnParams; + KTX_API KTX_error_code KTX_APIENTRY ktxTexture2_CompressAstcEx(ktxTexture2* This, ktxAstcParams* params); @@ -1415,6 +1672,16 @@ ktxTexture2_CompressAstc(ktxTexture2* This, ktx_uint32_t quality); KTX_API KTX_error_code KTX_APIENTRY ktxTexture2_DecodeAstc(ktxTexture2* This); +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressBCnEx(ktxTexture2* This, ktxBCnParams* params); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_DecodeBCn(ktxTexture2* This); + +KTX_API KTX_error_code KTX_APIENTRY +ktxUnpackBCn(const ktx_uint8_t* imageDataIn, ktx_uint8_t* imageDataOut, ktx_uint32_t width, + ktx_uint32_t height, ktx_bcn_compression_e bcn); + /** * @~English * @brief Options specifiying basis codec. diff --git a/lib/libktx_mainpage.md b/lib/libktx_mainpage.md index fb4ddfb392..52cf3b8706 100644 --- a/lib/libktx_mainpage.md +++ b/lib/libktx_mainpage.md @@ -416,3 +416,99 @@ modify is the `qualityLevel`. Remove the declaration and initialization of int quality = KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM; result = ktxTexture2_CompressAstc(texture, quality); ~~~~~~~~~~~~~~~~ + +## Writing a BCn-Compressed Texture + +~~~~~~~~~~~~~~~~{.c} +#include +#include // From your Vulkan SDK + +ktxTexture2* texture = NULL; // KTX2 texture handle +ktxTextureCreateInfo createInfo; +KTX_error_code result; // libktx returns success/error results as KTX_error_code +FILE* src = NULL; +ktx_size_t srcSize; // Size of uncompressed source image slice +ktxBCnParams params = {0}; // Make sure to 0-initialize this struct + +// Fill up texture creation struct. Notice that the supplied vkFormat has to +// of some uncompressed format that matches the target BCn that is set later. +// In this example, BC7 expects RGBA input. +createInfo.glInternalformat = 0; // Ignored as we'll create a KTX2 texture. +createInfo.vkFormat = VK_FORMAT_R8G8B8A8_SRGB; +createInfo.baseWidth = 1024; +createInfo.baseHeight = 1024; +createInfo.baseDepth = 1; +createInfo.numDimensions = 1; +// Note: it is not necessary to provide a full mipmap pyramid. +createInfo.numLevels = log2(createInfo.baseWidth) + 1; +createInfo.numLayers = 1; +createInfo.numFaces = 1; +createInfo.isArray = KTX_FALSE; +createInfo.generateMipmaps = KTX_FALSE; + +result = ktxTexture2_Create(&createInfo, + KTX_TEXTURE_CREATE_ALLOC_STORAGE, + &texture); +if (result != KTX_SUCCESS) { + // error handling ... +} + +src = 0; // Open the file for the baseLevel image, slice 0 and + // read it into memory. +srcSize = 0; // Query size of one image slice from file + +// Set uncompressed image data for each mip level, each array layer, and each +// face/depth slice +for (uint32_t levelIndex = 0; levelIndex < texture->numLevels; ++levelIndex) { + // Compute the width/height/depth for this MIP level + const uint32_t width = max(texture->baseWidth >> levelIndex, 1u); + const uint32_t height = max(texture->baseHeight >> levelIndex, 1u); + const uint32_t depth = max(texture->baseDepth >> levelIndex, 1u); + for (uint32_t layerIndex = 0; layerIndex < texture->numLayers; ++layerIndex) { + for (uint32_t faceIndex = 0; faceIndex < texture->numFaces; ++faceIndex) { + for (uint32_t depthSliceIndex = 0; depthSliceIndex < depth; ++depthSliceIndex) { + // Set image data. Adding face and depth slice indices is fine because both + // are mutually exclusive (if one is > 0 the other is guranteed to be 0) + result = ktxTexture_SetImageFromMemory(ktxTexture(texture), + levelIndex, layerIndex, + faceIndex + depthSliceIndex, + src, srcSize); + if (result != KTX_SUCCESS) { + // error handling ... + } + } + } + } +} + +// Now that uncompressed image data is set, compress to BCn: +params.structSize = sizeof(params); +params.bcn = KTX_BCN_COMPRESSION_BC7; +params.bc7CompressionQuality = KTX_PACK_BC7_QUALITY_LEVEL_MEDIUM; + +// There are a lot of other parameters that are only activated depending on the +// target BCn scheme and whether rate distortion optimization (RDO) is enabled. +// The discussion about RDO parameter details is too involved for this simple +// example => read member parameter description ktxBCnParams struct + +result = ktxtexture2_CompressBCnEx(texture, ¶ms); +if (result != KTX_SUCCESS) { + // error handling ... +} + +result = ktxTexture_WriteToNamedFile(ktxTexture(texture), "bc7_texture.ktx2"); +if (result != KTX_SUCCESS) { + // error handling ... +} + +// Don't forget to destroy the texture (this will clean-up all allocated +// resources) +ktxTexture_Destroy(ktxTexture(texture)); + +// You may notice that there are a `ktxTexture2` alternative for the above +// textures that take `ktxTexture2*`. These can also be used instead of the +// ones above. Both fall down to calling the same approriate function on the +// supplied KTX texture (either ktxTexture1 or ktxTexture2). This is achieved +// via virtual table pointer in ktxTexture C struct. + +~~~~~~~~~~~~~~~~ diff --git a/lib/src/astc_codec.cpp b/lib/src/astc_codec.cpp index 36ab754074..415709ca93 100644 --- a/lib/src/astc_codec.cpp +++ b/lib/src/astc_codec.cpp @@ -33,111 +33,12 @@ #include "vkformat_enum.h" #include "astc-encoder/Source/astcenc.h" +#include "multithreading.h" //************************************************************************ //* Functions common to decoder and encoder * //************************************************************************ -#if !defined(_WIN32) || defined(WIN32_HAS_PTHREADS) -#include -#else -// Provide pthreads support on windows -#define WIN32_LEAN_AND_MEAN -#include - -typedef HANDLE pthread_t; -typedef int pthread_attr_t; - -/* Public function, see header file for detailed documentation */ -static int -pthread_create(pthread_t* thread, const pthread_attr_t* attribs, - void* (*threadfunc)(void*), void* thread_arg) { - (void)attribs; -#ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wcast-function-type-mismatch" -#endif - LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)(threadfunc); -#ifdef __clang__ - #pragma clang diagnostic pop -#endif - *thread = CreateThread(nullptr, 0, func, thread_arg, 0, nullptr); - return 0; -} - -/* Public function, see header file for detailed documentation */ -static int -pthread_join(pthread_t thread, void** value) { - (void)value; - WaitForSingleObject(thread, INFINITE); - return 0; -} -#endif - -/** - * @internal - * @~English - * @brief Worker thread helper payload for launchThreads. - */ -struct LaunchDesc { - /** The native thread handle. */ - pthread_t threadHandle; - /** The total number of threads in the thread pool. */ - int threadCount; - /** The thread index in the thread pool. */ - int threadId; - /** The user thread function to execute. */ - void (*func)(int, int, void*); - /** The user thread payload. */ - void* payload; -}; - -/** - * @internal - * @~English - * @brief Helper function to translate thread entry points. - * - * Convert a (void*) thread entry to an (int, void*) thread entry, where the - * integer contains the thread ID in the thread pool. - * - * @param p The thread launch helper payload. - */ -static void* -launchThreadsHelper(void *p) { - LaunchDesc* ltd = (LaunchDesc*)p; - ltd->func(ltd->threadCount, ltd->threadId, ltd->payload); - return nullptr; -} - - -static void -launchThreads(int threadCount, void (*func)(int, int, void*), void *payload) { - // Directly execute single threaded workloads on this thread - if (threadCount <= 1) { - func(1, 0, payload); - return; - } - - // Otherwise spawn worker threads - LaunchDesc *threadDescs = new LaunchDesc[threadCount]; - for (int i = 0; i < threadCount; i++) { - threadDescs[i].threadCount = threadCount; - threadDescs[i].threadId = i; - threadDescs[i].payload = payload; - threadDescs[i].func = func; - - pthread_create(&(threadDescs[i].threadHandle), nullptr, - launchThreadsHelper, (void*)&(threadDescs[i])); - } - - // ... and then wait for them to complete - for (int i = 0; i < threadCount; i++) { - pthread_join(threadDescs[i].threadHandle, nullptr); - } - - delete[] threadDescs; -} - /** * @internal * @~English diff --git a/lib/src/bcn_common.h b/lib/src/bcn_common.h new file mode 100644 index 0000000000..f4a67333e0 --- /dev/null +++ b/lib/src/bcn_common.h @@ -0,0 +1,371 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2026 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Utilities (structs, functions, macros, etc.) common to both; BCn + * encoder and decoder. + * + * @author Walid Chtioui, independent contributor (walid.chtioui.main@gmail.com) + */ + +#ifndef _BCN_CODEC_H_ +#define _BCN_CODEC_H_ + +#include +#include +#include +#include +#include "ktx.h" +#include "vulkan/vulkan_core.h" +#include + +#define BC1_BLOCK_SIZE 8 +#define BC2_BLOCK_SIZE 16 +#define BC3_BLOCK_SIZE 16 +#define BC4_BLOCK_SIZE 8 +#define BC5_BLOCK_SIZE 16 +#define BC6H_BLOCK_SIZE 16 +#define BC7_BLOCK_SIZE 16 + +#define BC1_NCHANNELS 3 +#define BC1A_NCHANNELS 4 +#define BC2_NCHANNELS 4 +#define BC3_NCHANNELS 4 +#define BC4_NCHANNELS 1 +#define BC5_NCHANNELS 2 +#define BC6H_NCHANNELS 3 +#define BC7_NCHANNELS 4 + +/* Since this is used to pass parameters/data to thread runners, make sure all + * pointers point to heap-allocated resources (and, obviously, make sure that + * the lifetimes of spawned threads do not exceed that of these resources). */ +struct bcn_compression_workload { + uint32_t width; + uint32_t height; + uint32_t nchannels; + ktxBCnParams params; + const uint8_t* data_in; + uint8_t* data_out; +}; + +template +inline F +lerp(F a, F b, F s) { + return a + (b - a) * s; +} + +// +// Extracts/Copies a [4 x 4 x nchannels] unpacked/uncompressed block of data +// from provided pSrc to provided pDst. For each row of the source block, +// performs a memcpy to the destination block while accounting for potential +// non-multiple-of-block-size destination dimensions. +// +// Source and destination SHOULD have the same stride (i.e., nchannels). +// +// This is the inverse (in terms of source/destination) of insert_block(). +// +// In the following diagrams: +// - '.': denotes input texture/image's texels/pixels +// - '=': denotes texels/pixels that are copied from source texture/image +// - 'x': denotes texels/pixels that are filled by clamp-to-edge method +// (i.e., for each raw, repeat the last pixel). +// +// Source texture and block: (width x height x nchannels) +// +// <----------- width -----------> +// +-----------------------------+ +// | . . . . . . . . . . . . . . | <-- row: 0 +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . +-----|-----+ <- source block +// | . . . . . . . . . . . | = = | x x | +// | . . . . . . . . . . . | = = | x x | +// | . . . . . . . . . . . | = = | x x | +// | . . . . . . . . . . . | = = | x x | +// | . . . . . . . . . . . +-----|-----+ +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . . . . | <-- row: height - 1 +// +-----------------------------+ +// +// Destination block: (4 x 4 x nchannels) +// +// block_size +// +---------+ +// | = = x x | <-- row: 0 +// | = = x x | +// | = = x x | +// | = = x x | <-- row: block_size - 1 +// +---------+ +// +template +inline void +extract_block(T* dst, const T* src, uint32_t x, uint32_t y, uint32_t width, uint32_t height, + uint32_t nchannels /* stride */) { + constexpr uint32_t kBlockSize = 4; + const uint32_t src_pitch = width * nchannels; // nbr bytes per raw of src + const uint32_t dst_pitch = kBlockSize * nchannels; // nbr bytes per raw of dst + const uint32_t cols = std::min(kBlockSize, width - x); // nbr columns to copy from src + + // nbr remaining columns that were not copied from src and should be + // clamp-to-edge-x generated for dst + const uint32_t remaining_cols = kBlockSize - cols; + + // nbr remaining raws that were not copied from src and should be + // clamp-to-edge-y generated for src + const uint32_t remaining_raws = (uint32_t)std::max(0, (int)(y + kBlockSize) - (int)height); + + const T* pSrc = src + y * src_pitch + x * nchannels; + T* pDst = dst; + + for (uint32_t py = 0; py < kBlockSize && y + py < height; ++py) { + memcpy(pDst, pSrc, cols * nchannels * sizeof(T)); + // Add padding for this raw (it needed) - CLAMP_TO_EDGE_X + for (uint32_t i = 0; i < remaining_cols; ++i) { + memcpy(pDst + (cols + i) * nchannels, pSrc + (cols - 1) * nchannels, + nchannels * sizeof(T)); + } + pSrc += src_pitch; + pDst += dst_pitch; + } + + // Add padding raws (if needed) CLAMP_TO_EDGE_Y + const T* pDstLastRaw = dst + (kBlockSize - remaining_raws - 1) * dst_pitch; + for (uint32_t py = 0; py < remaining_raws; ++py) { + memcpy(pDst, pDstLastRaw, dst_pitch * sizeof(T)); + pDst += dst_pitch; + } +} + +// +// Inserts/Copies a [4 x 4 x nchannels] unpacked/uncompressed block from +// provided pSrc to provided pDst. For each row of the source block, performs a +// memcpy to the destination block while accounting for potential +// non-multiple-of-block-size destination dimensions. +// +// Source and destination SHOULD have the same stride (i.e., nchannels). +// +// This is the inverse (in terms of source/destination) of extract_block(). +// +// In the following diagrams: +// - '.': denotes untouched destination texture/image's texels/pixels. +// - '=': denotes texels/pixels that are copied from source block and written +// into destination texture. +// - 'x': denotes block texels/pixels that are discarded/ignored. +// +// Source block: (4 x 4 x nchannels) +// +// block_size +// +---------+ +// | = = x x | <-- row: 0 +// | = = x x | +// | x x x x | +// | x x x x | <-- row: block_size - 1 +// +---------+ +// +// Destination: (width x height x nchannels) +// +// <----------- width -----------> +// +-----------------------------+ +// | . . . . . . . . . . . . . . | <-- row: 0 +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . . . . | +// | . . . . . . . . . . . +-----|-----+ +// | . . . . . . . . . . . | = = | x x | +// | . . . . . . . . . . . | = = | x x | <-- row: height - 1 +// +-----------------------------+ +// | x x | x x | +// | x x | x x | +// +-----+-----+ +// +template +inline uint32_t +insert_block(T* dst, const T* src, uint32_t x, uint32_t y, uint32_t width, uint32_t height, + uint32_t nchannels /* stride */) { + constexpr uint32_t kBlockSize = 4; + const uint32_t src_pitch = kBlockSize * nchannels; // nbr bytes per raw of src + const uint32_t dst_pitch = width * nchannels; // nbr bytes per raw of dst + uint32_t nbr_written_bytes = 0; + const int cols = std::min(kBlockSize, width - x); // nbr columns to copy from src + const T* pSrc = src; + T* pDst = dst + y * dst_pitch + nchannels * x; + for (uint32_t py = 0; py < kBlockSize && y + py < height; ++py) { + const uint32_t nbr_bytes_to_write = cols * nchannels * sizeof(T); + memcpy(pDst, pSrc, nbr_bytes_to_write); + nbr_written_bytes += nbr_bytes_to_write; + pSrc += src_pitch; + pDst += dst_pitch; + } + return nbr_written_bytes; +} + +inline void +extract_rgb_from_rgba_block(uint8_t* rgb, const uint8_t* rgba) { + const uint32_t src_pitch = 4 * 4; + const uint32_t dst_pitch = 4 * 3; + [[maybe_unused]] uint32_t nbr_written_bytes_total = 0; + for (uint32_t py = 0; py < 4; ++py) { + for (uint32_t px = 0; px < 4; ++px) { + memcpy(rgb + px * 3 + py * dst_pitch, rgba + px * 4 + py * src_pitch, 3); + nbr_written_bytes_total += 3; + } + } + assert(nbr_written_bytes_total == (4 * 4 * 3)); +} + +inline void +rgb_to_rgba_block(uint8_t* rgba, const uint8_t* rgb, uint8_t alpha = 255) { + const uint32_t src_pitch = 4 * 3; /* 4 x 3 */ + const uint32_t dst_pitch = 4 * 4; /* because we add alpha */ + [[maybe_unused]] uint32_t nbr_written_bytes_total = 0; + for (uint32_t py = 0; py < 4; ++py) { + for (uint32_t px = 0; px < 4; ++px) { + uint8_t* pDst = rgba + px * 4 + py * dst_pitch; + memcpy(pDst, rgb + px * 3 + py * src_pitch, 3); + pDst[3] = alpha; + nbr_written_bytes_total += 4; + } + } + assert(nbr_written_bytes_total == (4 * 4 * 4)); +} + +inline ktx_bcn_compression_e +get_bcn_compression_kind(VkFormat vkformat, VkFormat& decompressed_vkformat, int& nchannels) { + switch (vkformat) { + /* BC1 */ + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8_UNORM; + nchannels = BC1_NCHANNELS; /* 3 */ + return KTX_BCN_COMPRESSION_BC1; + + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8_SRGB; + nchannels = BC1_NCHANNELS; /* 3 */ + return KTX_BCN_COMPRESSION_BC1; + + /* BC1A */ + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_UNORM; + nchannels = BC1A_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC1A; + + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_SRGB; + nchannels = BC1A_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC1A; + + /* BC2 */ + case VK_FORMAT_BC2_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_UNORM; + nchannels = BC2_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC2; + + case VK_FORMAT_BC2_SRGB_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_SRGB; + nchannels = BC2_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC2; + + /* BC3 */ + case VK_FORMAT_BC3_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_UNORM; + nchannels = BC3_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC3; + + case VK_FORMAT_BC3_SRGB_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_SRGB; + nchannels = BC3_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC3; + + /* BC4 */ + case VK_FORMAT_BC4_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8_UNORM; + nchannels = BC4_NCHANNELS; /* 1 */ + return KTX_BCN_COMPRESSION_BC4; + + case VK_FORMAT_BC4_SNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8_SNORM; + nchannels = BC4_NCHANNELS; /* 1 */ + return KTX_BCN_COMPRESSION_BC4; + + /* BC5 */ + case VK_FORMAT_BC5_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8_UNORM; + nchannels = BC5_NCHANNELS; /* 2 */ + return KTX_BCN_COMPRESSION_BC5; + + case VK_FORMAT_BC5_SNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8_SNORM; + nchannels = BC5_NCHANNELS; /* 2 */ + return KTX_BCN_COMPRESSION_BC5; + + /* BC6HU */ + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + decompressed_vkformat = VK_FORMAT_R16G16B16_SFLOAT; + nchannels = BC6H_NCHANNELS; /* 3 */ + return KTX_BCN_COMPRESSION_BC6HU; + + /* BC6HS */ + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + decompressed_vkformat = VK_FORMAT_R16G16B16_SFLOAT; + nchannels = BC6H_NCHANNELS; /* 3 */ + return KTX_BCN_COMPRESSION_BC6HS; + + /* BC7 */ + case VK_FORMAT_BC7_UNORM_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_UNORM; + nchannels = BC7_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC7; + + case VK_FORMAT_BC7_SRGB_BLOCK: + decompressed_vkformat = VK_FORMAT_R8G8B8A8_SRGB; + nchannels = BC7_NCHANNELS; /* 4 */ + return KTX_BCN_COMPRESSION_BC7; + + default: + decompressed_vkformat = VK_FORMAT_UNDEFINED; + nchannels = -1; + return KTX_BCN_COMPRESSION_NONE; + } +} + +inline uint32_t +get_nchannels(ktx_bcn_compression_e bcn) { + switch (bcn) { + case KTX_BCN_COMPRESSION_BC4: + return 1; + case KTX_BCN_COMPRESSION_BC5: + return 2; + case KTX_BCN_COMPRESSION_BC1: + case KTX_BCN_COMPRESSION_BC6HU: + case KTX_BCN_COMPRESSION_BC6HS: + return 3; + case KTX_BCN_COMPRESSION_BC1A: + case KTX_BCN_COMPRESSION_BC2: + case KTX_BCN_COMPRESSION_BC3: + case KTX_BCN_COMPRESSION_BC7: + return 4; + default: + return 0; + } +} + +inline void +set_default_bcn_params_fields(ktxBCnParams& params) { + if (params.threadCount == 0) params.threadCount = 1; + if (params.bcnRDOQualityScalar == 0) params.bcnRDOQualityScalar = 1.0f; + if (params.bcnRDODictSize == 0) params.bcnRDODictSize = 4096; + if (params.bcnRDOMaxSmoothBlockStdDev == 0) params.bcnRDOMaxSmoothBlockStdDev = 18.0f; + if (params.bcnRDOMaxAllowedRMSIncreaseRatio == 0) + params.bcnRDOMaxAllowedRMSIncreaseRatio = 10.0f; +} + +#endif diff --git a/lib/src/bcn_decoder.cpp b/lib/src/bcn_decoder.cpp new file mode 100644 index 0000000000..44f4f9aa4f --- /dev/null +++ b/lib/src/bcn_decoder.cpp @@ -0,0 +1,384 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2026 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Functions for decoding a BCn-compressed texture. All BCn formats are + * supported (BC1, BC2, BC3, BC4, BC5, BC6HU, BC6HS, and BC7). + * + * @author Walid Chtioui, independent contributor (walid.chtioui.main@gmail.com) + */ + +#include "bcn_common.h" + +#include "encoder/basisu_gpu_texture.h" /* for BC6HU/BC6HS decoders */ +#include "rgbcx.h" +#include "transcoder/basisu_transcoder_internal.h" +#include "vkformat_enum.h" /* for VkFormat enum */ +#include "ktxint.h" +#include "texture2.h" + +#define DECLARE_PRIVATE_EX(n, t2) ktxTexture2_private& n = *(t2->_private) +#define DECLARE_PROTECTED_EX(n, t2) ktxTexture_protected& n = *(t2->_protected) + +static inline bool +unpack_block_bc1(const void* pBlock, void* pPixels, rgbcx::bc1_approx_mode bc1_approx_mode) { + const bool allow_3color_mode = true; // hardcoded + const bool use_3color_mode_for_black = false; // hardcoded + bool used_3color = rgbcx::unpack_bc1(pBlock, pPixels, true, bc1_approx_mode); + // This check is copied from the original code at: rdo_bc_encoder.h + if (used_3color) { + if (!allow_3color_mode) return false; + if (!use_3color_mode_for_black) { + auto pBC1_block = static_cast(pBlock); + for (uint32_t y = 0; y < 4; ++y) + for (uint32_t x = 0; x < 4; ++x) + if (pBC1_block->get_selector(x, y) == 3) return false; + } + } + return true; +}; + +/** + * @ingroup reader + * @brief Decodes a ktx2 texture object, if it is BCn encoded. All BCn + * formats are supported (BC1, BC2, BC3, BC4, BC5, BC6HU, BC6HS, or BC7). + * + * The decompressed format is determined from corresponding BCn format. + * - For BC1: + * VK_FORMAT_R8G8B8_[UNORM|SRGB] + * - For BC2, BC3, and BC7: + * VK_FORMAT_R8G8B8A8_[UNORM|SRGB] + * - For BC4: + * VK_FORMAT_R8_[UNORM|SNORM] + * - For BC5: + * VK_FORMAT_R8G8_[UNORM|SNORM] + * - For BC6HU, BC6HS: + * VK_FORMAT_R16G16B16_SFLOAT + * + * UNORM vs. SRGB is determined depending on the original color model. + * + * The images are decompressed from BCn block-compressed format. The + * decompressed images replace the original images and the texture's + * fields including the DFD are modified to reflect the new state. + * + * Such textures can be directly uploaded to the GPU as raw + * (decompressed) formats. + * + * Decoding into non-multiple-of-4 texture dimensions is also supported + * (decoded blocks that fall out of the texture's dimensions are simply + * discarded). + * + * @param [in] This pointer to the ktxTexture2 object of interest. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_DATA_ERROR + * The texture's supercompression scheme is invalid. + * @exception KTX_INVALID_OPERATION + * The texture is not compressed (i.e., + * @c This->isCompressed is @c false). + * @exception KTX_INVALID_OPERATION + * The texture's images are not in BCn format (i.e., either + * color model is not set to BCn or This->vkFormat does not + * correspond to the set BCn color model). + * @exception KTX_INVALID_OPERATION + * The texture does not contain any data (i.e., + * @c This->pData is @c NULL and there is no pending data + * load). + * @exception KTX_INVALID_OPERATION + * Decoder/Unpacker returned an error exit code or a + * non-success return flag. Only occurs for BC1, BC2, BC3, + * and BC7 (BC2 and BC3 are based on BC1). + * @exception KTX_OUT_OF_MEMORY + * Not enough memory to carry out decoding. + * @exception KTX_UNSUPPORTED_FEATURE + * Unsupported supercompression scheme. + */ +extern "C" KTX_error_code +ktxTexture2_DecodeBCn(ktxTexture2* This) { + uint32_t* BDB = This->pDfd + 1; + uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID); + int nchannels; + VkFormat decompressedVkFormat; + KTX_error_code result; + + if (This->supercompressionScheme == KTX_SS_BASIS_LZ || + This->supercompressionScheme == KTX_SS_UASTC_HDR_6x6_INTERMEDIATE) { + return KTX_FILE_DATA_ERROR; // Not a valid file. + } + // Safety check. + if (This->supercompressionScheme > KTX_SS_END_RANGE) { + return KTX_UNSUPPORTED_FEATURE; // Unsupported scheme. + } + + if (!This->isCompressed) return KTX_INVALID_OPERATION; + + ktx_bcn_compression_e bcn = get_bcn_compression_kind(static_cast(This->vkFormat), + decompressedVkFormat, nchannels); + + if ((bcn == KTX_BCN_COMPRESSION_BC1 || bcn == KTX_BCN_COMPRESSION_BC1A) && + !(channelId == KHR_DF_CHANNEL_BC1A_COLOR || channelId == KHR_DF_CHANNEL_BC1A_ALPHA)) + return KTX_FILE_DATA_ERROR; + + if (This->pData == NULL) { + if (ktxTexture_isActiveStream((ktxTexture*)This)) { + // Load pending. Complete it. + result = ktxTexture2_LoadImageData(This, NULL, 0); + if (result != KTX_SUCCESS) return result; + } else { + return KTX_INVALID_OPERATION; // No data to decode. + } + } + + DECLARE_PRIVATE_EX(priv, This); + + // Create a prototype texture to use for calculating sizes in the target + // format and, as useful side effects, provide us with a properly sized + // data allocation and the DFD for the target format. + ktxTextureCreateInfo createInfo; + createInfo.glInternalformat = 0; + createInfo.vkFormat = decompressedVkFormat; + createInfo.baseWidth = This->baseWidth; + createInfo.baseHeight = This->baseHeight; + createInfo.baseDepth = This->baseDepth; + createInfo.generateMipmaps = This->generateMipmaps; + createInfo.isArray = This->isArray; + createInfo.numDimensions = This->numDimensions; + createInfo.numFaces = This->numFaces; + createInfo.numLayers = This->numLayers; + createInfo.numLevels = This->numLevels; + createInfo.pDfd = nullptr; + + ktxTexture2* prototype = nullptr; + result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &prototype); + + if (result != KTX_SUCCESS) { + assert(result == KTX_OUT_OF_MEMORY); // The only run time error + return result; + } + + for (uint32_t levelIndex = 0; levelIndex < This->numLevels; ++levelIndex) { + const uint32_t width = std::max(This->baseWidth >> levelIndex, 1u); + const uint32_t height = std::max(This->baseHeight >> levelIndex, 1u); + const uint32_t depth = std::max(This->baseDepth >> levelIndex, 1u); + + for (uint32_t layerIndex = 0; layerIndex < This->numLayers; ++layerIndex) { + for (uint32_t faceIndex = 0; faceIndex < This->numFaces; ++faceIndex) { + for (uint32_t depthSliceIndex = 0; depthSliceIndex < depth; ++depthSliceIndex) { + ktx_size_t image_offset_in; + ktx_size_t image_offset_out; + ktxTexture2_GetImageOffset(This, levelIndex, layerIndex, + faceIndex + depthSliceIndex, &image_offset_in); + ktxTexture2_GetImageOffset(prototype, levelIndex, layerIndex, + faceIndex + depthSliceIndex, &image_offset_out); + const auto* image_data_in = This->pData + image_offset_in; + auto* image_data_out = prototype->pData + image_offset_out; + auto res = ktxUnpackBCn(image_data_in, image_data_out, width, height, bcn); + if (res != KTX_SUCCESS) { + ktxTexture2_Destroy(prototype); + return res; + } + } // depth slices + } // faces + } // layers + } + + if (result == KTX_SUCCESS) { + // Fix up the current texture + DECLARE_PROTECTED_EX(thisPrtctd, This); + DECLARE_PRIVATE_EX(protoPriv, prototype); + DECLARE_PROTECTED_EX(protoPrtctd, prototype); + memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize, sizeof(ktxFormatSize)); + This->vkFormat = decompressedVkFormat; + This->isCompressed = prototype->isCompressed; + This->supercompressionScheme = KTX_SS_NONE; + priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment; + + // Copy typesize otherwise `ktx info` and `ktx validate` fails and + // complains that block-compressed types should have a typesize of 1. + This->_protected->_typeSize = prototype->_protected->_typeSize; + + // Copy the levelIndex from the prototype to This. + memcpy(priv._levelIndex, protoPriv._levelIndex, + This->numLevels * sizeof(ktxLevelIndexEntry)); + + // Move the DFD and data from the prototype to This. + free(This->pDfd); + This->pDfd = prototype->pDfd; + prototype->pDfd = 0; + free(This->pData); + This->pData = prototype->pData; + This->dataSize = prototype->dataSize; + prototype->pData = 0; + prototype->dataSize = 0; + + // Free SGD data + This->_private->_sgdByteLength = 0; + if (This->_private->_supercompressionGlobalData) { + free(This->_private->_supercompressionGlobalData); + This->_private->_supercompressionGlobalData = NULL; + } + } + ktxTexture2_Destroy(prototype); + return result; +} + +/** + * @ingroup reader + * @brief Decodes a provided BCn-encoded image. All BCn formats are supported + * (BC1, BC2, BC3, BC4, BC5, BC6HU, BC6HS, or BC7). + * + * Decoding into non-multiple-of-4 texture dimensions is also supported + * (decoded blocks that fall out of the image's dimensions are simply + * discarded). + * + * @param [in] src_blocks pointer to the BCn-encoded blocks. + * @param [in] dst pointer to where to write the decoded image. + * Should be able to hold the size of the + * corresponding decompressed vkFormat. + * @param [in] width current image's width. + * @param [in] height current image's height. + * @param [in] bcn which BCn compression kind the provided image is + * encoded in. + * @param [in] bc1_approx_mode BC1 approximation mode that is passed to BC1/BC3 + * block unpackers. + * + * @return KTX_SUCCESS on success, other KTX_* enum values + * on error. + * + * @exception KTX_DECOMPRESS_FAILURE + * Decoder/Unpacker returned an error exit code or a + * non-success return flag. Only occurs for BC1, BC2, + * BC3, and BC7 (BC2 and BC3 are based on BC1). + * @exception KTX_INVALID_OPERATION + * @p bcn is set to KTX_BCN_COMPRESSION_NONE. + */ +extern "C" KTX_error_code +ktxUnpackBCn(const ktx_uint8_t* src_blocks, ktx_uint8_t* dst, ktx_uint32_t width, + ktx_uint32_t height, ktx_bcn_compression_e bcn) { + // Create intermediate storage to store decoded blocks. Not all blocks + // necessarily decode to 4x4x4 but this is enough to hold all possible + // combinations (at least for LDR - i.e., not for BC6H formats). + const uint32_t rgba_pitch = 4 * 4; + uint8_t rgba[4 * rgba_pitch]; /* 64 bytes */ + + const uint32_t rgbh_pitch = 4 * 3; + uint16_t rgbh[4 * 4 * rgbh_pitch]; + + uint8_t rgb[4 * 4 * 3]; /* only for BC1 */ + + const uint32_t nchannels = get_nchannels(bcn); + + // [[maybe_unused]] size_t nbr_written_bytes_total = 0; + + if (bcn == KTX_BCN_COMPRESSION_NONE) return KTX_INVALID_OPERATION; + + if (bcn == KTX_BCN_COMPRESSION_BC1 || bcn == KTX_BCN_COMPRESSION_BC1A || + bcn == KTX_BCN_COMPRESSION_BC2 || bcn == KTX_BCN_COMPRESSION_BC3 || + bcn == KTX_BCN_COMPRESSION_BC4 || bcn == KTX_BCN_COMPRESSION_BC5) + rgbcx::init(rgbcx::bc1_approx_mode::cBC1Ideal); + + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + bool rv = true; + switch (bcn) { + case KTX_BCN_COMPRESSION_BC1: + case KTX_BCN_COMPRESSION_BC1A: + // BC1A: 8 bytes -> 4 x 4 x 4 = 64 bytes (alpha 1-bit encoded) + rv = unpack_block_bc1(src_blocks, rgba, rgbcx::bc1_approx_mode::cBC1Ideal); + src_blocks += BC1_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC2: + // BC2: 16 bytes -> 4 x 4 x 4 = 64 bytes + rv = rgbcx::unpack_bc2(src_blocks, rgba, rgbcx::bc1_approx_mode::cBC1Ideal); + src_blocks += BC2_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC3: + // BC3: 16 bytes -> 4 x 4 x 4 = 64 bytes + // Return code is not used because if this returns false => this means that 3 color + // punchthrough has been used which is (apparently) not supported on all GPUs. + // Problem is that when using RDO, this always returns false so I decided to just + // suppress it for the moment. + /* rv = */ rgbcx::unpack_bc3(src_blocks, rgba, rgbcx::bc1_approx_mode::cBC1Ideal); + src_blocks += BC3_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC4: + // BC4: 8 bytes -> 4 x 4 x 1 = 16 bytes + /* always succeeds */ rgbcx::unpack_bc4(src_blocks, rgba, + /* stride */ BC4_NCHANNELS); + src_blocks += BC4_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC5: + // BC5: 16 bytes -> 4 x 4 x 2 = 32 bytes + /* always succeeds */ rgbcx::unpack_bc5(src_blocks, rgba, 0, 1, + /* stride */ BC5_NCHANNELS); + src_blocks += BC5_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC6HU: + // BC6HU: 16 bytes -> 4 x 4 x 3 x 2 = 96 bytes + rv = basisu::unpack_bc6h(src_blocks, rgbh, false, rgbh_pitch); + src_blocks += BC6H_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC6HS: + // BC6HS: 16 bytes -> 4 x 4 x 3 x 2 = 96 bytes + rv = basisu::unpack_bc6h(src_blocks, rgbh, true, rgbh_pitch); + src_blocks += BC6H_BLOCK_SIZE; + break; + + case KTX_BCN_COMPRESSION_BC7: + // BC7: 16 bytes -> 4 x 4 x 4 = 64 bytes + rv = basist::bc7u::unpack_bc7(src_blocks, + reinterpret_cast(rgba)); + src_blocks += BC7_BLOCK_SIZE; + break; + + default: // should never occur + assert(false); + rv = false; + break; + } + + // If any of the decoders/unpackers returned false + // => something went wrong + if (!rv) { + return KTX_DECOMPRESS_FAILURE; // decoder failure + } + + // size_t nbr_written_bytes = 0; + + // copy the decoded block into the actual texture image + if (bcn == KTX_BCN_COMPRESSION_BC6HU || bcn == KTX_BCN_COMPRESSION_BC6HS) { + /* nbr_written_bytes = */ insert_block(reinterpret_cast(dst), rgbh, x, y, + width, height, nchannels); + } else if (bcn == KTX_BCN_COMPRESSION_BC1) { + extract_rgb_from_rgba_block(rgb, rgba); + /* nbr_written_bytes = */ insert_block(dst, rgb, x, y, width, height, nchannels); + } else { + /* nbr_written_bytes = */ + insert_block(dst, rgba, x, y, width, height, nchannels); + } + // nbr_written_bytes_total += nbr_written_bytes; + } // x blocks + } // y blocks + + // [[maybe_unused]] size_t expected_nbr_written_bytes_total = + // ktxTexture2_GetImageSize(prototype, levelIndex); + // assert(nbr_written_bytes_total == expected_nbr_written_bytes_total); + + return KTX_SUCCESS; +} diff --git a/lib/src/bcn_encoder.cpp b/lib/src/bcn_encoder.cpp new file mode 100644 index 0000000000..41a895b938 --- /dev/null +++ b/lib/src/bcn_encoder.cpp @@ -0,0 +1,1840 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab textwidth=70: */ + +/* + * Copyright 2026 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @internal + * @file + * @~English + * + * @brief Functions for encoding an uncompressed texture to a BCn format with an + * optional RDO post-processing step to significantly (circa 50% or more) + * reduce bit rate when supercompressed with Deflate (Zlib or ZSTD). + * Currently supported BCn formats are: BC1, BC3, BC4, BC5, BC6HU*, and + * BC7. + * + * *: support for BC6HS is not yet implemented because basisu's encoder + * currently fails when given signed half float values. + * + * BC2 (RGB+A) encoder is not implemented because it's rarely used in + * practice due to poor quality alpha encoding. BC3 is almost always used + * instead. + * + * @author Walid Chtioui, independent contributor (walid.chtioui.main@gmail.com) + */ + +#include "ktx.h" + +#if KTX_FEATURE_WRITE + +#include "bcn_common.h" +#include "rgbcx.h" + +#include +#include "ert.h" /* for RDO */ +#include "transcoder/basisu_transcoder.h" /* for BC7 encoder */ +#include "transcoder/basisu_astc_hdr_core.h" /* for BC6H encoder */ +#include "vkformat_enum.h" /* for VkFormat enum */ +#include "ktxint.h" +#include "texture2.h" +#include "multithreading.h" +#include + +#define DEBUG_PRINT_STATS 0 /* set to print RDO stats */ +#if DEBUG_PRINT_STATS +#include "chrono" +#include +#include +#include +#endif + +/* + * set to write utrasmooth blocks mask PNG. You need to add: + * ${KTX_ROOT_DIR}/external/lodepng/lodepng.h + * ${KTX_ROOT_DIR}/external/lodepng/lodepng.cpp + * to libktx's CMakeLists' LIBKTX_MAIN_SRC. + */ +#define DEBUG_RDO_ULTRASMOOTH 0 +#if DEBUG_RDO_ULTRASMOOTH +#include "lodepng/lodepng.h" +#endif + +#define DECLARE_PRIVATE_EX(n, t2) ktxTexture2_private& n = *(t2->_private) +#define DECLARE_PROTECTED_EX(n, t2) ktxTexture_protected& n = *(t2->_protected) + +//************************************************************************ +//* RDO Structs + Functions * +//************************************************************************ + +struct rdo_params { + ert::reduce_entropy_params ert_p; + /* Whether to automatically compute the maximum MSE scale factor for + * smooth/flat blocks. */ + ktx_bool_t auto_smooth_block_max_mse_scale; + ktx_bool_t ultra_smooth_block_mse_handling; +}; + +/* Since this is used to pass parameters/data to thread runners, make sure all + * pointers point to heap-allocated resources (and, obviously, make sure that + * the lifetimes of spawned threads do not exceed that of these resources). */ +struct rdo_bc1_workload { + uint32_t m_width; + uint32_t m_height; + /* [in,out] packed/encoded image blocks */ + uint8_t* m_packed_img; + /* [in,out] BC1 RGBX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_rgbx; + /* [in] BC1 RGB blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_rgb; + /* [out] BC1 total number smooth RGB blocks */ + std::atomic_int32_t m_total_smooth_blocks_rgb; + /* [out] BC1 total number second matches for RGB blocks */ + std::atomic_int32_t m_total_second_matches_rgb; + /* [out] BC1 total number total number modified packed/encoded RGB blocks */ + std::atomic_int32_t m_total_modified_rgb; + /* [out] false if any thread fails */ + bool m_success; + /* [in] RGB ultrasmooth blocks (a positive entry means that the block is + * considered ultrasmooth) */ + const float* m_block_rgb_mse_scales; + + rdo_bc1_workload() = delete; + rdo_bc1_workload(uint32_t width, uint32_t height, uint8_t* packed_img, + const ert::color_rgba* unpacked_img_rgbx, ert::reduce_entropy_params ert_p_rgb, + const float* block_rgb_mse_scales = nullptr) + : m_width{width}, + m_height{height}, + m_packed_img{packed_img}, + m_unpacked_img_rgbx{unpacked_img_rgbx}, + m_ert_p_rgb{ert_p_rgb}, + m_block_rgb_mse_scales{block_rgb_mse_scales} { + assert(m_packed_img != nullptr); + assert(m_unpacked_img_rgbx != nullptr); + assert(m_ert_p_rgb.m_color_weights[3] == 0); + m_total_smooth_blocks_rgb = 0; + m_total_second_matches_rgb = 0; + m_total_modified_rgb = 0; + m_success = true; + } +}; + +struct rdo_bc3_workload { + uint32_t m_width; + uint32_t m_height; + /* [in,out] packed/encoded image blocks */ + uint8_t* m_packed_img; + /* [in,out] BC1 RGBX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_rgbx; + /* [in,out] BC4 AXXX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_axxx; + /* [in] BC1 RGB blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_rgb; + /* [in] BC4 A blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_a; + /* [out] BC1 total number smooth RGB blocks */ + std::atomic_int32_t m_total_smooth_blocks_rgb; + /* [out] BC4 total number smooth A blocks */ + std::atomic_int32_t m_total_smooth_blocks_a; + /* [out] BC1 total number second matches for RGB blocks */ + std::atomic_int32_t m_total_second_matches_rgb; + /* [out] BC4 total number second matches for A blocks */ + std::atomic_int32_t m_total_second_matches_a; + /* [out] BC1 total number total number modified packed/encoded RGB blocks */ + std::atomic_int32_t m_total_modified_rgb; + /* [out] BC4 total number total number modified packed/encoded A blocks */ + std::atomic_int32_t m_total_modified_a; + /* [out] false if any thread fails */ + bool m_success; + const float* m_block_rgb_mse_scales; + + rdo_bc3_workload() = delete; + rdo_bc3_workload(uint32_t width, uint32_t height, uint8_t* packed_img, + const ert::color_rgba* unpacked_img_rgbx, + const ert::color_rgba* unpacked_img_axxx, ert::reduce_entropy_params ert_p_rgb, + ert::reduce_entropy_params ert_p_a, + const float* block_rgb_mse_scales = nullptr) + : m_width{width}, + m_height{height}, + m_packed_img{packed_img}, + m_unpacked_img_rgbx{unpacked_img_rgbx}, + m_unpacked_img_axxx{unpacked_img_axxx}, + m_ert_p_rgb{ert_p_rgb}, + m_ert_p_a{ert_p_a}, + m_block_rgb_mse_scales{block_rgb_mse_scales} { + assert(m_packed_img != nullptr); + assert(m_unpacked_img_rgbx != nullptr); + assert(m_unpacked_img_axxx != nullptr); + assert(m_ert_p_rgb.m_color_weights[3] == 0); + assert(m_ert_p_a.m_color_weights[1] == 0); + assert(m_ert_p_a.m_color_weights[2] == 0); + assert(m_ert_p_a.m_color_weights[3] == 0); + m_total_smooth_blocks_rgb = 0; + m_total_smooth_blocks_a = 0; + m_total_second_matches_rgb = 0; + m_total_second_matches_a = 0; + m_total_modified_rgb = 0; + m_total_modified_a = 0; + m_success = true; + } +}; + +struct rdo_bc4_workload { + uint32_t m_width; + uint32_t m_height; + /* [in,out] BC4 packed/encoded image blocks */ + uint8_t* m_packed_img; + /* [in,out] BC4 RXXX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_rxxx; + /* [in] BC4 R blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_r; + /* [out] BC4 total number smooth R blocks */ + std::atomic_int32_t m_total_smooth_blocks_r; + /* [out] BC4 total number smooth G blocks */ + std::atomic_int32_t m_total_second_matches_r; + /* [out] BC4 total number second matches for G blocks */ + std::atomic_int32_t m_total_modified_r; + /* [out] false if any thread fails */ + bool m_success; + + rdo_bc4_workload() = delete; + rdo_bc4_workload(uint32_t width, uint32_t height, uint8_t* packed_img, + const ert::color_rgba* unpacked_img_rxxx, + const ert::reduce_entropy_params ert_p_r) + : m_width{width}, + m_height{height}, + m_packed_img{packed_img}, + m_unpacked_img_rxxx{unpacked_img_rxxx}, + m_ert_p_r{ert_p_r} { + assert(m_packed_img != nullptr); + assert(m_unpacked_img_rxxx != nullptr); + assert(m_ert_p_r.m_color_weights[1] == 0); + assert(m_ert_p_r.m_color_weights[2] == 0); + assert(m_ert_p_r.m_color_weights[3] == 0); + m_total_smooth_blocks_r = 0; + m_total_second_matches_r = 0; + m_total_modified_r = 0; + m_success = true; + } +}; + +struct rdo_bc5_workload { + uint32_t m_width; + uint32_t m_height; + /* [in,out] BC5 packed/encoded image blocks */ + uint8_t* m_packed_img; + /* [in,out] BC4 RXXX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_rxxx; + /* [in,out] BC4 GXXX unpacked/decoded blocks (x == ignored) */ + const ert::color_rgba* m_unpacked_img_gxxx; + /* [in] BC4 R blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_r; + /* [in] BC4 G blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_g; + /* [out] BC4 total number smooth R blocks */ + std::atomic_int32_t m_total_smooth_blocks_r; + /* [out] BC4 total number smooth G blocks */ + std::atomic_int32_t m_total_smooth_blocks_g; + /* [out] BC4 total number second matches for R blocks */ + std::atomic_int32_t m_total_second_matches_r; + /* [out] BC4 total number second matches for G blocks */ + std::atomic_int32_t m_total_second_matches_g; + /* [out] BC4 total number total number modified packed/encoded R blocks */ + std::atomic_int32_t m_total_modified_r; + /* [out] BC4 total number total number modified packed/encoded G blocks */ + std::atomic_int32_t m_total_modified_g; + /* [out] false if any thread fails */ + bool m_success; + + rdo_bc5_workload() = delete; + rdo_bc5_workload(uint32_t width, uint32_t height, uint8_t* packed_img, + const ert::color_rgba* unpacked_img_rxxx, + const ert::color_rgba* unpacked_img_gxxx, ert::reduce_entropy_params ert_p_r, + ert::reduce_entropy_params ert_p_g) + : m_width{width}, + m_height{height}, + m_packed_img{packed_img}, + m_unpacked_img_rxxx{unpacked_img_rxxx}, + m_unpacked_img_gxxx{unpacked_img_gxxx}, + m_ert_p_r{ert_p_r}, + m_ert_p_g{ert_p_g} { + assert(m_packed_img != nullptr); + assert(m_unpacked_img_rxxx != nullptr); + assert(m_unpacked_img_gxxx != nullptr); + assert(m_ert_p_r.m_color_weights[3] == 0); + assert(m_ert_p_g.m_color_weights[1] == 0); + assert(m_ert_p_g.m_color_weights[2] == 0); + assert(m_ert_p_g.m_color_weights[3] == 0); + m_total_smooth_blocks_r = 0; + m_total_smooth_blocks_g = 0; + m_total_second_matches_r = 0; + m_total_second_matches_g = 0; + m_total_modified_r = 0; + m_total_modified_g = 0; + m_success = true; + } +}; + +struct rdo_bc7_workload { + uint32_t m_width; + uint32_t m_height; + /* [in,out] packed/encoded image blocks */ + uint8_t* m_packed_img; + /* [in,out] BC7 RGBA unpacked/decoded blocks */ + const ert::color_rgba* m_unpacked_img_rgba; + /* [in] BC7 RGBA blocks entropy reduction params */ + ert::reduce_entropy_params m_ert_p_rgba; + /* [out] BC7 total number smooth RGBA blocks */ + std::atomic_int32_t m_total_smooth_blocks_rgba; + /* [out] BC7 total number second matches for RGBA blocks */ + std::atomic_int32_t m_total_second_matches_rgba; + /* [out] BC7 total number total number modified packed/encoded RGBA blocks */ + std::atomic_int32_t m_total_modified_rgba; + /* [out] false if any thread fails */ + bool m_success; + const float* m_block_rgb_mse_scales; + + rdo_bc7_workload() = delete; + rdo_bc7_workload(uint32_t width, uint32_t height, uint8_t* packed_img, + const ert::color_rgba* unpacked_img_rgba, ert::reduce_entropy_params ert_p, + const float* block_rgb_mse_scales = nullptr) + : m_width{width}, + m_height{height}, + m_packed_img{packed_img}, + m_unpacked_img_rgba{unpacked_img_rgba}, + m_ert_p_rgba{ert_p}, + m_block_rgb_mse_scales{block_rgb_mse_scales} { + assert(m_packed_img != nullptr); + assert(m_unpacked_img_rgba != nullptr); + m_total_smooth_blocks_rgba = 0; + m_total_second_matches_rgba = 0; + m_total_modified_rgba = 0; + m_success = true; + } +}; + +static bool +unpack_block_bc1(const void* pBlock, ert::color_rgba* pPixels, uint32_t, void*) { + const bool allow_3color_mode = true; // hardcoded + const bool use_3color_mode_for_black = false; // hardcoded + // rgbcx's BC1 decoder decodes into an rgbcx::color32 struct not into a ert::color_rgba struct + // (which, even though is similar results in UB if passed directly). + rgbcx::color32 p_src_pixels[4 * 4]; + bool used_3color = + rgbcx::unpack_bc1(pBlock, p_src_pixels, true, rgbcx::bc1_approx_mode::cBC1Ideal); + memcpy(pPixels, p_src_pixels, sizeof(rgbcx::color32) * 4 * 4); + // This check is copied from the original code at: rdo_bc_encoder.h + if (used_3color) { + if (!allow_3color_mode) return false; + if (!use_3color_mode_for_black) { + // TODO: is this also UB (does it respect strict-aliasing rule or am I paranoid)? + auto pBC1_block = static_cast(pBlock); + for (uint32_t y = 0; y < 4; ++y) + for (uint32_t x = 0; x < 4; ++x) + if (pBC1_block->get_selector(x, y) == 3) return false; + } + } + return true; +}; + +static bool +unpack_block_bc4(const void* pBlock, ert::color_rgba* pPixels, uint32_t, void*) { + uint8_t p_src_pixels[4 * 4 * 4]; + memset(p_src_pixels, 0, sizeof(p_src_pixels)); + rgbcx::unpack_bc4(pBlock, p_src_pixels, 4); + memcpy(pPixels, p_src_pixels, 4 * 4 * 4); + return true; +}; + +static bool +unpack_block_bc7(const void* pBlock, ert::color_rgba* pPixels, uint32_t, void*) { + // Q. Why not pass reinterpret_cast(pPixels) directly? + // A. basist::color_rgba and ert::color_rgba although similar, are in + // different TUs and this is apparently undefined behaviour + // (see: https://tttapa.github.io/Pages/Programming/Cpp/Practices/type-punning.html) + // Even if they were defined in same TU, this would still be UB. + // (also see: + // https://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html) + // bc7enc_rdo expects that unpackers decode blocks to ert::color_rgba + static_assert(sizeof(ert::color_rgba) == sizeof(basist::color_rgba)); + basist::color_rgba p_unpacked_block[4 * 4]; + if (basist::bc7u::unpack_bc7(pBlock, p_unpacked_block)) { + memcpy(pPixels, p_unpacked_block, sizeof(p_unpacked_block)); + return true; + } + return false; +}; + +//************************************************************************ +//* Multithreading utils * +//************************************************************************ + +static inline void +get_current_thread_blocks(uint32_t w, uint32_t h, int thread_id, int thread_count, + uint32_t& block_start_idx, uint32_t& num_blocks) { + const uint32_t num_blocks_x = (w + 3) / 4; + const uint32_t num_blocks_y = (h + 3) / 4; + const uint32_t nbrBlocksTotal = num_blocks_x * num_blocks_y; + // Each thread takes a set of contiguous blocks to encode + const bool is_last_thread = thread_id == (thread_count - 1); + const uint32_t num_blocks_per_thread = nbrBlocksTotal / thread_count; + block_start_idx = thread_id * num_blocks_per_thread; + num_blocks = is_last_thread ? nbrBlocksTotal - (thread_id * num_blocks_per_thread) + : num_blocks_per_thread; +} + +//************************************************************************ +//* BCn encoder runner * +//************************************************************************ + +static void +compression_workload_runner(int thread_count, int thread_id, void* payload) { + bcn_compression_workload* workload = static_cast(payload); + const uint32_t width = workload->width; + const uint32_t height = workload->height; + const uint32_t nchannels = workload->nchannels; + const uint32_t num_blocks_x = (width + 3) / 4; + + // Each thread takes a set of contiguous blocks to encode + uint32_t block_start_idx, block_end_idx, num_blocks; + get_current_thread_blocks(workload->width, workload->height, thread_id, thread_count, + block_start_idx, num_blocks); + block_end_idx = block_start_idx + num_blocks; + + // Intermediate store for decoded LDR/HDR BCn block + uint8_t rgba[4 * 4 * 4]; /* 4 x 4 x 4 */ + uint8_t rgb[4 * 4 * 3]; /* only for BC1 */ + uint16_t rgbh[4 * 4 * 3]; /* 4 x 4 x 3 x 2 - for BC6H? */ + uint8_t rgbx[4 * 4 * 4]; /* only for BC1 */ + + basist::color_rgba rgba_bc7[16]; + + basist::astc_6x6_hdr::fast_bc6h_params bc6h_params; + bc6h_params.init(); + + uint8_t* pDstLevelImage = workload->data_out; + + for (uint32_t block_idx = block_start_idx; block_idx < block_end_idx; ++block_idx) { + const uint32_t xBlock = block_idx % num_blocks_x; + const uint32_t yBlock = block_idx / num_blocks_x; + // Extract/Copy source block (non-multiple-of-4 texture dimensions are handled + // via clamping to edge). + if (workload->params.bcn == KTX_BCN_COMPRESSION_BC6HU || + workload->params.bcn == KTX_BCN_COMPRESSION_BC6HS) { + extract_block(rgbh, reinterpret_cast(workload->data_in), xBlock * 4, + yBlock * 4, width, height, nchannels); + } else if (workload->params.bcn == KTX_BCN_COMPRESSION_BC1) { + // BC1 is an edge case because encoder expects 4 while input is 3 + extract_block(rgb, workload->data_in, xBlock * 4, yBlock * 4, width, height, nchannels); + rgb_to_rgba_block(rgbx, rgb); + } else { + extract_block(rgba, workload->data_in, xBlock * 4, yBlock * 4, width, height, + nchannels); + } + + switch (workload->params.bcn) { +#if 0 // bc7enc BC1 encoder does not support punch-through alpha + case KTX_BCN_COMPRESSION_BC1A: +#endif + case KTX_BCN_COMPRESSION_BC1: + // BC1: 4 x 4 x 3 = 64 bytes -> 8 bytes + rgbcx::encode_bc1(workload->params.bc1CompressionQuality, + pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC1_BLOCK_SIZE, + rgbx, true, false); + break; + + case KTX_BCN_COMPRESSION_BC3: + // BC3: 4 x 4 x 4 = 64 bytes -> 16 bytes + rgbcx::encode_bc3(workload->params.bc1CompressionQuality, + pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC3_BLOCK_SIZE, + rgba); + break; + + case KTX_BCN_COMPRESSION_BC4: + // BC4: 4 x 4 x 1 = 16 bytes -> 8 bytes + rgbcx::encode_bc4(pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC4_BLOCK_SIZE, + rgba, /* stride */ BC4_NCHANNELS); + break; + + case KTX_BCN_COMPRESSION_BC5: + // BC5: 4 x 4 x 2 = 32 bytes -> 16 bytes + rgbcx::encode_bc5(pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC5_BLOCK_SIZE, + rgba, 0, 1, + /* stride */ BC5_NCHANNELS); + break; + + case KTX_BCN_COMPRESSION_BC6HU: + // TODO: is this reinterpret_cast an UB? fix/verify before merge + basist::astc_6x6_hdr::fast_encode_bc6h( + rgbh, + reinterpret_cast( + pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC6H_BLOCK_SIZE), + bc6h_params); + break; + + case KTX_BCN_COMPRESSION_BC7: + // BC7: 4 x 4 x 4 = 64 bytes -> 16 bytes + // TODO: is this needed? libktx is compiled with strict aliasing while basisu is + // compiled with '-fno-strict-aliasing'. basist::color_rgba is a trivial type after all + // ... + memcpy(rgba_bc7, rgba, sizeof(rgba_bc7)); + basist::bc7f::fast_pack_bc7_auto_rgba( + pDstLevelImage + (yBlock * num_blocks_x + xBlock) * BC7_BLOCK_SIZE, rgba_bc7, + workload->params.bc7CompressionQuality); + break; + + default: // should never occur + assert(false); + return; + } + } +} + +//************************************************************************ +//* RDO runners * +//************************************************************************ + +static void +rdo_bc1_workload_runner(int thread_count, int thread_id, void* payload) { + uint32_t block_start_idx, num_blocks; + uint32_t total_modified_local = 0; + ert::reduce_entropy_stats stats_local; + auto workload = static_cast(payload); + get_current_thread_blocks(workload->m_width, workload->m_height, thread_id, thread_count, + block_start_idx, num_blocks); + bool res = ert::reduce_entropy( + workload->m_packed_img + block_start_idx * BC1_BLOCK_SIZE, num_blocks, BC1_BLOCK_SIZE, + BC1_BLOCK_SIZE, 4, 4, 3 /* alpha channel ignored */, + workload->m_unpacked_img_rgbx + block_start_idx * (4 * 4), workload->m_ert_p_rgb, + total_modified_local, unpack_block_bc1, nullptr, stats_local, + workload->m_block_rgb_mse_scales != nullptr + ? workload->m_block_rgb_mse_scales + block_start_idx + : nullptr); + + if (res) { + workload->m_total_modified_rgb += total_modified_local; + workload->m_total_smooth_blocks_rgb += stats_local.total_smooth_blocks; + workload->m_total_second_matches_rgb += stats_local.total_second_matches; + } else { + workload->m_success = false; + } +} + +static void +rdo_bc3_workload_runner(int thread_count, int thread_id, void* payload) { + uint32_t block_start_idx, num_blocks; + uint32_t total_modified_local_rgb = 0; + uint32_t total_modified_local_a = 0; + ert::reduce_entropy_stats local_stats; + auto workload = reinterpret_cast(payload); + get_current_thread_blocks(workload->m_width, workload->m_height, thread_id, thread_count, + block_start_idx, num_blocks); + // In bc7enc_rdo's code and after confirmed testing: BC4 A then BC1 RGB blocks. + // First, RDO the BC4 A channel blocks ... + bool res = ert::reduce_entropy( + workload->m_packed_img + block_start_idx * BC3_BLOCK_SIZE, num_blocks, BC3_BLOCK_SIZE, + BC4_BLOCK_SIZE, 4, 4, 1, workload->m_unpacked_img_axxx + block_start_idx * (4 * 4), + workload->m_ert_p_a, total_modified_local_a, unpack_block_bc4, nullptr, local_stats); + if (res) { + workload->m_total_modified_a += total_modified_local_a; + workload->m_total_smooth_blocks_a += local_stats.total_smooth_blocks; + workload->m_total_second_matches_a += local_stats.total_second_matches; + } else { + workload->m_success = false; + } + // Then reduce entropy for the BC1 RGB block ... + res = ert::reduce_entropy( + (workload->m_packed_img + BC4_BLOCK_SIZE) + block_start_idx * BC3_BLOCK_SIZE, num_blocks, + BC3_BLOCK_SIZE, BC1_BLOCK_SIZE, 4, 4, 3, + workload->m_unpacked_img_rgbx + block_start_idx * (4 * 4), workload->m_ert_p_rgb, + total_modified_local_rgb, unpack_block_bc1, nullptr, local_stats, + workload->m_block_rgb_mse_scales != nullptr + ? workload->m_block_rgb_mse_scales + block_start_idx + : nullptr); + if (res) { + workload->m_total_modified_rgb += total_modified_local_rgb; + workload->m_total_smooth_blocks_rgb += local_stats.total_smooth_blocks; + workload->m_total_second_matches_rgb += local_stats.total_second_matches; + } else { + workload->m_success = false; + return; + } +} + +static void +rdo_bc4_workload_runner(int thread_count, int thread_id, void* payload) { + uint32_t block_start_idx, num_blocks; + uint32_t total_modified_local = 0; + ert::reduce_entropy_stats local_stats; + auto workload = reinterpret_cast(payload); + get_current_thread_blocks(workload->m_width, workload->m_height, thread_id, thread_count, + block_start_idx, num_blocks); + bool res = ert::reduce_entropy( + workload->m_packed_img + block_start_idx * BC4_BLOCK_SIZE, num_blocks, BC4_BLOCK_SIZE, + BC4_BLOCK_SIZE, 4, 4, 1, workload->m_unpacked_img_rxxx + block_start_idx * (4 * 4), + workload->m_ert_p_r, total_modified_local, unpack_block_bc4, nullptr, local_stats); + if (res) { + workload->m_total_modified_r += total_modified_local; + workload->m_total_smooth_blocks_r += local_stats.total_smooth_blocks; + workload->m_total_second_matches_r += local_stats.total_second_matches; + } else { + workload->m_success = false; + } +} + +static void +rdo_bc5_workload_runner(int thread_count, int thread_id, void* payload) { + uint32_t block_start_idx, num_blocks; + uint32_t total_modified_local_r = 0; + uint32_t total_modified_local_g = 0; + ert::reduce_entropy_stats local_stats; + auto workload = static_cast(payload); + get_current_thread_blocks(workload->m_width, workload->m_height, thread_id, thread_count, + block_start_idx, num_blocks); + // BC5: one BC4 block for R followed by one BC4 block for G + // reduce entropy for the BC4 R block ... + bool res = ert::reduce_entropy( + workload->m_packed_img + block_start_idx * BC5_BLOCK_SIZE, num_blocks, + BC5_BLOCK_SIZE /* 2 x BC4_BLOCK_SIZE */, BC4_BLOCK_SIZE, 4, 4, 1, + workload->m_unpacked_img_rxxx + block_start_idx * (4 * 4), workload->m_ert_p_r, + total_modified_local_r, unpack_block_bc4, nullptr, local_stats); + if (res) { + workload->m_total_modified_r += total_modified_local_r; + workload->m_total_smooth_blocks_r += local_stats.total_smooth_blocks; + workload->m_total_second_matches_r += local_stats.total_second_matches; + } else { + workload->m_success = false; + return; + } + // then reduce entropy for the BC4 G block ... + res = ert::reduce_entropy( + (workload->m_packed_img + BC4_BLOCK_SIZE) + block_start_idx * BC5_BLOCK_SIZE, num_blocks, + BC5_BLOCK_SIZE /* 2 x BC4_BLOCK_SIZE */, BC4_BLOCK_SIZE, 4, 4, 1, + workload->m_unpacked_img_gxxx + block_start_idx * (4 * 4), workload->m_ert_p_g, + total_modified_local_g, unpack_block_bc4, nullptr, local_stats); + + if (res) { + workload->m_total_modified_g += total_modified_local_g; + workload->m_total_smooth_blocks_g += local_stats.total_smooth_blocks; + workload->m_total_second_matches_g += local_stats.total_second_matches; + } else { + workload->m_success = false; + } +} + +static void +rdo_bc7_workload_runner(int thread_count, int thread_id, void* payload) { + uint32_t block_start_idx, num_blocks; + uint32_t total_modified_local = 0; + auto workload = static_cast(payload); + get_current_thread_blocks(workload->m_width, workload->m_height, thread_id, thread_count, + block_start_idx, num_blocks); + ert::reduce_entropy_stats stats_local; + bool res = ert::reduce_entropy( + workload->m_packed_img + block_start_idx * BC7_BLOCK_SIZE, num_blocks, BC7_BLOCK_SIZE, + BC7_BLOCK_SIZE, 4, 4, 4, workload->m_unpacked_img_rgba + block_start_idx * (4 * 4), + workload->m_ert_p_rgba, total_modified_local, unpack_block_bc7, nullptr, stats_local, + workload->m_block_rgb_mse_scales != nullptr + ? workload->m_block_rgb_mse_scales + block_start_idx + : nullptr); + + if (res) { + workload->m_total_modified_rgba += total_modified_local; + workload->m_total_smooth_blocks_rgba += stats_local.total_smooth_blocks; + workload->m_total_second_matches_rgba += stats_local.total_second_matches; + } else { + workload->m_success = false; + } +} + +#if DEBUG_RDO_ULTRASMOOTH +bool +save_png(const char* pFilename, const std::vector& img, uint32_t width, + uint32_t height) { + std::vector pixels; + pixels.resize(width * height * 3); + unsigned char* pDst = &pixels[0]; + for (uint32_t y = 0; y < height; y++) + for (uint32_t x = 0; x < width; x++, pDst += 3) { + pDst[0] = img[x + y * width].r; + pDst[1] = img[x + y * width].g; + pDst[2] = img[x + y * width].b; + } + return lodepng::encode(pFilename, pixels, width, height, LCT_RGB) == 0; +} +#endif + +//************************************************************************ +//* RDO ultrasmooth blocks handling functions * +//************************************************************************ + +// Adapted from bc7enc_rdo's compute_block_mse_scales function with +// minor changes: +// - use ert::color_rgba instead of color_quad_u8 +// - remove image_u8 dependency and use std::vector instead +// - integrate mse adjustment based on smooth_block_max_mse_scale and lambda +// within the internal vs. doing this externally (as in bc7enc_rdo's case) +// +// TODO: this is obviously very unoptimized (e.g., return std::vector +// instead of std::float which at the very least should result in 4x less space). +// Also, internally some std::vectors are recreated a lot. It is definitely not +// trivial to optimize this. +// +// TODO: this should be moved to ert.cpp but we still rely on extract_block +// which is why this is currently implemented here. +static std::vector +compute_block_rgb_mse_scales(const uint8_t* unpacked_img, uint32_t width, uint32_t height, + uint32_t nchannels, float smooth_block_max_mse_scale, float lambda) { + const uint32_t num_blocks_x = (width + 3) / 4; + const uint32_t num_blocks_y = (height + 3) / 4; + const float ULTRASMOOTH_BLOCK_STD_DEV_THRESHOLD = 2.9f; + const float DARK_THRESHOLD = 13.0f; + const float BRIGHT_THRESHOLD = 222.0f; + const float ULTRAMOOTH_BLOCK_MSE_SCALE = 120.0f; + const uint32_t ULTRASMOOTH_REGION_TOO_SMALL_THRESHOLD = 64; + const int rgb_pitch = 4 * 3; + uint8_t rgb[4 * rgb_pitch]; /* only for BC1 */ + + assert(nchannels == 3 || nchannels == 4); + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + std::vector ultrasmooth_blocks_vis(num_blocks_x * num_blocks_y); + + for (uint32_t by = 0; by < num_blocks_y; by++) { + for (uint32_t bx = 0; bx < num_blocks_x; bx++) { + ert::color_rgba block_pixels[16]; + + if (nchannels == 3) { + // in this case (BC1 RGB), unpacked_img does not contain alpha + // but to keep everything simple, all proceeding operations + // expect an RGBA block (not an RGB block) hence why we extract + // to intermediate rgb block and not directly to block_pixels + extract_block(rgb, unpacked_img, bx * 4, by * 4, width, height, nchannels); + for (int i = 0; i < 4 * 4; ++i) { + block_pixels[i].r = rgb[i * 3]; + block_pixels[i].g = rgb[i * 3 + 1]; + block_pixels[i].b = rgb[i * 3 + 2]; + block_pixels[i].a = 255u; + } + } else /* nchannels == 4 */ { + extract_block(&block_pixels[0].r, unpacked_img, bx * 4, by * 4, width, height, + nchannels); + } + + ert::tracked_stat y_stats; + for (uint32_t y = 0; y < 4; y++) + for (uint32_t x = 0; x < 4; x++) { + int l = block_pixels[x + y * 4].get_luma(false); + y_stats.update(l); + } + + float max_std_dev = ert::compute_block_max_std_dev(block_pixels, 4, 4, 3); + + float yl = max_std_dev / ULTRASMOOTH_BLOCK_STD_DEV_THRESHOLD; + + yl = ert::clamp(yl, 0.0f, 1.0f); + yl *= yl; + + float y_avg = y_stats.get_mean(); + if ((y_avg < DARK_THRESHOLD) || (y_avg >= BRIGHT_THRESHOLD)) yl = 1.0f; + int k = std::min((int)(yl * 255.0f + .5f), 255); + + ultrasmooth_blocks_vis[bx + by * num_blocks_x] = ert::color_rgba(k, 255); + } + } + + // Propagate blocks that are certainly non-ultra-smooth (i.e., 255) to their + // surrounding 3 x 3 blocks regions. + for (int pass = 0; pass < 1; pass++) { + std::vector next_vis(ultrasmooth_blocks_vis); + for (int y = 0; y < (int)num_blocks_y; y++) { + for (int x = 0; x < (int)num_blocks_x; x++) { + int m = 0; + for (int dy = -1; dy <= 1; dy++) + for (int dx = -1; dx <= 1; dx++) { + auto c_idx = + ert::get_clamped_index(x + dx, y + dy, num_blocks_x, num_blocks_y); + if (ultrasmooth_blocks_vis[c_idx].r == 255) m = std::max(m, 255); + } + next_vis[x + y * num_blocks_x].set((uint8_t)m, 255); + } // x blocks + } // y blocks + ultrasmooth_blocks_vis.swap(next_vis); + } + +#if DEBUG_RDO_ULTRASMOOTH + save_png("ultrasmooth_block_mask_sharp_pre_propagation.png", ultrasmooth_blocks_vis, + num_blocks_x, num_blocks_y); +#endif + + // Smooth out/further propagate blocks that are certainly + // non-ultra smooth (i.e., 255) to their surrounding. + // The difference here is that we are doing propagation 32 times based on + // whether the number of surrounding (delta = +-1) + // certainly-non-ultra-smooth blocks is >= 5 (half of 9 == 3 x 3) blocks + for (uint32_t pass = 0; pass < 32; pass++) { + std::vector next_vis(ultrasmooth_blocks_vis); + for (int y = 0; y < (int)num_blocks_y; y++) { + for (int x = 0; x < (int)num_blocks_x; x++) { + // auto c_idx = get_clamped_index(x, y, num_blocks_x, num_blocks_y); + if (ultrasmooth_blocks_vis[x + y * num_blocks_x].r < 255) { + int m = 0; + for (int dy = -1; dy <= 1; dy++) + for (int dx = -1; dx <= 1; dx++) { + auto c_idx = + ert::get_clamped_index(x + dx, y + dy, num_blocks_x, num_blocks_y); + if (ultrasmooth_blocks_vis[c_idx].r == 255) m++; + } + + if (m >= 5) next_vis[x + y * num_blocks_x].set(255); + } + } + } + ultrasmooth_blocks_vis.swap(next_vis); + } + + std::vector orig_ultrasmooth_blocks_vis(ultrasmooth_blocks_vis); + +#if DEBUG_RDO_ULTRASMOOTH + save_png("ultrasmooth_block_mask_pre_filter.png", orig_ultrasmooth_blocks_vis, num_blocks_x, + num_blocks_y); +#endif + + // filter (still have not understood this in detail) + for (uint32_t by = 0; by < num_blocks_y; by++) { + for (uint32_t bx = 0; bx < num_blocks_x; bx++) { + const bool is_ultrasmooth = ultrasmooth_blocks_vis[bx + by * num_blocks_x].r == 0; + if (!is_ultrasmooth) continue; + + std::vector filled_pixels; + filled_pixels.reserve(256); + + uint32_t total_set_pixels = flood_fill( + ultrasmooth_blocks_vis, bx, by, num_blocks_x, num_blocks_y, + ert::color_rgba(255, 255, 255, 255), ert::color_rgba(0, 0, 0, 255), &filled_pixels); + + if (total_set_pixels < ULTRASMOOTH_REGION_TOO_SMALL_THRESHOLD) { + for (uint32_t i = 0; i < filled_pixels.size(); i++) + orig_ultrasmooth_blocks_vis[filled_pixels[i].m_x + + filled_pixels[i].m_y * num_blocks_x] = + ert::color_rgba(255, 255, 255, 255); + } + + } // bx + } // by + +#if DEBUG_RDO_ULTRASMOOTH + save_png("ultrasmooth_block_mask_post_filter.png", orig_ultrasmooth_blocks_vis, num_blocks_x, + num_blocks_y); +#endif + + std::vector block_mse_scales(num_blocks_x * num_blocks_y, -1.0f); + + [[maybe_unused]] uint32_t total_ultrasmooth_blocks = 0; + for (uint32_t by = 0; by < num_blocks_y; by++) { + for (uint32_t bx = 0; bx < num_blocks_x; bx++) { + const bool is_ultrasmooth = orig_ultrasmooth_blocks_vis[bx + by * num_blocks_x].r == 0; + float& s = block_mse_scales[bx + by * num_blocks_x]; + if (is_ultrasmooth) { + s = ULTRAMOOTH_BLOCK_MSE_SCALE; + s = std::max(smooth_block_max_mse_scale, s * std::min(lambda, 3.0f)); + ++total_ultrasmooth_blocks; + } + } + } +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_ultrasmooth_time = + std::chrono::duration_cast(finish - start).count(); + std::cout << "total nbr ultra smooth blocks (%): " + << (100.0f * total_ultrasmooth_blocks) / (num_blocks_x * num_blocks_y) << '\n'; + std::cout << "total ultra smooth blocks MSE scales generation time (s): " + << total_ultrasmooth_time / 1000.0f << '\n'; +#endif + return block_mse_scales; +} + +#if DEBUG_PRINT_STATS +[[maybe_unused]] static inline void +print_rdo_params(const rdo_params& params) { + std::cout << "rdo lambda: " << params.ert_p.m_lambda << '\n'; + std::cout << "rdo loopback window size: " << params.ert_p.m_lookback_window_size << '\n'; + std::cout << "rdo auto smooth block max MSE scale: " << params.auto_smooth_block_max_mse_scale + << '\n'; + std::cout << "rdo smooth block max MSE scale: " << params.ert_p.m_smooth_block_max_mse_scale + << '\n'; + std::cout << "rdo smooth block max std dev: " << params.ert_p.m_max_smooth_block_std_dev + << '\n'; + std::cout << "rdo max allowed RMS increase ratio: " + << params.ert_p.m_max_allowed_rms_increase_ratio << '\n'; + std::cout << "rdo color weights: [" << params.ert_p.m_color_weights[0] << ", " + << params.ert_p.m_color_weights[1] << ", " << params.ert_p.m_color_weights[2] << ", " + << params.ert_p.m_color_weights[3] << "] \n"; + std::cout << "rdo try two matches: " << params.ert_p.m_try_two_matches << '\n'; + std::cout << "rdo skip zero MSE blocks: " << params.ert_p.m_skip_zero_mse_blocks << '\n'; +}; +#endif + +//************************************************************************ +//* HDR (BC6HU/BC6HS) functions + structs * +//************************************************************************ + +struct clean_hdr_results { + double hdr_image_scale; + bool one_or_more_nan_values; + bool one_or_more_inf_values; + bool one_or_more_signed_values; + + clean_hdr_results() + : hdr_image_scale{1.0}, + one_or_more_nan_values{false}, + one_or_more_inf_values{false}, + one_or_more_signed_values{false} {} +}; + +/* + * @~English + * @brief Clean up HDR input for basisu's BC6HU encoder. + * + * Based on basisu's basis_compressor::clean_hdr_image. It sets NaN, + * infinite and negative values to 0 and scales input values that are + * greater than basist::ASTC_HDR_MAX_VAL. + * + * @param [in,out] src_rgb16 pointer to the source 16-bit unpacked data. + * @param [in] width image width (in texels). + * @param [in] height image height (in texels). + * + * @return whether one or more NaN, infinite, or signed values were + * encountered and set to 0 and whether scaling has been applied. + */ +static clean_hdr_results +clean_hdr_image(ktx_uint8_t* src_rgb16, uint32_t width, uint32_t height) { + clean_hdr_results res; + res.hdr_image_scale = 1.0; + auto p_src = reinterpret_cast(src_rgb16); + + // Find max used value + float max_used_val = 0.0f; + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + for (uint32_t i = 0; i < 3; i++) { + uint16_t& c = p_src[(x + y * width) * 3 + i]; + float val = basist::half_to_float(c); + + if (std::isnan(val) || std::isinf(val) || basist::half_is_signed(c)) { + if (std::isnan(val)) res.one_or_more_nan_values = true; + if (std::isinf(val)) res.one_or_more_inf_values = true; + if (basist::half_is_signed(c)) res.one_or_more_signed_values = true; + c = 0; + val = 0.0f; + } + + max_used_val = std::max(max_used_val, val); + } + } + } + + // If the max value can't be encoded safely to ASTC HDR, we'll have to + // rescale the source image. + if (max_used_val > basist::ASTC_HDR_MAX_VAL) { + res.hdr_image_scale = max_used_val / basist::ASTC_HDR_MAX_VAL; + const double inv_hdr_image_scale = basist::ASTC_HDR_MAX_VAL / max_used_val; + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + for (uint32_t i = 0; i < 3; i++) { + uint16_t& c = p_src[(x + y * width) * 3 + i]; + auto pre_scaled_val = basist::half_to_float(c); + c = basist::float_to_half((float)std::min( + pre_scaled_val * inv_hdr_image_scale, basist::ASTC_HDR_MAX_VAL)); + } + } + } + } + + return res; +} + +//************************************************************************ +//* RDO post-processing function * +//************************************************************************ + +/** + * @~English + * @brief Performs rate distortion optimization (RDO) on BCn-encoded blocks. + * + * RDO is performed to reduce entropy for a potential subsequent Deflate + * step (i.e., significant bit rate reduction can be achieved when + * further supercompressed with Zlib/ZSTD). BC2 and BC6H formats are + * currently not supported. + * + * Some values of the reduce_entropy_params struct may be adjusted before + * being passed to the underlying RDO subroutine. The reason for this is + * that, depending on the BCn compression, some values make no sense and + * may kill efficiency. + * + * @param [in] unpacked_img pointer to the source unpacked data. + * @param [in] unpacked_img_size size of source unpacked data in bytes. + * Internal sanity checks are performed on this + * provided size. + * @param [in,out] packed_img pointer to the packed/encoded image data. + * These packed/encoded blocks are modified to + * reduce bit/texel rate which is measured via + * an LZ (Deflate) compression simulation while + * keeping distortion minimal (i.e., difference + * between decoded/unpacked and actual source + * image data pointed to by @p unpacked_img). + * @param [in] packed_img_size size of packed/encoded data in bytes. + * Internal sanity checks are performed on this + * provided size. + * @param [in] params RDO parameters. No sanitation/checks are + * performed on the provided reduce entropy + * params (ert). The caller has to make sure + * that provided params are valid (i.e., in + * range for floats). + * @param [in] bcn used to determine the BCn compression of the + * provided compressed blocks. + * @param [in] width image width (in texels). + * @param [in] height image height (in texels). + * @param [in] threads number of threads to dispatch for RDO. + * Setting this to 1 disables multithreading + * and simply runs RDO within this main thread. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + */ +static KTX_error_code +postprocess_rdo_bcn(const uint8_t* unpacked_img, size_t unpacked_img_size, uint8_t* packed_img, + size_t packed_img_size, rdo_params params, ktx_bcn_compression_e bcn, + uint32_t width, uint32_t height, uint32_t threads) { +#define CHECK_SIZES(nchannels, block_size) \ + do { \ + if ((unpacked_img_size != (size_t)width * height * nchannels) || \ + (packed_img_size != (size_t)nBlocksTotal * block_size)) { \ + return KTX_DECOMPRESS_LENGTH_ERROR; \ + } \ + } while (0) + + const uint32_t nBlocksX = (width + 3) / 4; + const uint32_t nBlocksY = (height + 3) / 4; + const uint32_t nBlocksTotal = nBlocksX * nBlocksY; + std::vector block_rgb_mse_scales; /* only BC1, BC3, and BC7 */ + bool success = true; + + // Intermediate storage for extracted blocks. This is mainly for convenience + // so that we do not have to repeat logic for extracting and potentially + // padding a block of 4x4 RGBA pixels + const uint32_t rgba_pitch = 4 * 4; + uint8_t rgba[4 * rgba_pitch]; /* 4 x 4 x 4 */ + auto& ert_p = params.ert_p; + + if (ert_p.m_lambda <= 0.0f) return KTX_SUCCESS; + + switch (bcn) { + case KTX_BCN_COMPRESSION_BC1: { + CHECK_SIZES(BC1_NCHANNELS, BC1_BLOCK_SIZE); + + ert_p.m_color_weights[3] = 0; + + if (params.auto_smooth_block_max_mse_scale) { + ert_p.m_smooth_block_max_mse_scale = + lerp(15.0f, 50.0f, std::min(1.0f, ert_p.m_lambda / 8.0f)); + } + + if (params.ultra_smooth_block_mse_handling) + block_rgb_mse_scales = + compute_block_rgb_mse_scales(unpacked_img, width, height, BC1_NCHANNELS, + ert_p.m_smooth_block_max_mse_scale, ert_p.m_lambda); + + std::vector block_pixels(nBlocksTotal * 4 * 4); + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + // Extract block (non-multiple-of-4 texture dimensions are handled). + extract_block(rgba, unpacked_img, x, y, width, height, BC1_NCHANNELS); + // Now flatten the extracted block into block_pixels and add alpha because BC1 input + // is RGB (without the A). Unpacked BC1 blocks on the other hand are RGBX + const uint8_t* p_src = rgba; + ert::color_rgba* p_dst_rgbx = block_pixels.data() + x * 4 + y * 4 * nBlocksX; + for (int i = 0; i < 4 * 4; ++i) { + p_dst_rgbx->m_comps[0] = p_src[0]; + p_dst_rgbx->m_comps[1] = p_src[1]; + p_dst_rgbx->m_comps[2] = p_src[2]; + p_dst_rgbx->m_comps[3] = 255u; + p_dst_rgbx += 1; + p_src += BC1_NCHANNELS; + } // i + } + } + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + rdo_bc1_workload workload( + width, height, packed_img, block_pixels.data(), ert_p, + params.ultra_smooth_block_mse_handling ? block_rgb_mse_scales.data() : nullptr); + launchThreads(threads, rdo_bc1_workload_runner, &workload); + success = workload.m_success; + +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_rdo_time = + std::chrono::duration_cast(finish - start).count(); + print_rdo_params(params); + std::cout << "total rdo time (s): " << total_rdo_time / 1000.0f << '\n'; + std::cout << "total nbr modified blocks (%): " + << (100.0f * workload.m_total_modified_rgb.load()) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_rgb) / nBlocksTotal << '\n'; + std::cout << "total second matches: " << workload.m_total_second_matches_rgb << '\n'; +#endif + + break; + } // BC1 + + case KTX_BCN_COMPRESSION_BC3: { + CHECK_SIZES(BC3_NCHANNELS, BC3_BLOCK_SIZE); + + ert_p.m_color_weights[3] = 0; + + ert::reduce_entropy_params ert_p_a(ert_p); + ert_p_a.m_color_weights[1] = 0; + ert_p_a.m_color_weights[2] = 0; + ert_p_a.m_color_weights[3] = 0; + + if (params.auto_smooth_block_max_mse_scale) { + ert_p.m_smooth_block_max_mse_scale = + lerp(15.0f, 50.0f, std::min(1.0f, ert_p.m_lambda / 8.0f)); + ert_p_a.m_smooth_block_max_mse_scale = + lerp(10.0f, 30.0f, std::min(1.0f, ert_p.m_lambda / 4.0f)); + } + + if (params.ultra_smooth_block_mse_handling) + block_rgb_mse_scales = + compute_block_rgb_mse_scales(unpacked_img, width, height, BC3_NCHANNELS, + ert_p.m_smooth_block_max_mse_scale, ert_p.m_lambda); + + std::vector block_pixels_rgbx(nBlocksTotal * 4 * 4); + std::vector block_pixels_axxx(block_pixels_rgbx.size()); + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + // Extract block (non-multiple-of-4 texture dimensions are handled). + extract_block(rgba, unpacked_img, x, y, width, height, BC3_NCHANNELS); + // Now flatten the extracted block into block_pixels + const uint8_t* pSrc = rgba; + ert::color_rgba* pDstRGB = block_pixels_rgbx.data() + x * 4 + y * 4 * nBlocksX; + ert::color_rgba* pDstA = block_pixels_axxx.data() + x * 4 + y * 4 * nBlocksX; + for (int i = 0; i < 4 * 4; ++i) { + pDstRGB->m_comps[0] = pSrc[0]; + pDstRGB->m_comps[1] = pSrc[1]; + pDstRGB->m_comps[2] = pSrc[2]; + pDstRGB->m_comps[3] = 0; + pDstA->m_comps[0] = pSrc[3]; // alpha + pDstA->m_comps[1] = 0; + pDstA->m_comps[2] = 0; + pDstA->m_comps[3] = 0; + pSrc += BC3_NCHANNELS; /* pSrc += 4 */ + ++pDstRGB; + ++pDstA; + } // i + } + } + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + rdo_bc3_workload workload( + width, height, packed_img, block_pixels_rgbx.data(), block_pixels_axxx.data(), ert_p, + ert_p_a, + params.ultra_smooth_block_mse_handling ? block_rgb_mse_scales.data() : nullptr); + launchThreads(threads, rdo_bc3_workload_runner, &workload); + success = workload.m_success; + +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_rdo_time = + std::chrono::duration_cast(finish - start).count(); + print_rdo_params(params); + std::cout << "total rdo time (s): " << total_rdo_time / 1000.0f << '\n'; + std::cout << "total nbr modified BC1 RGB blocks (%): " + << (100.0f * workload.m_total_modified_rgb) / nBlocksTotal << '\n'; + std::cout << "total nbr rgb modified BC4 A blocks (%): " + << (100.0f * workload.m_total_modified_a) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth BC1 RGB blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_rgb) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth BC4 A blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_a) / nBlocksTotal << '\n'; + std::cout << "total second matches for BC1 RGB blocks: " + << workload.m_total_second_matches_rgb << '\n'; + std::cout << "total second matches for BC4 A blocks: " << workload.m_total_second_matches_a + << '\n'; +#endif + + break; + } // BC3 + + case KTX_BCN_COMPRESSION_BC4: { + CHECK_SIZES(BC4_NCHANNELS, BC4_BLOCK_SIZE); + + // only RDO R channel since this is BC4 + ert_p.m_color_weights[1] = 0; + ert_p.m_color_weights[2] = 0; + ert_p.m_color_weights[3] = 0; + + if (params.auto_smooth_block_max_mse_scale) { + ert_p.m_smooth_block_max_mse_scale = + lerp(10.0f, 30.0f, std::min(1.0f, ert_p.m_lambda / 4.0f)); + } + + const uint32_t r_pitch = 4 * BC4_NCHANNELS; // 4 x 1 + uint8_t r[4 * r_pitch]; // 4 x 4 x 1 + + std::vector block_pixels_rxxx(nBlocksTotal * 4 * 4); + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + // Extract block (non-multiple-of-4 texture dimensions are handled). + extract_block(r, unpacked_img, x, y, width, height, BC4_NCHANNELS); + // Now flatten the extracted block into block_pixels + ert::color_rgba* pDst = block_pixels_rxxx.data() + x * 4 + y * 4 * nBlocksX; + const uint8_t* pSrc = r; + for (int i = 0; i < 4 * 4; ++i) { + pDst->m_comps[0] = pSrc[0]; // alpha + pDst->m_comps[1] = 0; + pDst->m_comps[2] = 0; + pDst->m_comps[3] = 0; + pSrc += BC4_NCHANNELS; /* pSrc += 1 */ + ++pDst; + } // i + } + } + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + rdo_bc4_workload workload(width, height, packed_img, block_pixels_rxxx.data(), ert_p); + launchThreads(threads, rdo_bc4_workload_runner, &workload); + success = workload.m_success; + +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_rdo_time = + std::chrono::duration_cast(finish - start).count(); + print_rdo_params(params); + std::cout << "total rdo time (s): " << total_rdo_time / 1000.0f << '\n'; + std::cout << "total nbr modified blocks (%): " + << (100.0f * workload.m_total_modified_r) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_r) / nBlocksTotal << '\n'; + std::cout << "total second matches: " << workload.m_total_second_matches_r << '\n'; +#endif + + break; + } // BC4 + + case KTX_BCN_COMPRESSION_BC5: { + // One BC4 block for R followed by one BC4 block for G + CHECK_SIZES(BC5_NCHANNELS, BC5_BLOCK_SIZE); + + ert_p.m_color_weights[1] = 0; + ert_p.m_color_weights[2] = 0; + ert_p.m_color_weights[3] = 0; + + if (params.auto_smooth_block_max_mse_scale) { + ert_p.m_smooth_block_max_mse_scale = + lerp(10.0f, 30.0f, std::min(1.0f, ert_p.m_lambda / 4.0f)); + } + + std::vector block_pixels_rxxx(nBlocksTotal * 4 * 4); + std::vector block_pixels_gxxx(block_pixels_rxxx.size()); + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + // Extract block (non-multiple-of-4 texture dimensions are handled). + extract_block(rgba, unpacked_img, x, y, width, height, BC5_NCHANNELS); + // Now flatten the extracted block into block_pixels + const uint8_t* pSrc = rgba; + ert::color_rgba* pDstR = block_pixels_rxxx.data() + x * 4 + y * 4 * nBlocksX; + ert::color_rgba* pDstG = block_pixels_gxxx.data() + x * 4 + y * 4 * nBlocksX; + for (int i = 0; i < 4 * 4; ++i) { + pDstR->m_comps[0] = pSrc[0]; // R + pDstR->m_comps[1] = 0; + pDstR->m_comps[2] = 0; + pDstR->m_comps[3] = 0; + pDstG->m_comps[0] = pSrc[1]; // G + pDstG->m_comps[1] = 0; + pDstG->m_comps[2] = 0; + pDstG->m_comps[3] = 0; + pSrc += BC5_NCHANNELS; /* pSrc += 2 */ + ++pDstR; + ++pDstG; + } // i + } + } + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + rdo_bc5_workload workload(width, height, packed_img, block_pixels_rxxx.data(), + block_pixels_gxxx.data(), ert_p, ert_p); + launchThreads(threads, rdo_bc5_workload_runner, &workload); + + success = workload.m_success; + +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_rdo_time = + std::chrono::duration_cast(finish - start).count(); + print_rdo_params(params); + std::cout << "total rdo time (s): " << total_rdo_time / 1000.0f << '\n'; + std::cout << "total nbr modified BC4 R blocks (%): " + << (100.0f * workload.m_total_modified_r) / nBlocksTotal << '\n'; + std::cout << "total nbr modified BC4 G blocks (%): " + << (100.0f * workload.m_total_modified_g) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth BC4 R blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_r) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth BC4 G blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_g) / nBlocksTotal << '\n'; + std::cout << "total second matches for BC4 R blocks: " << workload.m_total_second_matches_r + << '\n'; + std::cout << "total second matches for BC4 G blocks: " << workload.m_total_second_matches_g + << '\n'; +#endif + + break; + } // BC5 + + // TODO: add BC6H RDO support. Requires changes to ert::reduce_entropy to + // handle half float channels (i.e., f16) instead of 1 byte per channel. + + case KTX_BCN_COMPRESSION_BC7: { + CHECK_SIZES(BC7_NCHANNELS, BC7_BLOCK_SIZE); + + // Attempt to compute a decent conservative smooth block MSE max scaling + // factor. No single smooth block scale setting can work for all + // textures (unless it's ridiuclously large, killing efficiency). + if (params.auto_smooth_block_max_mse_scale) { + ert_p.m_smooth_block_max_mse_scale = + lerp(15.0f, 50.0f, std::min(1.0f, ert_p.m_lambda / 4.0f)); + } + + if (params.ultra_smooth_block_mse_handling) + block_rgb_mse_scales = + compute_block_rgb_mse_scales(unpacked_img, width, height, BC7_NCHANNELS, + ert_p.m_smooth_block_max_mse_scale, ert_p.m_lambda); + + // Source image data need to be laid out as blocks (i.e., 1st raw of 1s + // block, then 2nd raw of 1st block, etc. - Not: 1st raw of 1st block + // then 1st raw of 2nd block, etc.). This is what the main RDO function + // ert::reduce_entropy expects. + std::vector block_pixels_rgba(nBlocksTotal * 4 * 4); + for (uint32_t y = 0; y < height; y += 4) { + for (uint32_t x = 0; x < width; x += 4) { + // Extract block (non-multiple-of-4 texture dimensions are handled). + extract_block(rgba, unpacked_img, x, y, width, height, BC7_NCHANNELS); + // Now flatten the extracted block into block_pixels + ert::color_rgba* p_dst = block_pixels_rgba.data() + x * 4 + y * 4 * nBlocksX; + for (uint32_t py{0}; py < 4; ++py) { + memcpy(p_dst + py * 4, rgba + py * rgba_pitch, 4 * BC7_NCHANNELS); + } + } + } + +#if DEBUG_PRINT_STATS + auto start = std::chrono::high_resolution_clock::now(); +#endif + + rdo_bc7_workload workload( + width, height, packed_img, block_pixels_rgba.data(), ert_p, + params.ultra_smooth_block_mse_handling ? block_rgb_mse_scales.data() : nullptr); + launchThreads(threads, rdo_bc7_workload_runner, &workload); + success = workload.m_success; + +#if DEBUG_PRINT_STATS + auto finish = std::chrono::high_resolution_clock::now(); + const auto total_rdo_time = + std::chrono::duration_cast(finish - start).count(); + print_rdo_params(params); + std::cout << "total rdo time (s): " << total_rdo_time / 1000.0f << '\n'; + std::cout << "total nbr modified blocks (%): " + << (100.0f * workload.m_total_modified_rgba.load()) / nBlocksTotal << '\n'; + std::cout << "total nbr smooth blocks (%): " + << (100.0f * workload.m_total_smooth_blocks_rgba) / nBlocksTotal << '\n'; + std::cout << "total second matches: " << workload.m_total_second_matches_rgba << '\n'; +#endif + + break; + } // BC7 + + default: + return KTX_INVALID_VALUE; // not a supported/valid BCn color bcn + } + + return success ? KTX_SUCCESS : KTX_INVALID_OPERATION; +} + +//************************************************************************ +//* BCn encoder function * +//************************************************************************ + +/** + * @memberof ktxTexture2 + * @ingroup writer + * @~English + * @brief Compress a ktx texture with uncompressed images to provided BCn + * format. Currently, only BC1, BC3, BC4, BC5, BC6HU*, and BC7 target + * formats are supported. Punch-through alpha for BC1 is not supported. + * + * The images are encoded to provided BCn block-compressed format. The + * encoded images replace the original images and the texture's fields + * including the DFD are modified to reflect the new state. + * + * Such textures can be directly uploaded to a GPU via a graphics API. + * + * Encoding non-multiple-of-4 texture dimensions is supported. Block + * pixels/texels that are out of the texture's dimensions are + * simply filled via a clamp-to-edge strategy. This strategy is easier + * for the encoder to handle since no abrupt changes are introduced in + * image boundaries. + * + * @param [in] This pointer to the ktxTexture2 object of interest. + * @param [in] params pointer to BCn parameters object. These + * parameters, among other things, control the + * compression quality and whether to apply an RDO + * post-processing step to significantly reduce + * bit rate when @p This texture gets supercompressed + * with Deflate (i.e., Zlib/ZSTD). + * + * @return KTX_SUCCESS on success, other KTX_* enum values on + * error. + * + * @exception KTX_INVALID_VALUE + * @p params is @c NULL or used with incompatible + * @p params. + * @exception KTX_FILE_DATA_ERROR + * The texture's supercompression scheme is invalid. + * @exception KTX_INVALID_OPERATION + * The texture's images are already in a block compressed + * format (i.e., @c This->isCompressed is @c true). + * @exception KTX_INVALID_OPERATION + * The texture image's format is a packed format (e.g., + * RGB565). + * @exception KTX_INVALID_OPERATION + * The texture's images are not 2D. Only 2D images can be + * block compressed. + * @exception KTX_INVALID_OPERATION + * The texture does not contain any data (i.e., + * @c This->pData is @c NULL and there is no pending data + * load). + * @exception KTX_INVALID_OPERATION + * VkFormat of @p This texture is not supported or does + * not match that of the set BCn color model. + * @exception KTX_INVALID_OPERATION + * @c This->generateMipmaps is set. + * @exception KTX_OUT_OF_MEMORY + * Not enough memory to carry out compression. + * @exception KTX_UNSUPPORTED_FEATURE + * Unsupported or not-yet-to-be-supported target BCn format + * (e.g., BC1A, BC2, and BC6HS). + * @exception KTX_UNSUPPORTED_FEATURE + * Unsupported supercompression scheme. + */ +extern "C" KTX_error_code +ktxTexture2_CompressBCnEx(ktxTexture2* This, ktxBCnParams* params) { + assert(This->classId == ktxTexture2_c && "Only support ktx2 BCn."); + + if (!params) return KTX_INVALID_VALUE; + + if (params->structSize != sizeof(struct ktxBCnParams)) return KTX_INVALID_VALUE; + + // Set fields to their actual default values if they are zero'ed + ktxBCnParams defaulted_params{*params}; + set_default_bcn_params_fields(defaulted_params); + + if (This->generateMipmaps) return KTX_INVALID_OPERATION; + + if (This->numDimensions != 2) return KTX_INVALID_OPERATION; + + if (This->supercompressionScheme == KTX_SS_BASIS_LZ || + This->supercompressionScheme == KTX_SS_UASTC_HDR_6x6_INTERMEDIATE) { + return KTX_FILE_DATA_ERROR; // Not a valid file. + } + // Safety check. + if (This->supercompressionScheme > KTX_SS_END_RANGE) { + return KTX_UNSUPPORTED_FEATURE; // Unsupported scheme. + } + + if (This->pData == NULL) { + if (ktxTexture_isActiveStream((ktxTexture*)This)) { + // Load pending. Complete it. + auto result = ktxTexture2_LoadImageData(This, NULL, 0); + if (result != KTX_SUCCESS) return result; + } else { + return KTX_INVALID_OPERATION; // No data to decode. + } + } + + // Only non-block compressed formats can be encoded into a BCn format. + if (This->isCompressed) return KTX_INVALID_OPERATION; + + if (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT) + return KTX_INVALID_OPERATION; + + // Basic descriptor block begins after the total size field. + const uint32_t* BDB = This->pDfd + 1; + uint32_t alphaMode = KHR_DFDVAL(BDB, FLAGS); + [[maybe_unused]] uint32_t transfer = KHR_DFDVAL(BDB, TRANSFER); + [[maybe_unused]] khr_df_model_e expected_color_model; + uint32_t nchannels; + uint32_t blocksize_in_bytes; + VkFormat compressedVkFormat; + ktx_error_code_e result; +#if DEBUG_PRINT_STATS + bool hdr_one_or_more_nan_values_msg = false; + bool hdr_one_or_more_inf_values_msg = false; + bool hdr_one_or_more_signed_values_msg = false; +#endif + + switch (defaulted_params.bcn) { + case KTX_BCN_COMPRESSION_BC1: + switch (This->vkFormat) { + case VK_FORMAT_R8G8B8_UNORM: + compressedVkFormat = VK_FORMAT_BC1_RGB_UNORM_BLOCK; + break; + case VK_FORMAT_R8G8B8_SRGB: + compressedVkFormat = VK_FORMAT_BC1_RGB_SRGB_BLOCK; + break; + default: + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC1 + } + nchannels = BC1_NCHANNELS; + blocksize_in_bytes = BC1_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC1A; + rgbcx::init(rgbcx::bc1_approx_mode::cBC1Ideal); + break; + + // bc7enc's BC1 encoder does not support punch-through alpha + // BC2 encoder is not yet implemented + // BC6HS is not supported by Basis Universal's BC6 encoder + case KTX_BCN_COMPRESSION_BC1A: + case KTX_BCN_COMPRESSION_BC2: + case KTX_BCN_COMPRESSION_BC6HS: + return KTX_UNSUPPORTED_FEATURE; + + case KTX_BCN_COMPRESSION_BC3: + switch (This->vkFormat) { + case VK_FORMAT_R8G8B8A8_UNORM: + compressedVkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + break; + case VK_FORMAT_R8G8B8A8_SRGB: + compressedVkFormat = VK_FORMAT_BC3_SRGB_BLOCK; + break; + default: + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC3 + } + nchannels = BC3_NCHANNELS; + blocksize_in_bytes = BC3_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC3; + rgbcx::init(rgbcx::bc1_approx_mode::cBC1Ideal); + break; + + case KTX_BCN_COMPRESSION_BC4: + if (This->vkFormat != VK_FORMAT_R8_UNORM) + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC4 + compressedVkFormat = VK_FORMAT_BC4_UNORM_BLOCK; + nchannels = BC4_NCHANNELS; + blocksize_in_bytes = BC4_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC4; + rgbcx::init(rgbcx::bc1_approx_mode::cBC1Ideal); + break; + + case KTX_BCN_COMPRESSION_BC5: + if (This->vkFormat != VK_FORMAT_R8G8_UNORM) + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC5 + compressedVkFormat = VK_FORMAT_BC5_UNORM_BLOCK; + nchannels = BC5_NCHANNELS; + blocksize_in_bytes = BC5_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC5; + rgbcx::init(rgbcx::bc1_approx_mode::cBC1Ideal); + break; + + case KTX_BCN_COMPRESSION_BC6HU: + switch (This->vkFormat) { + case VK_FORMAT_R16G16B16_SFLOAT: + compressedVkFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK; + break; + default: + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC6H + } + nchannels = BC6H_NCHANNELS; + blocksize_in_bytes = BC6H_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC6H; + basist::basisu_transcoder_init(); + break; + + case KTX_BCN_COMPRESSION_BC7: + switch (This->vkFormat) { + case VK_FORMAT_R8G8B8A8_UNORM: + compressedVkFormat = VK_FORMAT_BC7_UNORM_BLOCK; + break; + case VK_FORMAT_R8G8B8A8_SRGB: + compressedVkFormat = VK_FORMAT_BC7_SRGB_BLOCK; + break; + default: + return KTX_INVALID_OPERATION; // Not a valid decompressed vkformat for BC7 + } + nchannels = BC7_NCHANNELS; + blocksize_in_bytes = BC7_BLOCK_SIZE; + expected_color_model = khr_df_model_e::KHR_DF_MODEL_BC7; + basist::basisu_transcoder_init(); + break; + + default: + return KTX_INVALID_VALUE; // Provided color bcn is not BCn + } + + ktx_uint32_t thread_count = defaulted_params.threadCount; + if (thread_count < 1) thread_count = 1; + + // This->numLevels = 0 not allowed for block compressed formats + // But just in case make sure it's not zero + This->numLevels = MAX(1, This->numLevels); + + // Create a prototype texture to use for calculating sizes in the target + // format and, as useful side effects, provide us with a properly sized + // data allocation and the DFD for the target format. + ktxTextureCreateInfo createInfo; + createInfo.glInternalformat = 0; + createInfo.vkFormat = compressedVkFormat; + createInfo.baseWidth = This->baseWidth; + createInfo.baseHeight = This->baseHeight; + createInfo.baseDepth = This->baseDepth; + createInfo.generateMipmaps = This->generateMipmaps; + createInfo.isArray = This->isArray; + createInfo.numDimensions = This->numDimensions; + createInfo.numFaces = This->numFaces; + createInfo.numLayers = This->numLayers; + createInfo.numLevels = This->numLevels; + createInfo.pDfd = nullptr; + + ktxTexture2* prototype = nullptr; + result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &prototype); + if (result != KTX_SUCCESS) { + assert(result == KTX_OUT_OF_MEMORY && "Out of memory allocating texture."); + return result; + } + assert(prototype->dataSize && "Prototype texture size not initialized.\n"); + if (!prototype->pData) return KTX_OUT_OF_MEMORY; + + bcn_compression_workload work; + work.nchannels = nchannels; + work.params = defaulted_params; + + // ASTC encoder does this loop in reverse to (probably) avoid having to add + // levelDataOffsetOut (which has no additional cost whatsoever) + for (ktx_uint32_t level = 0; level < This->numLevels; ++level) { + // dims for current level + nbr blocks + const uint32_t width = MAX(1, This->baseWidth >> level); + const uint32_t height = MAX(1, This->baseHeight >> level); + const uint32_t depth = MAX(1, This->baseDepth >> level); + const uint32_t num_blocks_x = (width + 3) / 4; + const uint32_t num_blocks_y = (height + 3) / 4; + const uint32_t num_blocks_total = num_blocks_x * num_blocks_y; + + ktx_size_t levelImageSizeIn = 0; + ktx_size_t levelImageSizeOut = 0; + const ktx_uint32_t levelImages = This->numLayers * This->numFaces * depth; + + levelImageSizeIn = + ktxTexture_calcImageSize(ktxTexture(This), level, KTX_FORMAT_VERSION_TWO); + levelImageSizeOut = + ktxTexture_calcImageSize(ktxTexture(prototype), level, KTX_FORMAT_VERSION_TWO); + + assert(levelImageSizeIn == width * height * nchannels * + ((defaulted_params.bcn == KTX_BCN_COMPRESSION_BC6HU || + defaulted_params.bcn == KTX_BCN_COMPRESSION_BC6HS) + ? 2 + : 1) && + "Probably non-compact data (i.e., some padding)"); + assert(levelImageSizeOut == num_blocks_x * num_blocks_y * blocksize_in_bytes && + "Probably non-compact data (i.e., some padding) for BCn compressed texture"); + + const size_t levelDataOffsetIn = ktxTexture2_levelDataOffset(This, level); + const size_t levelDataOffsetOut = ktxTexture2_levelDataOffset(prototype, level); + + // Points to start of raw source/destination compressed blocks image + // data within this miplevl (e.g., for 3D texture and for miplevel 0, + // this initially points to start of first depth slice image data, then + // this gets update to point to next slice and so on until we exit this + // loop and this gets initialized again to point to start of depth slice + // 0 withing next mip level) + /* const */ ktx_uint8_t* pSrcLevelImage = This->pData + levelDataOffsetIn; + + // points to start of destination image within this miplevel + ktx_uint8_t* pDstLevelImage = prototype->pData + levelDataOffsetOut; + + work.width = width; + work.height = height; + + for (uint32_t image = 0; image < levelImages; image++) { + work.data_in = pSrcLevelImage; + work.data_out = pDstLevelImage; + + // HDR input may require some cleanup for basisu's bc6hu encoder + if (defaulted_params.bcn == KTX_BCN_COMPRESSION_BC6HU) { + // TODO: Basisu's outputs warning if HDR pixels are cleaned up. + // Should we also do that in here? + [[maybe_unused]] auto hdr_cleanup_res = + clean_hdr_image(pSrcLevelImage, width, height); +#if DEBUG_PRINT_STATS + if (hdr_cleanup_res.one_or_more_nan_values && !hdr_one_or_more_nan_values_msg) { + std::cout << "One or more input pixels was NaN, setting to 0." << '\n'; + hdr_one_or_more_nan_values_msg = false; + } + + if (hdr_cleanup_res.one_or_more_inf_values && !hdr_one_or_more_inf_values_msg) { + std::cout << "One or more input pixels was INF, setting to 0." << '\n'; + hdr_one_or_more_inf_values_msg = false; + } + + if (hdr_cleanup_res.one_or_more_signed_values && + !hdr_one_or_more_signed_values_msg) { + std::cout << "One or more input pixels was negative -- setting these pixel " + "components to 0 because BC6HU HDR doesn't support signed values." + << '\n'; + hdr_one_or_more_signed_values_msg = false; + } + + // Since each image within each mip level may have + // been scaled by a different value, print this + // message for each of one of these images. + // TODO: any better ideas here? (this looks ugly) + if (hdr_cleanup_res.hdr_image_scale != 1.0) { + std::cout + << "Warning: The input HDR image's maximum used float for mip level " + << level << " and layer/slice/face " << image + << " is too high to encode as BC6HU HDR. The image's components have " + "been linearly scaled by multiplying by " + << 1.0 / hdr_cleanup_res.hdr_image_scale + << ". The decoded/sampled ASTC HDR texture will have to be scaled up by " + << hdr_cleanup_res.hdr_image_scale + << ". See the \"HDRScale\" KTX2 key value field" << '\n'; + } +#endif + } + + // the actual encoding occurs here + launchThreads(thread_count, compression_workload_runner, &work); + + // post process the encoded blocks using RDO (if enabled) + if (defaulted_params.bcnRDO) { + // Don't naively just take provided thread count and divide RDO over those threads + // => this will certainly result in non-deterministic output (+ change of actual + // used window size) + uint32_t thread_count_rdo = 1; + if (!defaulted_params.bcnRDONoMultithreading) { + thread_count_rdo = ert::adjust_num_threads_for_deterministic_rdo( + thread_count, defaulted_params.bcnRDODictSize, blocksize_in_bytes, + num_blocks_total); + } + assert(thread_count_rdo <= thread_count); + assert((num_blocks_total >= defaulted_params.bcnRDODictSize / blocksize_in_bytes) + ? (num_blocks_total / thread_count_rdo >= + defaulted_params.bcnRDODictSize / blocksize_in_bytes) + : thread_count_rdo == 1); + rdo_params rdo_p; + rdo_p.ert_p.m_lambda = defaulted_params.bcnRDOQualityScalar; + rdo_p.ert_p.m_lookback_window_size = defaulted_params.bcnRDODictSize; + rdo_p.ert_p.m_smooth_block_max_mse_scale = + defaulted_params.bcnRDOMaxSmoothBlockErrorScale; + rdo_p.ert_p.m_max_smooth_block_std_dev = + defaulted_params.bcnRDOMaxSmoothBlockStdDev; + rdo_p.ert_p.m_try_two_matches = !defaulted_params.bcnRDOTryOneMatch; + rdo_p.ert_p.m_skip_zero_mse_blocks = defaulted_params.bcnRDOSkipZeroMSEBlocks; + rdo_p.auto_smooth_block_max_mse_scale = + defaulted_params.bcnRDOMaxSmoothBlockErrorScale == 0; + rdo_p.ultra_smooth_block_mse_handling = + !defaulted_params.bcnRDONoUltrasmoothBlockHandling; + auto res = postprocess_rdo_bcn( + pSrcLevelImage, (size_t)width * height * nchannels, pDstLevelImage, + (size_t)num_blocks_x * num_blocks_y * blocksize_in_bytes, rdo_p, + defaulted_params.bcn, width, height, thread_count_rdo); + if (res != KTX_SUCCESS) { + ktxTexture2_Destroy(prototype); + return res; + } + } + + pDstLevelImage += levelImageSizeOut; // next destination image within this miplevel + pSrcLevelImage += levelImageSizeIn; // next source image within this miplevel + } + } + + // cast to uint32_t so that Visual Studio stops complaining ... + assert(KHR_DFDVAL(prototype->pDfd + 1, MODEL) == (uint32_t)expected_color_model && + "Invalid dfd generated for BCn image\n"); + + assert((transfer == KHR_DF_TRANSFER_SRGB + ? KHR_DFDVAL(prototype->pDfd + 1, TRANSFER) == KHR_DF_TRANSFER_SRGB && + KHR_DFDVAL(prototype->pDfd + 1, PRIMARIES) == KHR_DF_PRIMARIES_SRGB + : true) && + "Not a valid sRGB image\n"); + +// Fix up the current (This) texture (this is copied from ASTC encoder - see +// astc_codec.cpp) +#undef DECLARE_PRIVATE +#undef DECLARE_PROTECTED +#define DECLARE_PRIVATE(n, t2) ktxTexture2_private& n = *(t2->_private) +#define DECLARE_PROTECTED(n, t2) ktxTexture_protected& n = *(t2->_protected) + + DECLARE_PROTECTED(thisPrtctd, This); + DECLARE_PRIVATE(protoPriv, prototype); + DECLARE_PROTECTED(protoPrtctd, prototype); + memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize, sizeof(ktxFormatSize)); + This->vkFormat = prototype->vkFormat; + This->isCompressed = prototype->isCompressed; + This->supercompressionScheme = KTX_SS_NONE; + This->_private->_requiredLevelAlignment = protoPriv._requiredLevelAlignment; + + // Copy typesize otherwise `ktx info` and `ktx validate` fails and + // complains that block-compressed types should have a typesize of 1. + This->_protected->_typeSize = prototype->_protected->_typeSize; + + // Copy the levelIndex from the prototype to This. + memcpy(This->_private->_levelIndex, protoPriv._levelIndex, + This->numLevels * sizeof(ktxLevelIndexEntry)); + + // Move the DFD and data from the prototype to This. + free(This->pDfd); + This->pDfd = prototype->pDfd; + prototype->pDfd = 0; + free(This->pData); + This->pData = prototype->pData; + This->dataSize = prototype->dataSize; + prototype->pData = 0; + prototype->dataSize = 0; + + ktxTexture2_Destroy(prototype); + + KHR_DFDSETVAL(This->pDfd + 1, FLAGS, alphaMode); // Restore alphaMode flags + return KTX_SUCCESS; +} + +#else // !KTX_FEATURE_WRITE + +extern "C" KTX_error_code +ktxTexture2_CompressBCnEx(ktxTexture2*, ktxBCnParams*) { + return KTX_INVALID_OPERATION; +} + +// extern "C" KTX_error_code +// ktxTexture2_CompressBCn(ktxTexture2*, ktx_uint32_t) { +// return KTX_INVALID_OPERATION; +// } + +#endif // KTX_FEATURE_WRITE diff --git a/lib/src/multithreading.cpp b/lib/src/multithreading.cpp new file mode 100644 index 0000000000..23ec4dd783 --- /dev/null +++ b/lib/src/multithreading.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2026 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "multithreading.h" + +#if defined(_WIN32) && !defined(WIN32_HAS_PTHREADS) +int +pthread_create(pthread_t* thread, const pthread_attr_t* attribs, void* (*threadfunc)(void*), + void* thread_arg) { + (void)attribs; + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wcast-function-type-mismatch" + #endif + LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)(threadfunc); + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + *thread = CreateThread(nullptr, 0, func, thread_arg, 0, nullptr); + return 0; +} + +int +pthread_join(pthread_t thread, void** value) { + (void)value; + WaitForSingleObject(thread, INFINITE); + return 0; +} +#endif + +/** + * @internal + * @~English + * @brief Helper function to translate thread entry points. + * + * Convert a (void*) thread entry to an (int, void*) thread entry, where the + * integer contains the thread ID in the thread pool. + * + * @param p The thread launch helper payload. + */ +void* +launchThreadsHelper(void* p) { + LaunchDesc* ltd = (LaunchDesc*)p; + ltd->func(ltd->threadCount, ltd->threadId, ltd->payload); + return nullptr; +} + +void +launchThreads(int threadCount, void (*func)(int, int, void*), void* payload) { + // Directly execute single threaded workloads on this thread + if (threadCount <= 1) { + func(1, 0, payload); + return; + } + + // Otherwise spawn worker threads + LaunchDesc* threadDescs = new LaunchDesc[threadCount]; + for (int i = 0; i < threadCount; i++) { + threadDescs[i].threadCount = threadCount; + threadDescs[i].threadId = i; + threadDescs[i].payload = payload; + threadDescs[i].func = func; + + pthread_create(&(threadDescs[i].threadHandle), nullptr, launchThreadsHelper, + (void*)&(threadDescs[i])); + } + + // ... and then wait for them to complete + for (int i = 0; i < threadCount; i++) { + pthread_join(threadDescs[i].threadHandle, nullptr); + } + + delete[] threadDescs; +} diff --git a/lib/src/multithreading.h b/lib/src/multithreading.h new file mode 100644 index 0000000000..48c4387ab5 --- /dev/null +++ b/lib/src/multithreading.h @@ -0,0 +1,45 @@ +/* + * Copyright 2026 The Khronos Group Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef MULTITHREADING_H +#define MULTITHREADING_H + +#if !defined(_WIN32) || defined(WIN32_HAS_PTHREADS) + #include +#else + // Provide pthreads support on windows + #define WIN32_LEAN_AND_MEAN + #include + +typedef HANDLE pthread_t; +typedef int pthread_attr_t; + +int pthread_create(pthread_t* thread, const pthread_attr_t* attribs, void* (*threadfunc)(void*), + void* thread_arg); +int pthread_join(pthread_t thread, void** value); +#endif + +/** + * @internal + * @~English + * @brief Worker thread helper payload for launchThreads. + */ +struct LaunchDesc { + /** The native thread handle. */ + pthread_t threadHandle; + /** The total number of threads in the thread pool. */ + int threadCount; + /** The thread index in the thread pool. */ + int threadId; + /** The user thread function to execute. */ + void (*func)(int, int, void*); + /** The user thread payload. */ + void* payload; +}; + +void* launchThreadsHelper(void* p); +void launchThreads(int threadCount, void (*func)(int, int, void*), void* payload); + +#endif diff --git a/lib/src/strings.c b/lib/src/strings.c index 92f5e7f549..ec6b73f9b0 100644 --- a/lib/src/strings.c +++ b/lib/src/strings.c @@ -39,7 +39,8 @@ static const char* const errorStrings[] = { "Feature not included in in-use library or not yet implemented.", /* KTX_UNSUPPORTED_FEATURE */ "Library dependency (OpenGL or Vulkan) not linked into application.", /* KTX_LIBRARY_NOT_LINKED */ "Decompressed byte count does not match expected byte size", /* KTX_DECOMPRESS_LENGTH_ERROR */ - "Checksum mismatch when decompressing" /* KTX_DECOMPRESS_CHECKSUM_ERROR */ + "Checksum mismatch when decompressing", /* KTX_DECOMPRESS_CHECKSUM_ERROR */ + "Decompression failure", /* KTX_DECOMPRESS_FAILURE */ }; /* This will cause compilation to fail if number of messages and codes doesn't match */ typedef int errorStrings_SIZE_ASSERT[sizeof(errorStrings) / sizeof(char*) - 1 == KTX_ERROR_MAX_ENUM]; diff --git a/scripts/install_macos.sh b/scripts/install_macos.sh index a5006f3028..7327776131 100755 --- a/scripts/install_macos.sh +++ b/scripts/install_macos.sh @@ -19,7 +19,6 @@ VULKAN_INSTALL_DIR=${VULKAN_INSTALL_DIR:-~/VulkanSDK} git lfs install git lfs version -gem install xcpretty if [[ (-n "$FEATURE_LOADTESTS" && "$FEATURE_LOADTESTS" != "OFF") || ("$FEATURE_TESTS" = "ON") ]]; then git lfs pull --include=tests/resources diff --git a/tests/cts b/tests/cts index 6d23ae9e52..f0b0727d99 160000 --- a/tests/cts +++ b/tests/cts @@ -1 +1 @@ -Subproject commit 6d23ae9e52cce2ebc6495c4692ec89f632ff70d4 +Subproject commit f0b0727d993356fa9a6a3f1c87901e690ccbcbf3 diff --git a/tests/ktxdiff/ktxdiff_main.cpp b/tests/ktxdiff/ktxdiff_main.cpp index d212f890f9..ddf049267e 100644 --- a/tests/ktxdiff/ktxdiff_main.cpp +++ b/tests/ktxdiff/ktxdiff_main.cpp @@ -12,6 +12,7 @@ #include "astc-encoder/Source/astcenc.h" #include +#include #include #include #include @@ -302,7 +303,7 @@ CompareResult compareAstc(const char* lhs, const char* rhs, std::size_t size, ui } } -bool compare(Texture& lhs, Texture& rhs, float tolerance) { +bool compare(Texture& lhs, Texture& rhs, float tolerance, bool skip_kvd) { const auto vkFormat = static_cast(lhs.header.vkFormat); const auto* bdfd = reinterpret_cast(lhs.dfdData) + 1; const auto componentCount = KHR_DFDSAMPLECOUNT(bdfd); @@ -338,11 +339,24 @@ bool compare(Texture& lhs, Texture& rhs, float tolerance) { if (lhs.transcoded) { // For encoded images the compressed data sizes can differ. // Skip the related checks for header.supercompressionGlobalData and levelIndex - if (std::memcmp(&lhs.header, &rhs.header, sizeof(lhs.header) - sizeof(ktxIndexEntry64)) != 0) + if (std::memcmp(&lhs.header, &rhs.header, + sizeof(lhs.header) - + (sizeof(ktxIndexEntry64) + (skip_kvd ? sizeof(ktxIndexEntry32) : 0))) != 0) return mismatch("Mismatching header"); } else { - if (std::memcmp(&lhs.header, &rhs.header, sizeof(lhs.header)) != 0) - return mismatch("Mismatching header"); + if (skip_kvd) { + // First compare up-to keyValueData member exclusive + if (std::memcmp( + &lhs.header, &rhs.header, + sizeof(lhs.header) - (sizeof(ktxIndexEntry64) + sizeof(ktxIndexEntry32))) != 0) + return mismatch("Mismatching header"); + // Then only compare supercompressionGlobalData + if (std::memcmp(&lhs.header.supercompressionGlobalData, &rhs.header.supercompressionGlobalData, sizeof(lhs.header.supercompressionGlobalData)) != 0) + return mismatch("Mismatching header"); + } else { + if (std::memcmp(&lhs.header, &rhs.header, sizeof(lhs.header)) != 0) + return mismatch("Mismatching header"); + } if (lhs.levelIndexSize != rhs.levelIndexSize) return mismatch("Mismatching levelIndices"); for (uint32_t i = 0; i < lhs.levelIndices.size(); ++i) @@ -353,8 +367,9 @@ bool compare(Texture& lhs, Texture& rhs, float tolerance) { if (lhs.dfdSize != rhs.dfdSize || std::memcmp(lhs.dfdData, rhs.dfdData, lhs.dfdSize) != 0) return mismatch("Mismatching DFD"); - if (lhs.kvdSize != rhs.kvdSize || std::memcmp(lhs.kvdData, rhs.kvdData, lhs.kvdSize) != 0) - return mismatch("Mismatching KVD"); + if (!skip_kvd) + if (lhs.kvdSize != rhs.kvdSize || std::memcmp(lhs.kvdData, rhs.kvdData, lhs.kvdSize) != 0) + return mismatch("Mismatching KVD"); if (!lhs.transcoded) if (lhs.sgdSize != rhs.sgdSize || std::memcmp(lhs.sgdData, rhs.sgdData, lhs.sgdSize) != 0) @@ -414,6 +429,15 @@ bool compare(Texture& lhs, Texture& rhs, float tolerance) { return true; } +inline void print_usage_msg() { + fmt::print( + "Usage: ktxdiff [tolerance] [--skip-kvd]\n" + " For normalized formats tolerance is the normalized absolute value of the acceptable " + "difference.\n" + " For unnormalized formats it is the fraction of the minimum of the values being compared " + "that is acceptable.\n"); +} + /// EXIT CODES: /// 0 - Matching files /// 1 - Mismatching files @@ -423,17 +447,39 @@ int main(int argc, char* argv[]) { if (argc < 3) { fmt::print("Missing input file arguments\n"); - fmt::print("Usage: ktxdiff [tolerance]\n"); - fmt::print(" For normalized formats tolerance is the normalized absolute value of the acceptable difference.\n"); - fmt::print(" For unnormalized formats it is the fraction of the minimum of the values being compared that is acceptable.\n"); + print_usage_msg(); return EXIT_FAILURE; } - const float tolerance = argc > 3 ? std::stof(argv[3]) : 0.05f; + if (argc > 5) { + fmt::print("Too many arguments or/and options\n"); + print_usage_msg(); + return EXIT_FAILURE; + } + + float tolerance = 0.05f; + bool skip_kvd = false; + std::string lhs_path = argv[1], rhs_path = argv[2]; + + if (argc == 4) { + if (std::string(argv[3]) != "--skip-kvd") { + tolerance = std::stof(argv[3]); + } else { + skip_kvd = true; + } + } else if (argc == 5) { + if (std::string(argv[4]) != "--skip-kvd") { + fmt::print("Option '--skip-kvd' should be specified as last option\n"); + print_usage_msg(); + return EXIT_FAILURE; + } + tolerance = std::stof(argv[3]); + skip_kvd = true; + } - Texture lhs(argv[1]); - Texture rhs(argv[2]); - const auto match = compare(lhs, rhs, tolerance); + Texture lhs(lhs_path); + Texture rhs(rhs_path); + const auto match = compare(lhs, rhs, tolerance, skip_kvd); return match ? 0 : 1; } diff --git a/tests/resources/genktx2 b/tests/resources/genktx2 index fad242a4ba..9bb3c8d745 100755 --- a/tests/resources/genktx2 +++ b/tests/resources/genktx2 @@ -122,7 +122,7 @@ fi $ktx create --testrun --format R8G8B8A8_SRGB --assign-tf srgb --encode uastc --zstd 5 input/png/color_grid.png ktx2/color_grid_uastc_zstd_5.ktx2 $ktx create --testrun --format R8G8B8A8_SRGB --assign-tf srgb --zstd 5 input/png/color_grid.png ktx2/color_grid_zstd_5.ktx2 $ktx create --testrun --format R8G8B8_SRGB --assign-tf srgb --encode basis-lz input/png/FlightHelmet_baseColor.png ktx2/FlightHelmet_baseColor_blze.ktx2 -$ktx create --testrun --format R8G8B8_UNORM --no-warn-on-color-conversions --assign-tf linear --generate-mipmap --normalize --normal-mode --encode basis-lz input/jpg/Iron_Bars/Iron_Bars_001_normal_unnormalized.png ktx2/Iron_Bars_001_normal_blze.ktx2 +$ktx create --testrun --format R8G8B8_UNORM --no-warn-on-color-conversions --assign-tf linear --generate-mipmap --normalize --normal-mode --encode basis-lz input/jpg/Iron_Bars/Iron_Bars_001_normal_unnormalized.png ktx2/Iron_Bars_001_normal_blze.ktx2 $ktx create --testrun --format R8G8B8_UNORM --no-warn-on-color-conversions --assign-tf linear --generate-mipmap --normalize --normal-mode --encode uastc --zstd 10 input/jpg/Iron_Bars/Iron_Bars_001_normal_unnormalized.png ktx2/Iron_Bars_001_normal_uastc_zstd_10.ktx2 $ktx create --testrun --format R8G8B8A8_SRGB --convert-texcoord-origin bottom-left input/npbm/up.ppm ktx2/orient_up_metadata.ktx2 $ktx create --testrun --format R8G8B8A8_SRGB --convert-texcoord-origin top-left input/npbm/up.ppm ktx2/orient_down_metadata.ktx2 @@ -143,3 +143,55 @@ $ktx create --testrun --format R16G16B16_SFLOAT --encode uastc-hdr-4x4 --uastc-h $ktx create --testrun --format R16G16B16_SFLOAT --encode uastc-hdr-6x6i --uastc-hdr-lambda 300 input/exr/Desk.exr ktx2/Desk_uastc_hdr6x6i.ktx2 # Small uncompressed file for the pyktx tests. $ktx create --testrun --format R16G16B16_SFLOAT --scale 0.25 --zstd 15 input/exr/Desk.exr ktx2/Desk_small_zstd_15.ktx2 + +# Some useful OpenImageIO commands (to change color space, extract channels, etc.). Run this before the following if you have change the input BCn PNG files. +# oiiotool --colorconvert sRGB linear --ch R,=0,=0 input/png/r8_unorm_bc4.png -o input/png/r8_unorm_bc4.png +# oiiotool --colorconvert sRGB linear --ch R,G,=0 input/png/rg8_unorm_bc5.png -o input/png/rg8_unorm_bc5.png +# oiiotool --colorconvert sRGB linear input/png/rgb8_unorm_bc1.png -o input/png/rgb8_unorm_bc1.png +# oiiotool --colorconvert sRGB linear input/png/rgb8_unorm_bc2.png -o input/png/rgb8_unorm_bc2.png +# oiiotool --colorconvert sRGB linear input/png/rgba8_unorm_bc3.png -o input/png/rgba8_unorm_bc3.png +# oiiotool --colorconvert sRGB linear input/png/rgba8_unorm_bc7.png -o input/png/rgba8_unorm_bc7.png + +# To convert to sRGB colorspace (from whatever the input is) +# oiiotool input/png/.png --tocolorspace srgb -o input/png/.png + +# differential +# BCn textures. Notice that these are created via a transcode using Basis Universal's UASTC format. This is important for differential testing (i.e., using different software with the same output to verify ours).A +# BC2 textures can be genearated using GIMP (export to DDS, no plugins needed) +$ktx create --testrun --encode uastc --format R8G8B8_UNORM input/png/rgb8_unorm_bc1.png - | $ktx transcode --testrun --target bc1 - ktx2/rgb8_unorm_bc1.ktx2 +$ktx create --testrun --encode uastc --format R8G8B8_SRGB input/png/rgb8_srgb_bc1.png - | $ktx transcode --testrun --target bc1 - ktx2/rgb8_srgb_bc1.ktx2 +$ktx create --testrun --encode uastc --format R8G8B8A8_UNORM input/png/rgba8_unorm_bc3.png - | $ktx transcode --testrun --target bc3 - ktx2/rgba8_unorm_bc3.ktx2 +$ktx create --testrun --encode uastc --format R8G8B8A8_SRGB input/png/rgba8_srgb_bc3.png - | $ktx transcode --testrun --target bc3 - ktx2/rgba8_srgb_bc3.ktx2 +$ktx create --testrun --encode uastc --format R8_UNORM input/png/r8_unorm_bc4.png - | $ktx transcode --testrun --target bc4 - ktx2/r8_unorm_bc4.ktx2 +$ktx create --testrun --encode uastc --normal-mode --format R8G8_UNORM input/png/rg8_unorm_bc5.png - | $ktx transcode --testrun --target bc5 - ktx2/rg8_unorm_bc5.ktx2 + +# BCn original KTX2 textures (so that ktxdiff can compare against these) +$ktx create --testrun --format R8G8B8_SRGB input/png/rgb8_srgb_bc1.png ktx2/rgb8_srgb_input_for_bc1.ktx2 +$ktx create --testrun --format R8G8B8_UNORM input/png/rgb8_unorm_bc1.png ktx2/rgb8_unorm_input_for_bc1.ktx2 +$ktx create --testrun --format R8G8B8A8_SRGB input/png/rgba8_srgb_bc2.png ktx2/rgba8_srgb_input_for_bc2.ktx2 +$ktx create --testrun --format R8G8B8A8_UNORM input/png/rgba8_unorm_bc2.png ktx2/rgba8_unorm_input_for_bc2.ktx2 +$ktx create --testrun --format R8G8B8A8_SRGB input/png/rgba8_srgb_bc3.png ktx2/rgba8_srgb_input_for_bc3.ktx2 +$ktx create --testrun --format R8G8B8A8_UNORM input/png/rgba8_unorm_bc3.png ktx2/rgba8_unorm_input_for_bc3.ktx2 +$ktx create --testrun --format R8_UNORM input/png/r8_unorm_bc4.png ktx2/r8_unorm_input_for_bc4.ktx2 +$ktx create --testrun --format R8G8_UNORM input/png/rg8_unorm_bc5.png ktx2/rg8_unorm_input_for_bc5.ktx2 +$ktx create --testrun --format R16G16B16_SFLOAT input/exr/rgb16_sfloat_bc6hu.exr ktx2/rgb16_sfloat_input_for_bc6hu.ktx2 +$ktx create --testrun --format R8G8B8A8_SRGB input/png/rgba8_srgb_bc7.png ktx2/rgba8_srgb_input_for_bc7.ktx2 +$ktx create --testrun --format R8G8B8A8_UNORM input/png/rgba8_unorm_bc7.png ktx2/rgba8_unorm_input_for_bc7.ktx2 + +# BCn + RDO + supercompression textures (generated using BCn encoder and not via a Basis Universal transcode) +$ktx create --testrun --format BC1_RGB_SRGB_BLOCK --bcn-rdo --zlib 9 input/png/rgb8_srgb_bc1.png ktx2/rgb8_srgb_bc1_rdo_zlib.ktx2 +$ktx create --testrun --format BC1_RGB_SRGB_BLOCK --bcn-rdo --zstd 22 input/png/rgb8_srgb_bc1.png ktx2/rgb8_srgb_bc1_rdo_zstd.ktx2 +$ktx create --testrun --format BC3_SRGB_BLOCK --bcn-rdo --zlib 9 input/png/rgba8_srgb_bc3.png ktx2/rgba8_srgb_bc3_rdo_zlib.ktx2 +$ktx create --testrun --format BC3_SRGB_BLOCK --bcn-rdo --zstd 22 input/png/rgba8_srgb_bc3.png ktx2/rgba8_srgb_bc3_rdo_zstd.ktx2 +$ktx create --testrun --format BC4_UNORM_BLOCK --bcn-rdo --zlib 9 input/png/r8_unorm_bc4.png ktx2/r8_unorm_bc4_rdo_zlib.ktx2 +$ktx create --testrun --format BC4_UNORM_BLOCK --bcn-rdo --zstd 22 input/png/r8_unorm_bc4.png ktx2/r8_unorm_bc4_rdo_zstd.ktx2 +$ktx create --testrun --format BC5_UNORM_BLOCK --bcn-rdo --zstd 22 input/png/rg8_unorm_bc5.png ktx2/rg8_unorm_bc5_rdo_zstd.ktx2 +$ktx create --testrun --format BC5_UNORM_BLOCK --bcn-rdo --zlib 9 input/png/rg8_unorm_bc5.png ktx2/rg8_unorm_bc5_rdo_zlib.ktx2 +$ktx create --testrun --format BC7_SRGB_BLOCK --bcn-rdo --zlib 9 input/png/rgba8_srgb_bc7.png ktx2/rgba8_srgb_bc7_rdo_zlib.ktx2 +$ktx create --testrun --format BC7_SRGB_BLOCK --bcn-rdo --zstd 22 input/png/rgba8_srgb_bc7.png ktx2/rgba8_srgb_bc7_rdo_zstd.ktx2 + +# EXR input file can be generated from input PNG file via OIIO: +# oiiotool tests/resources/input/png/rgb16_sfloat_bc6hu.png --iscolorspace srgb --tocolorspace lin_rec709_srgb -d half -o tests/resources/input/exr/rgb16_sfloat_bc6hu.exr +$ktx create --testrun --encode uastc-hdr-4x4 --format R16G16B16_SFLOAT input/exr/rgb16_sfloat_bc6hu.exr - | $ktx transcode --testrun --target bc6hu - ktx2/rgb16_sfloat_bc6hu.ktx2 +$ktx create --testrun --encode uastc --format R8G8B8A8_UNORM --assign-tf linear --assign-primaries bt709 input/png/rgba8_unorm_bc7.png - | $ktx transcode --testrun --target bc7 - ktx2/rgba8_unorm_bc7.ktx2 +$ktx create --testrun --encode uastc --format R8G8B8A8_SRGB --assign-tf srgb --assign-primaries bt709 input/png/rgba8_srgb_bc7.png - | $ktx transcode --testrun --target bc7 - ktx2/rgba8_srgb_bc7.ktx2 diff --git a/tests/resources/input/exr/rgb16_sfloat_bc6hu.exr b/tests/resources/input/exr/rgb16_sfloat_bc6hu.exr new file mode 100644 index 0000000000..d548cf8322 --- /dev/null +++ b/tests/resources/input/exr/rgb16_sfloat_bc6hu.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd4f3b86384117136a143cc21300607d1be0d979aa60a490e7a1db11498ea97b +size 1879 diff --git a/tests/resources/input/png/r8_unorm_bc4.png b/tests/resources/input/png/r8_unorm_bc4.png new file mode 100644 index 0000000000..136ea4f6e9 --- /dev/null +++ b/tests/resources/input/png/r8_unorm_bc4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5591f87551f08762b4e9ab8cf0406cce53658dee1250d0588b0424e6ccf0395f +size 688 diff --git a/tests/resources/input/png/rg8_unorm_bc5.png b/tests/resources/input/png/rg8_unorm_bc5.png new file mode 100644 index 0000000000..61bc87d129 --- /dev/null +++ b/tests/resources/input/png/rg8_unorm_bc5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c529aa3b8751219235ed643254e3239cc4abbf8b7a49a62e448247e7509717cf +size 720 diff --git a/tests/resources/input/png/rgb16_sfloat_bc6hs.png b/tests/resources/input/png/rgb16_sfloat_bc6hs.png new file mode 100644 index 0000000000..1c158bcf98 --- /dev/null +++ b/tests/resources/input/png/rgb16_sfloat_bc6hs.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43ebaa7bc6ea826d26bb48399186510e082f0375dfc823c197a613d0353aa20f +size 583 diff --git a/tests/resources/input/png/rgb16_sfloat_bc6hu.png b/tests/resources/input/png/rgb16_sfloat_bc6hu.png new file mode 100644 index 0000000000..6a04713c8b --- /dev/null +++ b/tests/resources/input/png/rgb16_sfloat_bc6hu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:464676599f8f14567d98e6ff5adcad3f3506fd4e2b51dc66664cc576f1d5e7c7 +size 573 diff --git a/tests/resources/input/png/rgb8_srgb_bc1.png b/tests/resources/input/png/rgb8_srgb_bc1.png new file mode 100644 index 0000000000..590299ade2 --- /dev/null +++ b/tests/resources/input/png/rgb8_srgb_bc1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7543b05d67b8f9f4cbaaa9e7c8b733a691c16e5b8f81f5cb156a021fafac2d5 +size 537 diff --git a/tests/resources/input/png/rgb8_unorm_bc1.png b/tests/resources/input/png/rgb8_unorm_bc1.png new file mode 100644 index 0000000000..1d5053c3b6 --- /dev/null +++ b/tests/resources/input/png/rgb8_unorm_bc1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:806d9b7e4d338ca3fb2a69a761980dd6475fa6b5bff380e5b1a3dd21839ec076 +size 751 diff --git a/tests/resources/input/png/rgba8_srgb_bc2.png b/tests/resources/input/png/rgba8_srgb_bc2.png new file mode 100644 index 0000000000..c7f04f7f1f --- /dev/null +++ b/tests/resources/input/png/rgba8_srgb_bc2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c9f9635038df2d7b18ed6de812525ecb2adc5eafdf00e81a56fcfcd894a72de +size 819 diff --git a/tests/resources/input/png/rgba8_srgb_bc3.png b/tests/resources/input/png/rgba8_srgb_bc3.png new file mode 100644 index 0000000000..57872c1ed8 --- /dev/null +++ b/tests/resources/input/png/rgba8_srgb_bc3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87066b72626b96de74a9aef0cb93d1cae3e5292ac26e709dcbe41457df117ced +size 596 diff --git a/tests/resources/input/png/rgba8_srgb_bc7.png b/tests/resources/input/png/rgba8_srgb_bc7.png new file mode 100644 index 0000000000..3a14567b2a --- /dev/null +++ b/tests/resources/input/png/rgba8_srgb_bc7.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b41556941bf5f30a005aea13a552cb45d58cef510ff9a564324e2faf920c72e0 +size 601 diff --git a/tests/resources/input/png/rgba8_unorm_bc2.png b/tests/resources/input/png/rgba8_unorm_bc2.png new file mode 100644 index 0000000000..46e0027298 --- /dev/null +++ b/tests/resources/input/png/rgba8_unorm_bc2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70cf753cb8a965dc1b1198dd3e3364f4008006b268e4f0a142814bea2034c727 +size 724 diff --git a/tests/resources/input/png/rgba8_unorm_bc3.png b/tests/resources/input/png/rgba8_unorm_bc3.png new file mode 100644 index 0000000000..87c02a1b66 --- /dev/null +++ b/tests/resources/input/png/rgba8_unorm_bc3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d21c00cd3d9c9c76cf8a9944ac2f7478353a41999e03e46b890befc08d2b01a +size 808 diff --git a/tests/resources/input/png/rgba8_unorm_bc7.png b/tests/resources/input/png/rgba8_unorm_bc7.png new file mode 100644 index 0000000000..d27dcbb460 --- /dev/null +++ b/tests/resources/input/png/rgba8_unorm_bc7.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d86d0bab19b8920bd04570672bc2bbd3a54e8a8d645716d6dbfc5bc807a9a008 +size 829 diff --git a/tests/resources/ktx2/r8_unorm_bc4.ktx2 b/tests/resources/ktx2/r8_unorm_bc4.ktx2 new file mode 100644 index 0000000000..3bf1c7462f --- /dev/null +++ b/tests/resources/ktx2/r8_unorm_bc4.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18a5b1726a5525790059b0c8a350f5977e49d8796bcab9abdd105b1ccca9ea01 +size 1024 diff --git a/tests/resources/ktx2/r8_unorm_bc4_rdo_zlib.ktx2 b/tests/resources/ktx2/r8_unorm_bc4_rdo_zlib.ktx2 new file mode 100644 index 0000000000..7cf48bd979 --- /dev/null +++ b/tests/resources/ktx2/r8_unorm_bc4_rdo_zlib.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88a3e855f92bfb77726f768754ab31233f720f0a5094b4b0ed410fda33d8dc2f +size 465 diff --git a/tests/resources/ktx2/r8_unorm_bc4_rdo_zstd.ktx2 b/tests/resources/ktx2/r8_unorm_bc4_rdo_zstd.ktx2 new file mode 100644 index 0000000000..98350a1017 --- /dev/null +++ b/tests/resources/ktx2/r8_unorm_bc4_rdo_zstd.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d729a5023604bfd65f7ca2bf870a728ccc498cdd84bcfa4db02a4d9ea87159b +size 460 diff --git a/tests/resources/ktx2/r8_unorm_input_for_bc4.ktx2 b/tests/resources/ktx2/r8_unorm_input_for_bc4.ktx2 new file mode 100644 index 0000000000..b8532d376a --- /dev/null +++ b/tests/resources/ktx2/r8_unorm_input_for_bc4.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f465f1c2df82b58668747a331a19288fa69245d15634b4fc1940fb6ac3e7d23 +size 1816 diff --git a/tests/resources/ktx2/rg8_unorm_bc5.ktx2 b/tests/resources/ktx2/rg8_unorm_bc5.ktx2 new file mode 100644 index 0000000000..8525bb1784 --- /dev/null +++ b/tests/resources/ktx2/rg8_unorm_bc5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09cca1897ac6fc5a270dcf43c70113204ea6e17d1ed048ab43734060213b4e3a +size 1840 diff --git a/tests/resources/ktx2/rg8_unorm_bc5_rdo_zlib.ktx2 b/tests/resources/ktx2/rg8_unorm_bc5_rdo_zlib.ktx2 new file mode 100644 index 0000000000..9c24d69f4b --- /dev/null +++ b/tests/resources/ktx2/rg8_unorm_bc5_rdo_zlib.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d3b8778f2813c76ef298f403b4a529dc6252077d1cae5956ecb4ade48ec28b3 +size 540 diff --git a/tests/resources/ktx2/rg8_unorm_bc5_rdo_zstd.ktx2 b/tests/resources/ktx2/rg8_unorm_bc5_rdo_zstd.ktx2 new file mode 100644 index 0000000000..bd574e24ae --- /dev/null +++ b/tests/resources/ktx2/rg8_unorm_bc5_rdo_zstd.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eaea0c4060f14a9f669ae443171b5490fee9da3b29c4cf13690006d38d8014c9 +size 523 diff --git a/tests/resources/ktx2/rg8_unorm_input_for_bc5.ktx2 b/tests/resources/ktx2/rg8_unorm_input_for_bc5.ktx2 new file mode 100644 index 0000000000..a0fa46b770 --- /dev/null +++ b/tests/resources/ktx2/rg8_unorm_input_for_bc5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca1102af7b847898037e5e4e883efbef987bd520933836d82c90ec07021f93f8 +size 3432 diff --git a/tests/resources/ktx2/rgb16_sfloat_bc6hu.ktx2 b/tests/resources/ktx2/rgb16_sfloat_bc6hu.ktx2 new file mode 100644 index 0000000000..41bb15281b --- /dev/null +++ b/tests/resources/ktx2/rgb16_sfloat_bc6hu.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:292c204f782225519453a036b6a3a895df0afbaf3d5a21af4931726d120788e0 +size 1824 diff --git a/tests/resources/ktx2/rgb16_sfloat_input_for_bc6hu.ktx2 b/tests/resources/ktx2/rgb16_sfloat_input_for_bc6hu.ktx2 new file mode 100644 index 0000000000..ca18ad1455 --- /dev/null +++ b/tests/resources/ktx2/rgb16_sfloat_input_for_bc6hu.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:246dc80d7c124d3c822047b915ec92c7a4eb1702069996b753553e0c7b468238 +size 9852 diff --git a/tests/resources/ktx2/rgb8_srgb_bc1.ktx2 b/tests/resources/ktx2/rgb8_srgb_bc1.ktx2 new file mode 100644 index 0000000000..1873336e30 --- /dev/null +++ b/tests/resources/ktx2/rgb8_srgb_bc1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c0230f0c85c9319d36b5be6f0db41a44df290914d55233ad9cd8a4acfea58d8 +size 1024 diff --git a/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zlib.ktx2 b/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zlib.ktx2 new file mode 100644 index 0000000000..3a6a358739 --- /dev/null +++ b/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zlib.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cefd5df19b73eb2e1fa795d50e75ca4683665ba87420d6f165b632cbf7973cbf +size 495 diff --git a/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zstd.ktx2 b/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zstd.ktx2 new file mode 100644 index 0000000000..6c9ef3a6bd --- /dev/null +++ b/tests/resources/ktx2/rgb8_srgb_bc1_rdo_zstd.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a72205b55333fde80f26adacc86dd31779606aa30125bd2c0fbd78cd56b7e603 +size 473 diff --git a/tests/resources/ktx2/rgb8_srgb_input_for_bc1.ktx2 b/tests/resources/ktx2/rgb8_srgb_input_for_bc1.ktx2 new file mode 100644 index 0000000000..6c937c55cc --- /dev/null +++ b/tests/resources/ktx2/rgb8_srgb_input_for_bc1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bea409c4486122645755f740065a71d55e4ca3a606702713622ce17ad8595fe9 +size 5052 diff --git a/tests/resources/ktx2/rgb8_unorm_bc1.ktx2 b/tests/resources/ktx2/rgb8_unorm_bc1.ktx2 new file mode 100644 index 0000000000..c2da858728 --- /dev/null +++ b/tests/resources/ktx2/rgb8_unorm_bc1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d389a4320b121fbc775a8bc8673842ec358948dcadd9ee1acc87799d45cb28b +size 1024 diff --git a/tests/resources/ktx2/rgb8_unorm_input_for_bc1.ktx2 b/tests/resources/ktx2/rgb8_unorm_input_for_bc1.ktx2 new file mode 100644 index 0000000000..1c47e2eebf --- /dev/null +++ b/tests/resources/ktx2/rgb8_unorm_input_for_bc1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e429d76485eedafb5a086a8ccf912177446c09c54ce453f57f54eb842cec8925 +size 5052 diff --git a/tests/resources/ktx2/rgba8_srgb_bc3.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc3.ktx2 new file mode 100644 index 0000000000..c9ddd8381f --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc3.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c46c26d5a953c63996953cf297264ad4c0346a51b5ba7c1fc2e6d10b9a458daa +size 1840 diff --git a/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zlib.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zlib.ktx2 new file mode 100644 index 0000000000..f2d157ed44 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zlib.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58823b78a391ef7ba0b70300f57180601392f619f94ee7df0b365ed7b29e7318 +size 552 diff --git a/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zstd.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zstd.ktx2 new file mode 100644 index 0000000000..315bd0aca0 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc3_rdo_zstd.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68b88a5db4d701147313ae7c99b64b5c08f1f4fd920fa760caab178ba0f2effe +size 518 diff --git a/tests/resources/ktx2/rgba8_srgb_bc7.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc7.ktx2 new file mode 100644 index 0000000000..b4434bb894 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc7.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efcbcd8abd5315cbf15b65e86359e8760d2fdb5915f7b0f444a1f1617bd0d263 +size 1824 diff --git a/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zlib.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zlib.ktx2 new file mode 100644 index 0000000000..08ea8d21a4 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zlib.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23053afbf4b0f64c3a07bab2b3362395c7484b1c7831d2406950e6f8f3050c0e +size 587 diff --git a/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zstd.ktx2 b/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zstd.ktx2 new file mode 100644 index 0000000000..1415811514 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_bc7_rdo_zstd.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61c9eedc6d4c2deea746d6792bba6fa3ac01c2ceaf69d84751b99fef1ef66f7a +size 558 diff --git a/tests/resources/ktx2/rgba8_srgb_input_for_bc2.ktx2 b/tests/resources/ktx2/rgba8_srgb_input_for_bc2.ktx2 new file mode 100644 index 0000000000..883e0007a4 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_input_for_bc2.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b948dc986514aeb1e778741612d55091658a007967e5acd3e2c537b05eb5460e +size 6664 diff --git a/tests/resources/ktx2/rgba8_srgb_input_for_bc3.ktx2 b/tests/resources/ktx2/rgba8_srgb_input_for_bc3.ktx2 new file mode 100644 index 0000000000..72e9d24935 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_input_for_bc3.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b231fd2a51731c0101a28ff984f1688e5950d33bb8d9263c957f34f7c64189d +size 6664 diff --git a/tests/resources/ktx2/rgba8_srgb_input_for_bc7.ktx2 b/tests/resources/ktx2/rgba8_srgb_input_for_bc7.ktx2 new file mode 100644 index 0000000000..2fa970b125 --- /dev/null +++ b/tests/resources/ktx2/rgba8_srgb_input_for_bc7.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76359a1b5e191f456be759167b3f4e09fc58d171e919b340dbffd3b12a47a634 +size 6664 diff --git a/tests/resources/ktx2/rgba8_unorm_bc2.ktx2 b/tests/resources/ktx2/rgba8_unorm_bc2.ktx2 new file mode 100644 index 0000000000..4d33c85400 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_bc2.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f5340b852b920140289d3e60e2adf2b6094e4218379bbb7c15cb256346790b6 +size 1872 diff --git a/tests/resources/ktx2/rgba8_unorm_bc3.ktx2 b/tests/resources/ktx2/rgba8_unorm_bc3.ktx2 new file mode 100644 index 0000000000..5cd645f7a6 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_bc3.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18d60977f4752408cc5c4b4ac8cd92fd07dfb2d0cb4b1fbca2ebac79af3874a8 +size 1840 diff --git a/tests/resources/ktx2/rgba8_unorm_bc7.ktx2 b/tests/resources/ktx2/rgba8_unorm_bc7.ktx2 new file mode 100644 index 0000000000..82cc2c3ee2 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_bc7.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50c6be6bfdc9bd8f9f4b3e36c706c8fa4d44e986b3e697267510a076cc6dbe3d +size 1824 diff --git a/tests/resources/ktx2/rgba8_unorm_input_for_bc2.ktx2 b/tests/resources/ktx2/rgba8_unorm_input_for_bc2.ktx2 new file mode 100644 index 0000000000..1e938af729 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_input_for_bc2.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:995c9881b548df00126c4b5d8fb3258bef1bf5db7204b098e3dd694e24c4a1ec +size 6664 diff --git a/tests/resources/ktx2/rgba8_unorm_input_for_bc3.ktx2 b/tests/resources/ktx2/rgba8_unorm_input_for_bc3.ktx2 new file mode 100644 index 0000000000..c48ee1c3a7 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_input_for_bc3.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99451b4fea0d1fe815e510e95389a21eddc84dd34ce0b9398f035247a1901817 +size 6664 diff --git a/tests/resources/ktx2/rgba8_unorm_input_for_bc7.ktx2 b/tests/resources/ktx2/rgba8_unorm_input_for_bc7.ktx2 new file mode 100644 index 0000000000..78098f9544 --- /dev/null +++ b/tests/resources/ktx2/rgba8_unorm_input_for_bc7.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ef4399ab4a8b2617cbce77f99b30af6acacb380b9fcf50c3adbe37c6a9b790f +size 6664 diff --git a/tests/tests.cmake b/tests/tests.cmake index 16fb0d52d1..31bb0a81a5 100644 --- a/tests/tests.cmake +++ b/tests/tests.cmake @@ -113,5 +113,5 @@ gtest_discover_tests(unittests gtest_discover_tests(texturetests TEST_PREFIX texturetest. DISCOVERY_TIMEOUT 20 - EXTRA_ARGS "${PROJECT_SOURCE_DIR}/tests/resources/" ${KTX_DIFF_PATH} + EXTRA_ARGS "${PROJECT_SOURCE_DIR}/tests/resources/" ${KTX_DIFF_PATH} "${CMAKE_BINARY_DIR}/Testing/Temporary/GeneratedFilesOnFailure/" ) diff --git a/tests/texturetests/texturetests.cc b/tests/texturetests/texturetests.cc index bf81d97478..60e0c1ab04 100644 --- a/tests/texturetests/texturetests.cc +++ b/tests/texturetests/texturetests.cc @@ -2993,6 +2993,8 @@ TEST(UnicodeFileNames, CreateFrom) { fs::path ktxdiffPath; fs::path ktx2Path; +fs::path tmpGeneratedFilesOutputPath; /*< temporary location for generated files (e.g., ktx2 files + on failure) */ template @@ -3334,15 +3336,540 @@ TEST_F(ktxTexture2AstcDecodeTestBase, Decode_astc_8x8_unorm_array_7) { runTest(u8"astc_8x8_unorm_array_7.ktx2"); } -} // namespace +//------------------------------------------------------------ +// Template for base fixture for BCn encode and decode tests. +//------------------------------------------------------------ + +/// RAII'fied fs::path wrapper that deletes the file in destructor only if it +/// exists. +class FileRAII { + public: + FileRAII() = default; + FileRAII(fs::path fp) : _fp{fp} {} + ~FileRAII() { + if (fs::exists(_fp)) fs::remove(_fp); + } + + FileRAII& operator=(fs::path other) { + _fp = other; + return *this; + } + + const fs::path& path() { return _fp; } + + private: + fs::path _fp; +}; + +template +class ktxTexture2BCnEncodeDecodeTestBase + : public ktxTexture2TestBase { + protected: + using ktxTextureTestBase::helper; + using ktxTextureTestBase::ktxMemFile; + using ktxTextureTestBase::ktxMemFileLen; + + public: + void runTest(ktx_bcn_compression_e bcn, bool rdo) { + ktxTexture_unique_ptr texture_raii{nullptr, ktxTexture_Deleter}; + KTX_error_code result; + + ktxTexture2* texture = nullptr; + auto tmpDir = fs::temp_directory_path(); + + result = ktxTexture2_CreateFromMemory(ktxMemFile.get(), ktxMemFileLen, + KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture); + texture_raii.reset((ktxTexture*)texture); + EXPECT_EQ(result, KTX_SUCCESS); + ASSERT_NE(texture, nullptr) + << "ktxTexture_CreateFromMemory failed: " << ktxErrorString(result); + ASSERT_TRUE(texture->pData != NULL) << "Image data not loaded"; + + VkFormat compressedFormat = VK_FORMAT_UNDEFINED; /* so that gtest stops complaining */ + ktx_uint32_t expectedDecompressedFormat; + + const bool isSRGB = KHR_DFDVAL(texture->pDfd + 1, TRANSFER) == KHR_DF_TRANSFER_SRGB; + + FileRAII original; + FileRAII decoded; + + std::string rdo_str = rdo ? "_rdo" : ""; + + switch (bcn) { + case KTX_BCN_COMPRESSION_BC1: + original = tmpDir / format("encode_rgb8_{}_to_bc1{}_then_decode_original.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + decoded = tmpDir / format("encode_rgb8_{}_to_bc1{}_then_decode_decoded.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + if (isSRGB) { + compressedFormat = VK_FORMAT_BC1_RGB_SRGB_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8_SRGB; + } else { + compressedFormat = VK_FORMAT_BC1_RGB_UNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8_UNORM; + } + break; + + case KTX_BCN_COMPRESSION_BC1A: + case KTX_BCN_COMPRESSION_BC2: + case KTX_BCN_COMPRESSION_BC6HS: + ASSERT_TRUE(false); + break; + + case KTX_BCN_COMPRESSION_BC3: + original = tmpDir / format("encode_rgba8_{}_to_bc3{}_then_decode_original.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + decoded = tmpDir / format("encode_rgba8_{}_to_bc3{}_then_decode_decoded.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + if (isSRGB) { + compressedFormat = VK_FORMAT_BC3_SRGB_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_SRGB; + } else { + compressedFormat = VK_FORMAT_BC3_UNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_UNORM; + } + break; + + case KTX_BCN_COMPRESSION_BC4: + ASSERT_FALSE(isSRGB); // should never occur + ASSERT_EQ(texture->vkFormat, (ktx_uint32_t)VK_FORMAT_R8_UNORM); + original = tmpDir / format("encode_r8_unorm_to_bc4{}_then_decode_original.ktx2", rdo_str); + decoded = tmpDir / format("encode_r8_unorm_to_bc4{}_then_decode_decoded.ktx2", rdo_str); + compressedFormat = VK_FORMAT_BC4_UNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8_UNORM; + break; + + case KTX_BCN_COMPRESSION_BC5: + ASSERT_FALSE(isSRGB); // should never occur + ASSERT_TRUE(texture->vkFormat == VK_FORMAT_R8G8_UNORM || + texture->vkFormat == VK_FORMAT_R8G8_SNORM); + original = tmpDir / format("encode_rg8_{}_to_bc5{}_then_decode_original.ktx2", texture->vkFormat == VK_FORMAT_R8G8_UNORM ? "unorm" : "snorm", rdo_str); + decoded = tmpDir / format("encode_rg8_{}_to_bc5{}_then_decode_decoded.ktx2", texture->vkFormat == VK_FORMAT_R8G8_UNORM ? "unorm" : "snorm", rdo_str); + if (texture->vkFormat == VK_FORMAT_R8G8_UNORM) { + compressedFormat = VK_FORMAT_BC5_UNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8_UNORM; + } else { + compressedFormat = VK_FORMAT_BC5_SNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8_SNORM; + } + break; + + case KTX_BCN_COMPRESSION_BC6HU: + ASSERT_FALSE(isSRGB); // should never occur + ASSERT_FALSE(rdo); // HDR rdo is not supported (yet) + original = tmpDir / "encode_rgb16_sfloat_to_bc6hu_then_decode_original.ktx2"; + decoded = tmpDir / "encode_rgb16_sfloat_to_bc6hu_then_decode_decoded.ktx2"; + compressedFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R16G16B16_SFLOAT; + break; + + case KTX_BCN_COMPRESSION_BC7: + original = tmpDir / format("encode_rgba8_{}_to_bc7{}_then_decode_original.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + decoded = tmpDir / format("encode_rgba8_{}_to_bc7{}_then_decode_decoded.ktx2", isSRGB ? "srgb" : "unorm", rdo_str); + if (isSRGB) { + compressedFormat = VK_FORMAT_BC7_SRGB_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_SRGB; + } else { + compressedFormat = VK_FORMAT_BC7_UNORM_BLOCK; + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_UNORM; + } + break; + + default: + return; + } + + ASSERT_NE(compressedFormat, VK_FORMAT_UNDEFINED); + ASSERT_EQ(texture->vkFormat, expectedDecompressedFormat); + ASSERT_NE(ktxMemFile, nullptr); + + result = ktxTexture2_WriteToNamedFile(texture, original.path().string().c_str()); + ASSERT_TRUE(result == KTX_SUCCESS); + + auto depth = texture->baseDepth; + auto height = texture->baseHeight; + auto width = texture->baseWidth; + auto dataSize = texture->dataSize; + + ASSERT_TRUE(depth == 1); + + ktxBCnParams params; + params.structSize = sizeof(params); + params.threadCount = 1; + params.bcn = bcn; + params.normalMap = false; + params.bc1CompressionQuality = KTX_PACK_BC1_QUALITY_LEVEL_MEDIUM; + params.bc7CompressionQuality = KTX_PACK_BC7_QUALITY_LEVEL_MEDIUM; + params.bcnRDO = rdo; + result = ktxTexture2_CompressBCnEx(texture, ¶ms); + + ASSERT_EQ(result, KTX_SUCCESS); + ASSERT_EQ(texture->vkFormat, (ktx_uint32_t)compressedFormat); + + uint32_t* pBdb = texture->pDfd + 1; + if (isSRGB) { + EXPECT_TRUE(KHR_DFDVAL(pBdb, TRANSFER) == KHR_DF_TRANSFER_SRGB); + } + khr_df_model_e model = static_cast(KHR_DFDVAL(pBdb, MODEL)); + // TODO: check that model is correctly set + // EXPECT_EQ(model, color); + EXPECT_EQ(texture->supercompressionScheme, KTX_SS_NONE); + EXPECT_TRUE(texture->_private->_supercompressionGlobalData == (ktx_uint8_t*)0); + EXPECT_EQ(texture->numLevels, helper.numLevels); + EXPECT_EQ(texture->baseDepth, depth); + EXPECT_EQ(texture->baseHeight, height); + EXPECT_EQ(texture->baseWidth, width); + EXPECT_LT(texture->dataSize, dataSize); + + result = ktxTexture2_DecodeBCn(texture); + + ASSERT_EQ(result, KTX_SUCCESS) << format("ktxTexture2_DecodeBCn failed with error code: {}", ktxErrorString(result)); + EXPECT_EQ(texture->vkFormat, expectedDecompressedFormat); + model = static_cast(KHR_DFDVAL(texture->pDfd + 1, MODEL)); + EXPECT_EQ(model, KHR_DF_MODEL_RGBSDA); + EXPECT_EQ(depth, texture->baseDepth); + result = ktxTexture2_WriteToNamedFile(texture, decoded.path().string().c_str()); + + FileRAII ktxdiffOut = tmpDir / format("ktxdiff_{}_vs_{}.txt", original.path().stem().string(), + decoded.path().stem().string()); + + // Compare orginal vs. decoded texture with 0.08 tolerance + // Since BC6HU input might be cleaned (i.e., signed values are set to 0) running ktxdiff on them will fail + if (bcn != KTX_BCN_COMPRESSION_BC6HU) { + std::string command = + format("{} {} {} 0.08 --skip-kvd > {}", ktxdiffPath.string(), original.path().string(), + decoded.path().string(), ktxdiffOut.path().string()); + int status = std::system(command.c_str()); + EXPECT_EQ(status, 0) + << format("std::system() with command \"{}\" returned error code: {}", command, status); + // Copy decoded ktx2 and ktxdiff output files out of tmp directory so that we can inspect it + if (status != 0) { + // Print ktxdiff output + std::cout << std::ifstream(ktxdiffOut.path()).rdbuf(); + { // Copy original KTX2 file + std::ifstream src(original.path(), std::ios::binary); + std::ofstream dst(tmpGeneratedFilesOutputPath / original.path().filename(), std::ios::binary); + dst << src.rdbuf(); + } + { // Copy decoded KTX2 file + std::ifstream src(decoded.path(), std::ios::binary); + std::ofstream dst(tmpGeneratedFilesOutputPath / decoded.path().filename(), std::ios::binary); + dst << src.rdbuf(); + } + { // Then copy ktxdiff output text file + std::ifstream src(ktxdiffOut.path(), std::ios::binary); + std::ofstream dst(tmpGeneratedFilesOutputPath / ktxdiffOut.path().filename(), std::ios::binary); + dst << src.rdbuf(); + } + } + } + + EXPECT_EQ(texture->baseHeight, height); + EXPECT_EQ(texture->baseWidth, width); + } +}; +// Test fixtures for BCn encode and decode tests. + +class ktxTexture2_BCnEncodeDecodeTestR8_UNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; +class ktxTexture2_BCnEncodeDecodeTestR8_SNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; + +class ktxTexture2_BCnEncodeDecodeTestRG8_UNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; +class ktxTexture2_BCnEncodeDecodeTestRG8_SNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; + +class ktxTexture2_BCnEncodeDecodeTestRGB8_UNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; +class ktxTexture2_BCnEncodeDecodeTestRGB8_SRGB + : public ktxTexture2BCnEncodeDecodeTestBase {}; + +class ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM + : public ktxTexture2BCnEncodeDecodeTestBase {}; +class ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB + : public ktxTexture2BCnEncodeDecodeTestBase {}; + +class ktxTexture2_BCnEncodeDecodeTestRGB16_SFLOAT + : public ktxTexture2BCnEncodeDecodeTestBase {}; + +//////////////////////////////////////////// +// BCn encode & decode tests +/////////////////////////////////////////// + +// BC1: +// - VK_FORMAT_R8G8B8_UNORM +// - VK_FORMAT_R8G8B8_SRGB +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGB8_UNORM, encode_rgb8_unorm_to_bc1_then_decode) { runTest(KTX_BCN_COMPRESSION_BC1, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGB8_UNORM, encode_rgb8_unorm_to_bc1_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC1, true); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGB8_SRGB, encode_rgb8_srgb_to_bc1_then_decode) { runTest(KTX_BCN_COMPRESSION_BC1, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGB8_SRGB, encode_rgb8_srgb_to_bc1_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC1, true); } + +// TODO: add encode-then-decode test for BC2 once BC2 encoder is implemented +// BC2: +// - VK_FORMAT_R8G8B8A8_UNORM +// - VK_FORMAT_R8G8B8A8_SRGB +// TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM, encode_rgba8_unorm_to_bc2_then_decode) { runTest(KTX_BCN_COMPRESSION_BC2); } +// TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB, encode_rgba8_srgb_to_bc2_then_decode) { runTest(KTX_BCN_COMPRESSION_BC2); } + +// BC3: +// - VK_FORMAT_R8G8B8_UNORM +// - VK_FORMAT_R8G8B8_SRGB +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM, encode_rgba8_unorm_to_bc3_then_decode) { runTest(KTX_BCN_COMPRESSION_BC3, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM, encode_rgba8_unorm_to_bc3_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC3, true); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB, encode_rgba8_srgb_to_bc3_then_decode) { runTest(KTX_BCN_COMPRESSION_BC3, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB, encode_rgba8_srgb_to_bc3_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC3, true); } + +// BC4: +// - VK_FORMAT_R8_UNORM +TEST_F(ktxTexture2_BCnEncodeDecodeTestR8_UNORM, encode_r8_unorm_to_bc4_then_decode) { runTest(KTX_BCN_COMPRESSION_BC4, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestR8_UNORM, encode_r8_unorm_to_bc4_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC4, true); } + +// BC5: +// - VK_FORMAT_R8G8_UNORM +TEST_F(ktxTexture2_BCnEncodeDecodeTestRG8_UNORM, encode_rg8_unorm_to_bc5_then_decode) { runTest(KTX_BCN_COMPRESSION_BC5, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRG8_UNORM, encode_rg8_unorm_to_bc5_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC5, true); } + +// BC6HU: +// - VK_FORMAT_R16G16B16A16_SFLOAT +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGB16_SFLOAT, encode_rgb16_sfloat_to_bc6hu_then_decode) { runTest(KTX_BCN_COMPRESSION_BC6HU, false); } + +// TODO: add BC6HS encode-then-decode test once a BC6HS encoder is implemented (if it is at all planned) +// BC6HS: +// - VK_FORMAT_R16G16B16A16_SFLOAT +// TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA16_SFLOAT, encode_rgba16_sfloat_to_bc6hs_then_decode) { runTest(KTX_BCN_COMPRESSION_BC6HS); } + +// BC7: +// - VK_FORMAT_R8G8B8A8_UNORM +// - VK_FORMAT_R8G8B8A8_SRGB +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM, encode_rgba8_unorm_to_bc7_then_decode) { runTest(KTX_BCN_COMPRESSION_BC7, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_UNORM, encode_rgba8_unorm_to_bc7_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC7, true); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB, encode_rgba8_srgb_to_bc7_then_decode) { runTest(KTX_BCN_COMPRESSION_BC7, false); } +TEST_F(ktxTexture2_BCnEncodeDecodeTestRGBA8_SRGB, encode_rgba8_srgb_to_bc7_rdo_then_decode) { runTest(KTX_BCN_COMPRESSION_BC7, true); } + +//------------------------------------------------- +// Template for base fixture for BCn decode tests. +//------------------------------------------------- + +class ktxTexture2BCnDecodeTestBase : public ::testing::Test { + public: + void runTest(const std::u8string& bcnFileName, const std::u8string bcnOriginal = u8"") { + ktxTexture_unique_ptr texture_raii{nullptr, ktxTexture_Deleter}; + KTX_error_code result; + ktxTexture2* texture = nullptr; + ktx_uint32_t expectedDecompressedFormat; + fs::path bcnPath = ktx2Path; + bcnPath.replace_filename(bcnFileName); + auto tmpDir = fs::temp_directory_path(); + + result = ktxTexture2_CreateFromNamedFile( + reinterpret_cast(bcnPath.u8string().c_str()), + KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture); + texture_raii.reset((ktxTexture*)texture); + + EXPECT_EQ(result, KTX_SUCCESS); + ASSERT_NE(texture, nullptr) + << format("ktxTexture2_CreateFromNamedFile \"{}\" failed: {}", + from_u8string(bcnPath.u8string()), ktxErrorString(result)); + + khr_df_model_e colormodel = ktxTexture2_GetColorModel_e(texture); + ASSERT_TRUE(colormodel == KHR_DF_MODEL_BC1A || colormodel == KHR_DF_MODEL_BC2 || + colormodel == KHR_DF_MODEL_BC3 || colormodel == KHR_DF_MODEL_BC4 || + colormodel == KHR_DF_MODEL_BC5 || colormodel == KHR_DF_MODEL_BC6H || + colormodel == KHR_DF_MODEL_BC7) + << format("Color model of \"{}\" {} is not a BCn encoded ktx test file", + from_u8string(bcnPath.u8string()), dfdToStringColorModel(colormodel)); + + switch (texture->vkFormat) { + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8B8_UNORM; + break; + + case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC2_UNORM_BLOCK: + case VK_FORMAT_BC7_UNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_UNORM; + break; + + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8B8_SRGB; + break; + + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8B8A8_SRGB; + break; + + case VK_FORMAT_BC4_UNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8_UNORM; + break; + + case VK_FORMAT_BC4_SNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8_SNORM; + break; + + case VK_FORMAT_BC5_UNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8_UNORM; + break; + + case VK_FORMAT_BC5_SNORM_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R8G8_SNORM; + break; + + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + expectedDecompressedFormat = VK_FORMAT_R16G16B16_SFLOAT; + break; + + default: + ASSERT_TRUE(false) << format( + "VkFormat {} is not of a supported BCn block compression format.", + texture->vkFormat); + break; // should never occur because of colormodel check above + } + + ASSERT_TRUE(texture->isCompressed); + ASSERT_FALSE(ktxTexture2_NeedsTranscoding(texture)); + ASSERT_TRUE(texture->pDfd != nullptr); + + auto height = texture->baseHeight; + auto width = texture->baseWidth; + auto depth = texture->baseDepth; + ASSERT_TRUE(depth == 1); + auto numLayers = texture->numLayers; + auto isArray = texture->isArray; + auto isHdr = ktxTexture2_IsHDR(texture); + auto isPremultipliedAlpha = ktxTexture2_GetPremultipliedAlpha(texture); + + result = ktxTexture2_DecodeBCn(texture); + + ASSERT_EQ(result, KTX_SUCCESS) + << format("ktxTexture2_DecodeBCn failed: {}", ktxErrorString(result)); + // Get the pointer to the updated DFD + ASSERT_FALSE(ktxTexture2_NeedsTranscoding(texture)); + ASSERT_FALSE(texture->isCompressed); + ASSERT_TRUE(texture->pDfd != nullptr); + uint32_t* pBdb = texture->pDfd + 1; + EXPECT_EQ(texture->vkFormat, expectedDecompressedFormat); + khr_df_model_e model = static_cast(KHR_DFDVAL(pBdb, MODEL)); + EXPECT_EQ(model, KHR_DF_MODEL_RGBSDA); + EXPECT_EQ(height, texture->baseHeight); + EXPECT_EQ(width, texture->baseWidth); + EXPECT_EQ(depth, texture->baseDepth); + EXPECT_EQ(numLayers, texture->numLayers); + EXPECT_EQ(isArray, texture->isArray); + EXPECT_EQ(isHdr, ktxTexture2_IsHDR(texture)); + EXPECT_EQ(isPremultipliedAlpha, ktxTexture2_GetPremultipliedAlpha(texture)); + + FileRAII decodedPath = tmpDir / ("decoded_" + from_u8string(bcnFileName)); + result = ktxTexture2_WriteToNamedFile(texture, decodedPath.path().string().c_str()); + ASSERT_EQ(result, KTX_SUCCESS) + << format("ktxTexture2_WriteToNamedFile failed: {}", ktxErrorString(result)); + + // Compare orginal vs. decoded texture with 0.08 tolerance + // Running ktxdiff on 16bit SFLOAT inputs simply fails (regardless of the provided + // tolerance) + if (texture->vkFormat != VK_FORMAT_R16G16B16_SFLOAT) { + fs::path bcnOriginalPath = ktx2Path; + bcnOriginalPath.replace_filename(bcnOriginal); + FileRAII ktxdiffOut = + tmpDir / format("ktxdiff_{}_vs_{}.txt", bcnOriginalPath.stem().string(), + decodedPath.path().stem().string()); + std::string command = + format("{} {} {} 0.08 --skip-kvd > {}", ktxdiffPath.string(), + bcnOriginalPath.string(), decodedPath.path().string(), ktxdiffOut.path().string()); + int status = std::system(command.c_str()); + EXPECT_EQ(status, 0) << format( + "std::system() with command \"{}\" returned error code: {}", command, status); + // Copy decoded ktx2 and ktxdiff output files out of tmp directory so that we can + // inspect it + if (status != 0) { + // Print ktxdiff output + std::cout << std::ifstream(ktxdiffOut.path()).rdbuf(); + { // Copy decoded KTX2 file + std::ifstream src(decodedPath.path(), std::ios::binary); + std::ofstream dst(tmpGeneratedFilesOutputPath / decodedPath.path().filename(), + std::ios::binary); + dst << src.rdbuf(); + } + { // Then copy ktxdiff output text file if it exists + std::ifstream src(ktxdiffOut.path(), std::ios::binary); + std::ofstream dst(tmpGeneratedFilesOutputPath / ktxdiffOut.path().filename(), + std::ios::binary); + dst << src.rdbuf(); + } + } + } + } +}; + +// Test fixtures for BCn decode tests. + +//////////////////////////////////////////// +// BCn decode tests +/////////////////////////////////////////// + +// BC1: +// - VK_FORMAT_R8G8B8_UNORM +// - VK_FORMAT_R8G8B8_SRGB +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgb8_unorm_bc1) { runTest(u8"rgb8_unorm_bc1.ktx2", u8"rgb8_unorm_input_for_bc1.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgb8_srgb_bc1) { runTest(u8"rgb8_srgb_bc1.ktx2", u8"rgb8_srgb_input_for_bc1.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgb8_srgb_bc1_rdo_zlib) { runTest(u8"rgb8_srgb_bc1_rdo_zlib.ktx2", u8"rgb8_srgb_input_for_bc1.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgb8_srgb_bc1_rdo_zstd) { runTest(u8"rgb8_srgb_bc1_rdo_zstd.ktx2", u8"rgb8_srgb_input_for_bc1.ktx2"); } + +// BC2: +// - VK_FORMAT_R8G8B8A8_UNORM +// - VK_FORMAT_R8G8B8A8_SRGB (TODO) +// Notes: +// - different BC2 encoder tools produce widely different results (e.g., GIMP vs OpenImageIO). +// - RDO is not supported for BC2 because of the lack of an encoder +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_unorm_bc2) { runTest(u8"rgba8_unorm_bc2.ktx2", u8"rgba8_unorm_input_for_bc2.ktx2"); } + +// BC3: +// - VK_FORMAT_R8G8B8_UNORM +// - VK_FORMAT_R8G8B8_SRGB +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_unorm_bc3) { runTest(u8"rgba8_unorm_bc3.ktx2", u8"rgba8_unorm_input_for_bc3.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc3) { runTest(u8"rgba8_srgb_bc3.ktx2", u8"rgba8_srgb_input_for_bc3.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc3_rdo_zlib) { runTest(u8"rgba8_srgb_bc3_rdo_zlib.ktx2", u8"rgba8_srgb_input_for_bc3.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc3_rdo_zstd) { runTest(u8"rgba8_srgb_bc3_rdo_zstd.ktx2", u8"rgba8_srgb_input_for_bc3.ktx2"); } + +// BC4: +// - VK_FORMAT_R8_UNORM +TEST_F(ktxTexture2BCnDecodeTestBase, decode_r8_unorm_bc4) { runTest(u8"r8_unorm_bc4.ktx2", u8"r8_unorm_input_for_bc4.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_r8_unorm_bc4_rdo_zlib) { runTest(u8"r8_unorm_bc4_rdo_zlib.ktx2", u8"r8_unorm_input_for_bc4.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_r8_unorm_bc4_rdo_zstd) { runTest(u8"r8_unorm_bc4_rdo_zstd.ktx2", u8"r8_unorm_input_for_bc4.ktx2"); } + +// BC5: +// - VK_FORMAT_R8G8_UNORM +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rg8_unorm_bc5) { runTest(u8"rg8_unorm_bc5.ktx2", u8"rg8_unorm_input_for_bc5.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rg8_unorm_bc5_rdo_zlib) { runTest(u8"rg8_unorm_bc5_rdo_zlib.ktx2", u8"rg8_unorm_input_for_bc5.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rg8_unorm_bc5_rdo_zstd) { runTest(u8"rg8_unorm_bc5_rdo_zstd.ktx2", u8"rg8_unorm_input_for_bc5.ktx2"); } + +// BC6HU: +// - VK_FORMAT_R16G16B16_SFLOAT +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgb16_sfloat_bc6hu) { runTest(u8"rgb16_sfloat_bc6hu.ktx2"); } +// RDO is not supported for HDR formats + +// TODO: add BC6HS decode test once a BC6HS encoder is implemented (if it is at all planned) +// BC6HS: +// - VK_FORMAT_R16G16B16A16_SFLOAT +// TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba16_sfloat_bc6hs) { +// runTest(u8"rgba16_sfloat_bc6hs.ktx2"); } + +// BC7: +// - VK_FORMAT_R8G8B8A8_UNORM +// - VK_FORMAT_R8G8B8A8_SRGB +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_unorm_bc7) { runTest(u8"rgba8_unorm_bc7.ktx2", u8"rgba8_unorm_input_for_bc7.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc7) { runTest(u8"rgba8_srgb_bc7.ktx2", u8"rgba8_srgb_input_for_bc7.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc7_rdo_zlib) { runTest(u8"rgba8_srgb_bc7_rdo_zlib.ktx2", u8"rgba8_srgb_input_for_bc7.ktx2"); } +TEST_F(ktxTexture2BCnDecodeTestBase, decode_rgba8_srgb_bc7_rdo_zstd) { runTest(u8"rgba8_srgb_bc7_rdo_zstd.ktx2", u8"rgba8_srgb_input_for_bc7.ktx2"); } + +} // namespace GTEST_API_ int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); if (!::testing::FLAGS_gtest_list_tests) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " \n"; + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " \n"; return -1; } @@ -3352,6 +3879,9 @@ GTEST_API_ int main(int argc, char* argv[]) { resourcesPath = u8argv[1]; resourcesPath /= ""; // Ensure trailing / so path is handled as directory. ktxdiffPath = u8argv[2]; + tmpGeneratedFilesOutputPath = u8argv[3]; + if (!std::filesystem::exists(tmpGeneratedFilesOutputPath)) + std::filesystem::create_directories(tmpGeneratedFilesOutputPath); std::error_code ec; auto stat = fs::status(resourcesPath, ec); diff --git a/tools/ktx/command_create.cpp b/tools/ktx/command_create.cpp index 3d7a502b31..804c8ff1dc 100644 --- a/tools/ktx/command_create.cpp +++ b/tools/ktx/command_create.cpp @@ -2,9 +2,11 @@ // Copyright 2022-2023 RasterGrid Kft. // SPDX-License-Identifier: Apache-2.0 +#include "platform_utils.h" +#include "bcn_common.h" #include "command.h" +#include "encode_utils_bcn.h" #include "encode_utils_common.h" -#include "platform_utils.h" #include "metrics_utils.h" #include "deflate_utils.h" #include "encode_utils_basis.h" @@ -151,7 +153,7 @@ struct OptionsCreate { "\n R16G16B16_SFLOAT" "\n R16G16B16A16_SFLOAT" "\nIf the format is an ASTC format the ASTC encoder specific options become valid," - " otherwise they are ignored." + " otherwise they are ignored. Likewise for a BCn format." "\nThe format will be used to verify and load all input files into a texture before encoding." " Case insensitive. Required.", cxxopts::value(), "") (k1D, "Create a 1D texture. If not set the texture will be a 2D or 3D texture.") @@ -580,6 +582,18 @@ struct OptionsCreate { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32, + VK_FORMAT_BC1_RGB_UNORM_BLOCK, + VK_FORMAT_BC1_RGB_SRGB_BLOCK, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + VK_FORMAT_BC3_UNORM_BLOCK, + VK_FORMAT_BC3_SRGB_BLOCK, + VK_FORMAT_BC4_UNORM_BLOCK, + VK_FORMAT_BC5_UNORM_BLOCK, + VK_FORMAT_BC6H_UFLOAT_BLOCK, + VK_FORMAT_BC6H_SFLOAT_BLOCK, + VK_FORMAT_BC7_UNORM_BLOCK, + VK_FORMAT_BC7_SRGB_BLOCK, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, VK_FORMAT_ASTC_5x4_UNORM_BLOCK, @@ -1142,6 +1156,7 @@ Create a KTX2 file from various input files. encoder has been selected. Common encoder options become valid when an encoder they apply to has been selected. Otherwise they are ignored. @snippet{doc} ktx/encode_utils_astc.h command options_encode_astc + @snippet{doc} ktx/encode_utils_bcn.h command options_encode_bcn @snippet{doc} ktx/encode_utils_basis.h command options_encode_basis @snippet{doc} ktx/encode_utils_common.h command options_encode_common @snippet{doc} ktx/metrics_utils.h command options_metrics @@ -1242,7 +1257,7 @@ Transfer function handling proceeds as follows: */ class CommandCreate : public Command { private: - Combine, OptionsEncodeCommon, OptionsMetrics, OptionsDeflate, OptionsMultiInSingleOut, OptionsGeneric> options; + Combine, OptionsEncodeCommon, OptionsMetrics, OptionsDeflate, OptionsMultiInSingleOut, OptionsGeneric> options; uint32_t targetChannelCount = 0; // Derived from VkFormat @@ -1263,6 +1278,7 @@ class CommandCreate : public Command { void executeCreate(); void encodeBasis(KTXTexture2& texture, OptionsEncodeBasis& opts); void encodeASTC(KTXTexture2& texture, OptionsEncodeASTC& opts); + void encodeBCn(KTXTexture2& texture, OptionsEncodeBCn& opts); void compress(KTXTexture2& texture, const OptionsDeflate& opts); private: @@ -1289,6 +1305,8 @@ class CommandCreate : public Command { void checkNumInputImages(); void checkSpecsMatch(const ImageInput& current, const ImageSpec& firstSpec); + + VkFormat decompressedBCnFormat(VkFormat format) const; }; // ------------------------------------------------------------------------------------------------- @@ -1345,14 +1363,25 @@ void CommandCreate::processOptions(cxxopts::Options& opts, cxxopts::ParseResult& checkNumInputImages(); } - if (!isFormatAstc(options.vkFormat)) { + const bool isASTC = isFormatAstc(options.vkFormat); + const bool isBCn = isFormatBCn(options.vkFormat); + + if (!isASTC && !isBCn) { for (const char* astcOption : OptionsEncodeASTC::kAstcOptions) if (args[astcOption].count()) fatal_usage("--{} can only be used with ASTC formats.", astcOption); - } else { + for (const char* bcnOption : OptionsEncodeBCn::kBCnOptions) + if (args[bcnOption].count()) + fatal_usage("--{} can only be used with BCn formats.", bcnOption); + } else if (isASTC) { fillOptionsCodecAstc(options); if (options.OptionsEncodeCommon::noSSE) fatal_usage("--{} is not allowed with ASTC encode", OptionsEncodeCommon::kNoSse); + } else /* isBCn */ { + // TODO: I have just copied this from ASTC above - need to verify this + fillOptionsCodecBCn(options); + if (options.OptionsEncodeCommon::noSSE) + fatal_usage("--{} is not allowed with BCn encode", OptionsEncodeCommon::kNoSse); } if (options.selectedCodec == BasisCodec::BasisLZ) { @@ -1415,18 +1444,17 @@ void CommandCreate::processOptions(cxxopts::Options& opts, cxxopts::ParseResult& options.selectedCodec == BasisCodec::UASTC_LDR_4x4 || options.selectedCodec == BasisCodec::UASTC_HDR_4x4 || options.selectedCodec == BasisCodec::UASTC_HDR_6x6i; - const auto astcCodec = isFormatAstc(options.vkFormat); - const auto canCompare = basisCodec || astcCodec; + const auto canCompare = basisCodec || isASTC || isBCn; if (basisCodec) fillOptionsCodecBasis(options); if (options.compare_ssim && !canCompare) - fatal_usage("--compare-ssim can only be used with BasisLZ, UASTC or ASTC encoding."); + fatal_usage("--compare-ssim can only be used with BasisLZ, UASTC, ASTC or BCn encoding."); if (options.compare_psnr && !canCompare) - fatal_usage("--compare-psnr can only be used with BasisLZ, UASTC or ASTC encoding."); + fatal_usage("--compare-psnr can only be used with BasisLZ, UASTC, ASTC or BCn encoding."); - if (isFormatAstc(options.vkFormat) && !options.raw) { + if (isASTC && !options.raw) { options.encodeASTC = true; switch (options.vkFormat) { @@ -1506,8 +1534,26 @@ void CommandCreate::processOptions(cxxopts::Options& opts, cxxopts::ParseResult& } } - if (options._1d && options.encodeASTC) - fatal_usage("ASTC format {} cannot be used for 1 dimensional textures (indicated by --1d).", + if (isBCn && !options.raw) { + options.encodeBCn = true; + + VkFormat decompressed_format; + int nchannels; + options.bcn = get_bcn_compression_kind(options.vkFormat, decompressed_format, nchannels); + if (options.bcn == KTX_BCN_COMPRESSION_NONE) { // should never occur + assert(false); + fatal(rc::NOT_SUPPORTED, "{} is unsupported for BCn encoding.", toString(options.vkFormat)); + } + else if (options.bcn == KTX_BCN_COMPRESSION_BC1A) + fatal(rc::IO_FAILURE, "Punch-through alpha encoding for BC1 format is not supported. Consider supplying an RGB8 input format instead."); + else if (options.bcn == KTX_BCN_COMPRESSION_BC2) + fatal(rc::IO_FAILURE, "Encoding to BC2 format is not supported (yet)."); + else if (options.bcn == KTX_BCN_COMPRESSION_BC6HS) + fatal(rc::IO_FAILURE, "Encoding to signed BC6H HDR format (BC6HS) is not supported."); + } + + if (options._1d && (options.encodeASTC || options.encodeBCn)) + fatal_usage("{} format {} cannot be used for 1 dimensional textures (indicated by --1d).", options.encodeASTC ? "ASTC" : "BCn", toString(options.vkFormat)); } @@ -1673,8 +1719,19 @@ void CommandCreate::executeCreate() { checkNumInputImages(); - if (options.encodeASTC) + // Overwrite options' vkFormat because ASTC/BCn encoding is performed by first creating an RGBA8 texture + // then encoding it using ktxTexture2_CompressAstcEx/ktxTexture2_CompressBCnEx + if (options.encodeASTC) { selectASTCMode(inputImageFile->spec().format().largestChannelBitLength()); + } + + if (options.encodeBCn) { + auto decompressed_format = decompressedBCnFormat(options.vkFormat); + if (decompressed_format == VK_FORMAT_UNDEFINED) { + fatal(rc::INVALID_FILE, "Failed to get decompressed BCn format from: {}", toString(options.vkFormat)); + } + options.vkFormat = decompressed_format; + } firstImageSpec = inputImageFile->spec(); @@ -2034,13 +2091,15 @@ void CommandCreate::executeCreate() { encodeBasis(texture, options); if (options.encodeASTC) encodeASTC(texture, options); + if (options.encodeBCn) + encodeBCn(texture, options); metrics.decodeAndCalculateMetrics(texture, options, *this); compress(texture, options); - // Add KTXwriterScParams metadata if ASTC encoding, BasisU encoding, or other supercompression was used - const auto writerScParams = fmt::format("{}{}{}{}", options.astcOptions, options.codecOptions, options.commonOptions, options.compressOptions); + // Add KTXwriterScParams metadata if ASTC encoding, BCn encoding, BasisU encoding, or other supercompression was used + auto writerScParams = fmt::format("{}{}{}{}{}", options.astcOptions, options.bcnOptions, options.codecOptions, options.commonOptions, options.compressOptions); if (writerScParams.size() > 0) { // Options always contain a leading space assert(writerScParams[0] == ' '); @@ -2073,6 +2132,12 @@ void CommandCreate::encodeASTC(KTXTexture2& texture, OptionsEncodeASTC& opts) { fatal(rc::KTX_FAILURE, "Failed to encode KTX2 file with codec ASTC. KTX Error: {}", ktxErrorString(ret)); } +void CommandCreate::encodeBCn(KTXTexture2& texture, OptionsEncodeBCn& opts) { + const auto ret = ktxTexture2_CompressBCnEx(texture, &opts); + if (ret != KTX_SUCCESS) + fatal(rc::KTX_FAILURE, "Failed to encode KTX2 file with codec BCn. KTX Error: {}", ktxErrorString(ret)); +} + void CommandCreate::compress(KTXTexture2& texture, const OptionsDeflate& opts) { if (opts.zstd) { const auto ret = ktxTexture2_DeflateZstd(texture, *opts.zstd); @@ -2345,6 +2410,24 @@ std::vector CommandCreate::convert(const std::unique_ptr& image, // Input files that have 16-bit components must be truncated to // 8 bits with a right-shift and a warning must be generated in the stderr. + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + case VK_FORMAT_BC2_UNORM_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC4_UNORM_BLOCK: + case VK_FORMAT_BC4_SNORM_BLOCK: + case VK_FORMAT_BC5_UNORM_BLOCK: + case VK_FORMAT_BC5_SNORM_BLOCK: + case VK_FORMAT_BC7_UNORM_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + requireUNORM(8); + assert(false && "Internal error"); + return {}; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: [[fallthrough]]; case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: [[fallthrough]]; case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: [[fallthrough]]; @@ -3119,6 +3202,47 @@ void CommandCreate::checkSpecsMatch(const ImageInput& currentFile, const ImageSp } } +VkFormat CommandCreate::decompressedBCnFormat(VkFormat format) const { + switch (format) { + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + return VK_FORMAT_R8G8B8_UNORM; + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + return VK_FORMAT_R8G8B8_SRGB; + +#if 0 + case VK_FORMAT_BC2_UNORM_BLOCK: [[fallthrough]]; + case VK_FORMAT_BC2_SRGB_BLOCK: + return VK_FORMAT_UNDEFINED; +#endif + + case VK_FORMAT_BC3_UNORM_BLOCK: + return VK_FORMAT_R8G8B8A8_UNORM; + case VK_FORMAT_BC3_SRGB_BLOCK: + return VK_FORMAT_R8G8B8A8_SRGB; + + case VK_FORMAT_BC4_UNORM_BLOCK: + return VK_FORMAT_R8_UNORM; + case VK_FORMAT_BC4_SNORM_BLOCK: + return VK_FORMAT_R8_SNORM; + + case VK_FORMAT_BC5_UNORM_BLOCK: + return VK_FORMAT_R8G8_UNORM; + case VK_FORMAT_BC5_SNORM_BLOCK: + return VK_FORMAT_R8G8_SNORM; + + case VK_FORMAT_BC6H_UFLOAT_BLOCK: [[fallthrough]]; + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + return VK_FORMAT_R16G16B16_SFLOAT; + + case VK_FORMAT_BC7_UNORM_BLOCK: + return VK_FORMAT_R8G8B8A8_UNORM; + case VK_FORMAT_BC7_SRGB_BLOCK: + return VK_FORMAT_R8G8B8A8_SRGB; + + default: return VK_FORMAT_UNDEFINED; + } +} + } // namespace ktx KTX_COMMAND_ENTRY_POINT(ktxCreate, ktx::CommandCreate) diff --git a/tools/ktx/command_encode.cpp b/tools/ktx/command_encode.cpp index 67425b968f..5ff7167e34 100644 --- a/tools/ktx/command_encode.cpp +++ b/tools/ktx/command_encode.cpp @@ -2,10 +2,12 @@ // Copyright 2022-2023 RasterGrid Kft. // SPDX-License-Identifier: Apache-2.0 +#include "platform_utils.h" +#include "bcn_common.h" #include "command.h" #include "encode_utils_astc.h" +#include "encode_utils_bcn.h" #include "encode_utils_common.h" -#include "platform_utils.h" #include "metrics_utils.h" #include "deflate_utils.h" #include "encode_utils_basis.h" @@ -18,6 +20,7 @@ #include #include #include +#include #include #include @@ -49,6 +52,10 @@ Encode a KTX2 file. For universal and ASTC LDR formats, the input file must be R8, R8G8, R8G8B8 or R8G8B8A8 (or their sRGB variants). + For BC1, BC3 and BC7 formats, the input file must be R8G8B8A8 (or their SRGB variants). + For BC4 format, the input file must be R8. + For BC5 format, the input file must be R8G8. + For universal HDR formats, the input file must be R16G16B16_SFLOAT or R16G16B16A16_SFLOAT.