From d71dfabc41a8393382b8e54f27cd748e72422d78 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:22:57 +0000 Subject: [PATCH] Squashed 'src/minisketch/' content from commit 89629eb2c7 git-subtree-dir: src/minisketch git-subtree-split: 89629eb2c7e262b39ba489b93b111760baded4b3 --- .cirrus.yml | 154 +++++ .gitignore | 35 + LICENSE | 21 + Makefile.am | 92 +++ README.md | 210 ++++++ autogen.sh | 15 + build-aux/m4/ax_check_compile_flag.m4 | 53 ++ build-aux/m4/ax_check_link_flag.m4 | 53 ++ build-aux/m4/ax_check_preproc_flag.m4 | 53 ++ build-aux/m4/ax_cxx_compile_stdcxx.m4 | 962 ++++++++++++++++++++++++++ ci/cirrus.sh | 41 ++ ci/linux-debian.Dockerfile | 17 + configure.ac | 162 +++++ doc/example.c | 51 ++ doc/gen_basefpbits.sage | 78 +++ doc/gen_params.sage | 333 +++++++++ doc/log2_factorial.sage | 85 +++ doc/math.md | 117 ++++ doc/minisketch-vs.png | Bin 0 -> 14212 bytes doc/moduli.md | 65 ++ doc/plot_bits.png | Bin 0 -> 70202 bytes doc/plot_capacity.png | Bin 0 -> 77020 bytes doc/plot_diff.png | Bin 0 -> 58547 bytes doc/plot_size.png | Bin 0 -> 67743 bytes doc/protocoltips.md | 30 + include/minisketch.h | 367 ++++++++++ sources.mk | 58 ++ src/bench.cpp | 122 ++++ src/false_positives.h | 110 +++ src/fielddefines.h | 560 +++++++++++++++ src/fields/clmul_1byte.cpp | 119 ++++ src/fields/clmul_2bytes.cpp | 154 +++++ src/fields/clmul_3bytes.cpp | 166 +++++ src/fields/clmul_4bytes.cpp | 158 +++++ src/fields/clmul_5bytes.cpp | 174 +++++ src/fields/clmul_6bytes.cpp | 170 +++++ src/fields/clmul_7bytes.cpp | 170 +++++ src/fields/clmul_8bytes.cpp | 175 +++++ src/fields/clmul_common_impl.h | 170 +++++ src/fields/generic_1byte.cpp | 112 +++ src/fields/generic_2bytes.cpp | 124 ++++ src/fields/generic_3bytes.cpp | 124 ++++ src/fields/generic_4bytes.cpp | 124 ++++ src/fields/generic_5bytes.cpp | 124 ++++ src/fields/generic_6bytes.cpp | 124 ++++ src/fields/generic_7bytes.cpp | 124 ++++ src/fields/generic_8bytes.cpp | 124 ++++ src/fields/generic_common_impl.h | 70 ++ src/int_utils.h | 290 ++++++++ src/lintrans.h | 150 ++++ src/minisketch.cpp | 490 +++++++++++++ src/sketch.h | 42 ++ src/sketch_impl.h | 433 ++++++++++++ src/test.cpp | 316 +++++++++ src/util.h | 74 ++ tests/pyminisketch.py | 507 ++++++++++++++ 56 files changed, 8652 insertions(+) create mode 100644 .cirrus.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile.am create mode 100644 README.md create mode 100755 autogen.sh create mode 100644 build-aux/m4/ax_check_compile_flag.m4 create mode 100644 build-aux/m4/ax_check_link_flag.m4 create mode 100644 build-aux/m4/ax_check_preproc_flag.m4 create mode 100644 build-aux/m4/ax_cxx_compile_stdcxx.m4 create mode 100755 ci/cirrus.sh create mode 100644 ci/linux-debian.Dockerfile create mode 100644 configure.ac create mode 100644 doc/example.c create mode 100644 doc/gen_basefpbits.sage create mode 100755 doc/gen_params.sage create mode 100644 doc/log2_factorial.sage create mode 100644 doc/math.md create mode 100644 doc/minisketch-vs.png create mode 100644 doc/moduli.md create mode 100644 doc/plot_bits.png create mode 100644 doc/plot_capacity.png create mode 100644 doc/plot_diff.png create mode 100644 doc/plot_size.png create mode 100644 doc/protocoltips.md create mode 100644 include/minisketch.h create mode 100644 sources.mk create mode 100644 src/bench.cpp create mode 100644 src/false_positives.h create mode 100644 src/fielddefines.h create mode 100644 src/fields/clmul_1byte.cpp create mode 100644 src/fields/clmul_2bytes.cpp create mode 100644 src/fields/clmul_3bytes.cpp create mode 100644 src/fields/clmul_4bytes.cpp create mode 100644 src/fields/clmul_5bytes.cpp create mode 100644 src/fields/clmul_6bytes.cpp create mode 100644 src/fields/clmul_7bytes.cpp create mode 100644 src/fields/clmul_8bytes.cpp create mode 100644 src/fields/clmul_common_impl.h create mode 100644 src/fields/generic_1byte.cpp create mode 100644 src/fields/generic_2bytes.cpp create mode 100644 src/fields/generic_3bytes.cpp create mode 100644 src/fields/generic_4bytes.cpp create mode 100644 src/fields/generic_5bytes.cpp create mode 100644 src/fields/generic_6bytes.cpp create mode 100644 src/fields/generic_7bytes.cpp create mode 100644 src/fields/generic_8bytes.cpp create mode 100644 src/fields/generic_common_impl.h create mode 100644 src/int_utils.h create mode 100644 src/lintrans.h create mode 100644 src/minisketch.cpp create mode 100644 src/sketch.h create mode 100644 src/sketch_impl.h create mode 100644 src/test.cpp create mode 100644 src/util.h create mode 100755 tests/pyminisketch.py diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000000..4a5353f137 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,154 @@ +env: + BUILD: check + HOST: + MAKEFLAGS: -j4 + BENCH: yes + TESTRUNS: + EXEC_CMD: + ENABLE_FIELDS: + +cat_logs_snippet: &CAT_LOGS + on_failure: + cat_test_log_script: + - cat test-suite.log || true + cat_config_log_script: + - cat config.log || true + cat_test_env_script: + - cat test_env.log || true + cat_ci_env_script: + - env + +merge_base_script_snippet: &MERGE_BASE + merge_base_script: + - if [ "$CIRRUS_PR" = "" ]; then exit 0; fi + - git fetch $CIRRUS_REPO_CLONE_URL $CIRRUS_BASE_BRANCH + - git config --global user.email "ci@ci.ci" + - git config --global user.name "ci" + - git merge FETCH_HEAD # Merge base to detect silent merge conflicts + +env_matrix_snippet: &ENV_MATRIX_VALGRIND + - env: + ENABLE_FIELDS: "7,32,58" + - env: + BUILD: distcheck + - env: + EXEC_CMD: valgrind --error-exitcode=42 + TESTRUNS: 1 + BUILD: + +env_matrix_snippet: &ENV_MATRIX_SAN + - env: + ENABLE_FIELDS: 28 + - env: + BUILD: distcheck + - env: + CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" + BENCH: no + +env_matrix_snippet: &ENV_MATRIX_SAN_VALGRIND + - env: + ENABLE_FIELDS: "11,64,37" + - env: + BUILD: distcheck + - env: + EXEC_CMD: valgrind --error-exitcode=42 + TESTRUNS: 1 + BUILD: + - env: + CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" + BENCH: no + +task: + name: "x86_64: Linux (Debian stable)" + container: + dockerfile: ci/linux-debian.Dockerfile + memory: 2G + cpu: 4 + matrix: + << : *ENV_MATRIX_SAN_VALGRIND + matrix: + - env: + CC: gcc + - env: + CC: clang + << : *MERGE_BASE + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + name: "i686: Linux (Debian stable)" + container: + dockerfile: ci/linux-debian.Dockerfile + memory: 2G + cpu: 4 + env: + HOST: i686-linux-gnu + matrix: + << : *ENV_MATRIX_VALGRIND + matrix: + - env: + CC: i686-linux-gnu-gcc + - env: + CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + name: "x86_64: macOS Catalina" + macos_instance: + image: catalina-base + env: + # Cirrus gives us a fixed number of 12 virtual CPUs. + MAKEFLAGS: -j13 + matrix: + << : *ENV_MATRIX_SAN + matrix: + - env: + CC: gcc-9 + - env: + CC: clang + brew_script: + - brew update + - brew install automake libtool gcc@9 + << : *MERGE_BASE + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + name: "s390x (big-endian): Linux (Debian stable, QEMU)" + container: + dockerfile: ci/linux-debian.Dockerfile + cpu: 4 + memory: 2G + env: + EXEC_CMD: qemu-s390x -L /usr/s390x-linux-gnu + HOST: s390x-linux-gnu + BUILD: + << : *MERGE_BASE + test_script: + # https://sourceware.org/bugzilla/show_bug.cgi?id=27008 + - rm /etc/ld.so.cache + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + name: "x86_64-w64-mingw32: Linux (Debian stable, Wine)" + container: + dockerfile: ci/linux-debian.Dockerfile + cpu: 4 + memory: 2G + env: + EXEC_CMD: wine + HOST: x86_64-w64-mingw32 + BUILD: + << : *MERGE_BASE + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..f5be6fab88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +*.o +*.lo +*.la +*.dll +*.dylib +*.so.* +.* +*.a +*~ + +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver +config.log +config.status +configure +libtool +stamp-h1 + +test* +bench diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..b25e3caee3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..86a5c9dc90 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,92 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) + +include sources.mk + +include_HEADERS = $(MINISKETCH_DIST_HEADERS_INT) +noinst_HEADERS = $(MINISKETCH_LIB_HEADERS_INT) $(MINISKETCH_FIELD_GENERIC_HEADERS_INT) $(MINISKETCH_FIELD_CLMUL_HEADERS_INT) + +LIBMINISKETCH = libminisketch.la +LIBMINISKETCH_FIELD_GENERIC = libminisketch_field_generic.la +if ENABLE_CLMUL +LIBMINISKETCH_FIELD_CLMUL = libminisketch_field_clmul.la +endif +if USE_TESTS +LIBMINISKETCH_VERIFY=libminisketch_verify.la +LIBMINISKETCH_FIELD_GENERIC_VERIFY=libminisketch_field_generic_verify.la +if ENABLE_CLMUL +LIBMINISKETCH_FIELD_CLMUL_VERIFY=libminisketch_field_clmul_verify.la +endif +endif + +lib_LTLIBRARIES = +lib_LTLIBRARIES += $(LIBMINISKETCH) + +noinst_LTLIBRARIES = +noinst_LTLIBRARIES += $(LIBMINISKETCH_FIELD_GENERIC) +noinst_LTLIBRARIES += $(LIBMINISKETCH_FIELD_GENERIC_VERIFY) +noinst_LTLIBRARIES += $(LIBMINISKETCH_FIELD_CLMUL) +noinst_LTLIBRARIES += $(LIBMINISKETCH_FIELD_CLMUL_VERIFY) +noinst_LTLIBRARIES += $(LIBMINISKETCH_VERIFY) + +# Release libs +libminisketch_field_generic_la_SOURCES = $(MINISKETCH_FIELD_GENERIC_SOURCES_INT) +libminisketch_field_generic_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELEASE_DEFINES) + +libminisketch_field_clmul_la_SOURCES = $(MINISKETCH_FIELD_CLMUL_SOURCES_INT) +libminisketch_field_clmul_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELEASE_DEFINES) +libminisketch_field_clmul_la_CXXFLAGS = $(AM_CXXFLAGS) $(CLMUL_CXXFLAGS) + +libminisketch_la_SOURCES = $(MINISKETCH_LIB_SOURCES_INT) +libminisketch_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELEASE_DEFINES) +libminisketch_la_LIBADD = $(LIBMINISKETCH_FIELD_CLMUL) $(LIBMINISKETCH_FIELD_GENERIC) + +# Libs with extra verification checks +libminisketch_field_generic_verify_la_SOURCES = $(MINISKETCH_FIELD_GENERIC_SOURCES_INT) +libminisketch_field_generic_verify_la_CPPFLAGS = $(AM_CPPFLAGS) $(VERIFY_DEFINES) + +libminisketch_field_clmul_verify_la_SOURCES = $(MINISKETCH_FIELD_CLMUL_SOURCES_INT) +libminisketch_field_clmul_verify_la_CPPFLAGS = $(AM_CPPFLAGS) $(VERIFY_DEFINES) +libminisketch_field_clmul_verify_la_CXXFLAGS = $(AM_CXXFLAGS) $(CLMUL_CXXFLAGS) + +libminisketch_verify_la_SOURCES = $(MINISKETCH_LIB_SOURCES_INT) +libminisketch_verify_la_CPPFLAGS = $(AM_CPPFLAGS) $(VERIFY_DEFINES) +libminisketch_verify_la_LIBADD = $(LIBMINISKETCH_FIELD_CLMUL_VERIFY) $(LIBMINISKETCH_FIELD_GENERIC_VERIFY) + +noinst_PROGRAMS = +if USE_BENCHMARK +noinst_PROGRAMS += bench +endif +if USE_TESTS +noinst_PROGRAMS += test test-verify +TESTS = test test-verify +endif + +bench_SOURCES = $(MINISKETCH_BENCH_SOURCES_INT) +bench_CPPFLAGS = $(AM_CPPFLAGS) $(RELEASE_DEFINES) +bench_LDADD = $(LIBMINISKETCH) +bench_LDFLAGS = $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +test_SOURCES = $(MINISKETCH_TEST_SOURCES_INT) +test_CPPFLAGS = $(AM_CPPFLAGS) $(RELEASE_DEFINES) +test_LDADD = $(LIBMINISKETCH) +test_LDFLAGS = $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +test_verify_SOURCES = $(MINISKETCH_TEST_SOURCES_INT) +test_verify_CPPFLAGS = $(AM_CPPFLAGS) $(VERIFY_DEFINES) +test_verify_LDADD = $(LIBMINISKETCH_VERIFY) +test_verify_LDFLAGS = $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +EXTRA_DIST= +EXTRA_DIST += LICENSE +EXTRA_DIST += README.md +EXTRA_DIST += doc/example.c +EXTRA_DIST += doc/gen_params.sage +EXTRA_DIST += doc/math.md +EXTRA_DIST += doc/moduli.md +EXTRA_DIST += doc/plot_bits.png +EXTRA_DIST += doc/plot_capacity.png +EXTRA_DIST += doc/plot_diff.png +EXTRA_DIST += doc/plot_size.png +EXTRA_DIST += doc/protocoltips.md +EXTRA_DIST += tests/pyminisketch.py diff --git a/README.md b/README.md new file mode 100644 index 0000000000..c0cfdc1623 --- /dev/null +++ b/README.md @@ -0,0 +1,210 @@ +# Minisketch: a library for [BCH](https://en.wikipedia.org/wiki/BCH_code)-based set reconciliation + + +`libminisketch` is an optimized standalone MIT-licensed library with C API for constructing and decoding *set sketches*, which can be used for compact set reconciliation and other applications. +It is an implementation of the PinSketch[[1]](#myfootnote1) algorithm. An explanation of the algorithm can be found [here](doc/math.md). + +## Sketches for set reconciliation + +Sketches, as produced by this library, can be seen as "set checksums" with two peculiar properties: +* Sketches have a predetermined capacity, and when the number of elements in the set is not higher than the capacity, `libminisketch` will always recover the entire set from the sketch. A sketch of *b*-bit elements with capacity *c* can be stored in *bc* bits. +* The sketches of two sets can be combined by adding them (XOR) to obtain a sketch of the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) between the two sets (*i.e.*, all elements that occur in one but not both input sets). + +This makes them appropriate for a very bandwidth-efficient set reconciliation protocol. If Alice and Bob each have a set of elements, and they suspect that the sets largely but not entirely overlap, +they can use the following protocol to let both parties learn all the elements: +* Alice and Bob both compute a sketch of their set elements. +* Alice sends her sketch to Bob. +* Bob combines the two sketches, and obtains a sketch of the symmetric difference. +* Bob tries to recover the elements from the difference sketch. +* Bob sends every element in the difference that he has to Alice. + +This will always succeed when the size of the difference (elements that Alice has but Bob doesn't plus elements that Bob has but Alice doesn't) does not exceed the +capacity of the sketch that Alice sent. The interesting part is that this works regardless of the actual set sizes—only the difference matters. + +If the elements are large, it may be preferable to compute the sketches over *hashes* of the set elements. In that case an additional step is added to the protocol, where Bob also sends the hash +of every element he does not have to Alice, who responds with the requested elements. + +The doc/ directory has additional [tips for designing reconciliation protocols using libminisketch](doc/protocoltips.md). + +## Evaluation + + + + + +**The first graph** above shows a benchmark of `libminisketch` against three other set reconciliation algorithms/implementations. The benchmarks were performed using a single core on a system with an Intel Core i7-7820HQ CPU with clock speed locked at 2.4 GHz. The diagram shows the time needed for merging of two sketches and decoding the result. The creation of a sketch on the same machine takes around 5 ns per capacity and per set element. The other implementations are: +* [`pinsketch`](https://www.cs.bu.edu/~reyzin/code/fuzzy.html), the original PinSketch implementation. +* [`cpisync`](https://github.com/trachten/cpisync), a software project which implements a number of set reconciliation algorithms and protocols. The included benchmark analyzes the non-probabilistic version of the original CPISync algorithm[[5]](#myfootnote5) only. +* A high-performance custom IBLT implementation using 4 hash functions and 32-bit checksums. + +For the largest sizes currently of interest to the authors, such as a set of capacity 4096 with 1024 differences, `libminisketch` is forty-nine times faster than `pinsketch` and over eight thousand times faster than `cpisync`. `libminisketch` is fast enough on realistic set sizes for use on high-traffic network servers where computational resources are limited. + +Even where performance is latency-limited, small minisketches can be fast enough to improve performance. On the above i7-7820HQ, a set of 2500 30-bit entries with a difference of 20 elements can be communicated in less time with a minisketch than sending the raw set so long as the communications bandwidth is 1 gigabit per second or less; an eight-element difference can be communicated in better than one-fifth the time on a gigabit link. + +**The second graph** above shows the performance of the same algorithms on the same system, but this time keeping the capacity constant at 128, while varying the number of differences to reconcile between 1 and 128. It shows how `cpisync`'s reconciliation speed is mostly dependent on capacity, while `pinsketch`/`libminisketch` are more dependent on number of differences. + +**The third graph** above shows the size overhead of a typical IBLT scheme over the other algorithms (which are near-optimal bandwidth), for various levels of failure probability. IBLT takes tens of times the bandwidth of `libminisketch` sketches when the set difference size is small and the required failure rate is low. + +**The fourth graph** above shows the effect of the field size on speed in `libminisketch`. The three lines correspond to: +* CLMUL 64-bit: Intel Core i7-7820HQ system at 2.4 GHz +* Generic 64-bit: POWER9 CP9M06 system at 2.8 GHz (Talos II) +* Generic 32-bit: Cortex-A53 at 1.2 GHz (Raspberry Pi 3B) + +It shows how CLMUL implementations are faster for certain fields (specifically, field sizes for which an irreducible polynomial of the form *xb + x + 1* over *GF(2)* exists, and to a lesser extent, fields which are a multiple of 8 bits). It also shows how (for now) a significant performance drop exists for fields larger than 32 bits on 32-bit platforms. Note that the three lines are not at the same scale (the Raspberry Pi 3B is around 10x slower for 32-bit fields than the Core i7; the POWER9 is around 1.3x slower). + +Below we compare the PinSketch algorithm (which `libminisketch` is an implementation of) with other set reconciliation algorithms: + +| Algorithm | Sketch size | Decode success | Decoding complexity | Difference type | Secure sketch | +| ----------------------------------------------------- | ------------------------- | ---------------| ------------------- | --------------- | ------------- | +| CPISync[[2]](#myfootnote2) | *(b+1)c* | Always | *O(n3)* | Both | Yes | +| PinSketch[[1]](#myfootnote1) | *bc* | Always | *O(n2)* | Symmetric only | Yes | +| IBLT[[6]](#myfootnote1)[[7]](#myfootnote1) | *αbc* (see graph 3) | Probabilistic | *O(n)* | Depends | No | + +* **Sketch size:** This column shows the size in bits of a sketch designed for reconciling *c* different *b*-bit elements. PinSketch and CPISync have a near-optimal[[11]](#myfootnote11) communication overhead, which in practice means the sketch size is very close (or equal to) *bc* bits. That is the same size as would be needed to transfer the elements of the difference naively (which is remarkable, as the difference isn't even known by the sender). For IBLT there is an overhead factor *α*, which depends on various design parameters, but is often between *2* and *10*. +* **Decode success:** Whenever a sketch is designed with a capacity not lower than the actual difference size, CPISync and PinSketch guarantee that decoding of the difference will always succeed. IBLT always has a chance of failure, though that chance can be made arbitrarily small by increasing the communication overhead. +* **Decoding complexity:** The space savings achieved by near-optimal algorithms come at a cost in performance, as their asymptotic decode complexity is quadratic or cubic, while IBLT is linear. This means that using near-optimal algorithms can be too expensive for applications where the difference is sufficiently large. +* **Difference type:** PinSketch can only compute the symmetric difference from a merged sketch, while CPISync and IBLT can distinguish which side certain elements were missing on. When the decoder has access to one of the sets, this generally doesn't matter, as he can look up each of the elements in the symmetric difference with one of the sets. +* **Secure sketch:** Whether the sketch satisfies the definition of a secure sketch[[1]](#myfootnote1), which implies a minimal amount about a set can be extracted from a sketch by anyone who does not know most of the elements already. This makes the algorithm appropriate for applications like fingerprint authentication. + +## Building + +The build system is very rudimentary for now, and [improvements](https://github.com/sipa/minisketch/pulls) are welcome. + +The following may work and produce a `libminisketch.a` file you can link against: + +```bash +git clone https://github.com/sipa/minisketch +cd minisketch +./autogen.sh && ./configure && make +``` + +## Usage + +In this section Alice and Bob are trying to find the difference between their sets. +Alice has the set *[3000 ... 3009]*, while Bob has *[3002 ... 3011]*. + +First, Alice creates a sketch: + +```c +#include +#include +#include "../include/minisketch.h" +int main(void) { + + minisketch *sketch_a = minisketch_create(12, 0, 4); +``` + +The arguments are: +* The field size *b*, which specifies the size of the elements being reconciled. With a field size *b*, the supported range of set elements is the integers from *1* to *2b* *- 1*, inclusive. Note that elements cannot be zero. +* The implementation number. Implementation *0* is always supported, but more efficient algorithms may be available on some hardware. The serialized form of a sketch is independent of the implementation, so different implementations can interoperate. +* The capacity *c*, which specifies how many differences the resulting sketch can reconcile. + +Then Alice adds her elements to her sketch. Note that adding the same element a second time removes it again, as sketches have set semantics, not multiset semantics. + +```c + for (int i = 3000; i < 3010; ++i) { + minisketch_add_uint64(sketch_a, i); + } +``` + +The next step is serializing the sketch into a byte array: + +```c + size_t sersize = minisketch_serialized_size(sketch_a); + assert(sersize == 12 * 4 / 8); // 4 12-bit values is 6 bytes. + unsigned char *buffer_a = malloc(sersize); + minisketch_serialize(sketch_a, buffer_a); + minisketch_destroy(sketch_a); +``` + +The contents of the buffer can then be submitted to Bob, who can create his own sketch: + +```c + minisketch *sketch_b = minisketch_create(12, 0, 4); // Bob's own sketch + for (int i = 3002; i < 3012; ++i) { + minisketch_add_uint64(sketch_b, i); + } +``` + +After Bob receives Alice's serialized sketch, he can reconcile: + +```c + sketch_a = minisketch_create(12, 0, 4); // Alice's sketch + minisketch_deserialize(sketch_a, buffer_a); // Load Alice's sketch + free(buffer_a); + + // Merge the elements from sketch_a into sketch_b. The result is a sketch_b + // which contains all elements that occurred in Alice's or Bob's sets, but not + // in both. + minisketch_merge(sketch_b, sketch_a); + + uint64_t differences[4]; + ssize_t num_differences = minisketch_decode(sketch_b, 4, differences); + minisketch_destroy(sketch_a); + minisketch_destroy(sketch_b); + if (num_differences < 0) { + printf("More than 4 differences!\n"); + } else { + ssize_t i; + for (i = 0; i < num_differences; ++i) { + printf("%u is in only one of the two sets\n", (unsigned)differences[i]); + } + } +} +``` + +In this example Bob would see output such as: + +``` +$ gcc -std=c99 -Wall -Wextra -o example ./doc/example.c -Lsrc/ -lminisketch -lstdc++ && ./example +3000 is in only one of the two sets +3011 is in only one of the two sets +3001 is in only one of the two sets +3010 is in only one of the two sets +``` + +The order of the output is arbitrary and will differ on different runs of minisketch_decode(). + +## Applications + +Communications efficient set reconciliation has been proposed to optimize Bitcoin transaction distribution[[8]](#myfootnote8), which would allow Bitcoin nodes to have many more peers while reducing bandwidth usage. It could also be used for Bitcoin block distribution[[9]](#myfootnote9), particularly for very low bandwidth links such as satellite. A similar approach (CPISync) is used by PGP SKS keyservers to synchronize their databases efficiently. Secure sketches can also be used as helper data to reliably extract a consistent cryptographic key from fuzzy biometric data while leaking minimal information[[1]](#myfootnote1). They can be combined with [dcnets](https://en.wikipedia.org/wiki/Dining_cryptographers_problem) to create cryptographic multiparty anonymous communication[[10]](#myfootnote10). + +## Implementation notes + +`libminisketch` is written in C++11, but has a [C API](include/minisketch.h) for compatibility reasons. + +Specific algorithms and optimizations used: +* Finite field implementations: + * A generic implementation using C unsigned integer bit operations, and one using the [CLMUL instruction](https://en.wikipedia.org/wiki/CLMUL_instruction_set) where available. The latter has specializations for different classes of fields that permit optimizations (those with trinomial irreducible polynomials, and those whose size is a multiple of 8 bits). + * Precomputed tables for (repeated) squaring, and for solving equations of the form *x2 + x = a*[[2]](#myfootnote2). + * Inverses are computed using an [exponentiation ladder](https://en.wikipedia.org/w/index.php?title=Exponentiation_by_squaring&oldid=868883860)[[12]](#myfootnote12) on systems where multiplications are relatively fast, and using an [extended GCD algorithm](https://en.wikipedia.org/w/index.php?title=Extended_Euclidean_algorithm&oldid=865802511#Computing_multiplicative_inverses_in_modular_structures) otherwise. + * Repeated multiplications are accelerated using runtime precomputations on systems where multiplications are relatively slow. + * The serialization of field elements always represents them as bits that are coefficients of the lowest-weight (using lexicographic order as tie breaker) irreducible polynomials over *GF(2)* (see [this list](doc/moduli.md)), but for some implementations they are converted to a different representation internally. +* The sketch algorithms are specialized for each separate field implementation, permitting inlining and specific optimizations while avoiding dynamic allocations and branching costs. +* Decoding of sketches uses the [Berlekamp-Massey algorithm](https://en.wikipedia.org/w/index.php?title=Berlekamp%E2%80%93Massey_algorithm&oldid=870768940)[[3]](#myfootnote3) to compute the characteristic polynomial. +* Finding the roots of polynomials is done using the Berlekamp trace algorithm with explicit formula for quadratic polynomials[[4]](#myfootnote4). The root finding is randomized to prevent adversarial inputs that intentionally trigger worst-case decode time. +* A (possibly) novel optimization combines a test for unique roots with the Berlekamp trace algorithm. + +Some improvements that are still TODO: +* Explicit formulas for the roots of polynomials of higher degree than 2 +* Subquadratic multiplication and modulus algorithms +* The [Half-GCD algorithm](http://mathworld.wolfram.com/Half-GCD.html) for faster GCDs +* An interface for incremental decoding: most of the computation in most failed decodes can be reused when attempting to decode a longer sketch of the same set +* Platform specific optimizations for platforms other than x86 +* Avoid using slow uint64_t for calculations on 32-bit hosts +* Optional IBLT / Hybrid and set entropy coder under the same interface + +## References + +* [1] Dodis, Ostrovsky, Reyzin and Smith. *Fuzzy Extractors: How to Generate Strong Keys from Biometrics and Other Noisy Data.* SIAM Journal on Computing, volume 38, number 1, pages 97-139, 2008). [[URL]](http://eprint.iacr.org/2003/235) [[PDF]](https://eprint.iacr.org/2003/235.pdf) +* [5] A. Trachtenberg, D. Starobinski and S. Agarwal. *Fast PDA synchronization using characteristic polynomial interpolation.* Proceedings, Twenty-First Annual Joint Conference of the IEEE Computer and Communications Societies, New York, NY, USA, 2002, pp. 1510-1519 vol.3. [[PDF]](https://pdfs.semanticscholar.org/43da/2070b6b7b2320a1fed2fd5e70e87332c9c5e.pdf) +* [2] Cherly, Jørgen, Luis Gallardo, Leonid Vaserstein, and Ethel Wheland. *Solving quadratic equations over polynomial rings of characteristic two.* Publicacions Matemàtiques (1998): 131-142. [[PDF]](https://www.raco.cat/index.php/PublicacionsMatematiques/article/viewFile/37927/40412) +* [3] J. Massey. *Shift-register synthesis and BCH decoding.* IEEE Transactions on Information Theory, vol. 15, no. 1, pp. 122-127, January 1969. [[PDF]](http://crypto.stanford.edu/~mironov/cs359/massey.pdf) +* [4] Bhaskar Biswas, Vincent Herbert. *Efficient Root Finding of Polynomials over Fields of Characteristic 2.* 2009. hal-00626997. [[URL]](https://hal.archives-ouvertes.fr/hal-00626997) [[PDF]](https://hal.archives-ouvertes.fr/hal-00626997/document) +* [6] Eppstein, David, Michael T. Goodrich, Frank Uyeda, and George Varghese. *What's the difference?: efficient set reconciliation without prior context.* ACM SIGCOMM Computer Communication Review, vol. 41, no. 4, pp. 218-229. ACM, 2011. [[PDF]](https://www.ics.uci.edu/~eppstein/pubs/EppGooUye-SIGCOMM-11.pdf) +* [7] Goodrich, Michael T. and Michael Mitzenmacher. *Invertible bloom lookup tables.* 2011 49th Annual Allerton Conference on Communication, Control, and Computing (Allerton) (2011): 792-799. [[PDF]](https://arxiv.org/pdf/1101.2245.pdf) +* [8] Maxwell, Gregory F. *[Blocksonly mode BW savings, the limits of efficient block xfer, and better relay](https://bitcointalk.org/index.php?topic=1377345.0)* Bitcointalk 2016, *[Technical notes on mempool synchronizing relay](https://people.xiph.org/~greg/mempool_sync_relay.txt)* #bitcoin-wizards 2016. +* [9] Maxwell, Gregory F. *[Block network coding](https://en.bitcoin.it/wiki/User:Gmaxwell/block_network_coding)* Bitcoin Wiki 2014, *[Technical notes on efficient block xfer](https://people.xiph.org/~greg/efficient.block.xfer.txt)* #bitcoin-wizards 2015. +* [10] Ruffing, Tim, Moreno-Sanchez, Pedro, Aniket, Kate, *P2P Mixing and Unlinkable Bitcoin Transactions* NDSS Symposium 2017 [[URL]](https://eprint.iacr.org/2016/824) [[PDF]](https://eprint.iacr.org/2016/824.pdf) +* [11] Y. Misky, A. Trachtenberg, R. Zippel. *Set Reconciliation with Nearly Optimal Communication Complexity.* Cornell University, 2000. [[URL]](https://ecommons.cornell.edu/handle/1813/5803) [[PDF]](https://ecommons.cornell.edu/bitstream/handle/1813/5803/2000-1813.pdf) +* [12] Itoh, Toshiya, and Shigeo Tsujii. "A fast algorithm for computing multiplicative inverses in GF (2m) using normal bases." Information and computation 78, no. 3 (1988): 171-177. [[URL]](https://www.sciencedirect.com/science/article/pii/0890540188900247) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000000..27417daf76 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Copyright (c) 2013-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +set -e +srcdir="$(dirname $0)" +cd "$srcdir" +if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then + LIBTOOLIZE="${GLIBTOOLIZE}" + export LIBTOOLIZE +fi +which autoreconf >/dev/null || \ + (echo "configuration failed, please install autoconf first" && exit 1) +autoreconf --install --force --warnings=all diff --git a/build-aux/m4/ax_check_compile_flag.m4 b/build-aux/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000000..bd753b34d7 --- /dev/null +++ b/build-aux/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/build-aux/m4/ax_check_link_flag.m4 b/build-aux/m4/ax_check_link_flag.m4 new file mode 100644 index 0000000000..03a30ce4c7 --- /dev/null +++ b/build-aux/m4/ax_check_link_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/build-aux/m4/ax_check_preproc_flag.m4 b/build-aux/m4/ax_check_preproc_flag.m4 new file mode 100644 index 0000000000..e43560fbd3 --- /dev/null +++ b/build-aux/m4/ax_check_preproc_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_preproc_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's +# preprocessor or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the preprocessor's default +# flags when the check is done. The check is thus made with the flags: +# "CPPFLAGS EXTRA-FLAGS FLAG". This can for example be used to force the +# preprocessor to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_PREPROC_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{COMPILE,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_PREPROC_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]cppflags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG preprocessor accepts $1], CACHEVAR, [ + ax_check_save_flags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $4 $1" + AC_PREPROC_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + CPPFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_PREPROC_FLAGS diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 0000000000..f7e5137003 --- /dev/null +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,962 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for no added switch, and then for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual ~Base() {} + virtual void f() {} + }; + + struct Derived : public Base + { + virtual ~Derived() override {} + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) diff --git a/ci/cirrus.sh b/ci/cirrus.sh new file mode 100755 index 0000000000..02f737ca7f --- /dev/null +++ b/ci/cirrus.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +set -e +set -x + +export LC_ALL=C + +env >> test_env.log + +$CC -v || true +valgrind --version || true + +./autogen.sh + +FIELDS= +if [ -n "$ENABLE_FIELDS" ]; then + FIELDS="--enable-fields=$ENABLE_FIELDS" +fi +./configure --host="$HOST" --enable-benchmark="$BENCH" $FIELDS + +# We have set "-j" in MAKEFLAGS. +make + +# Print information about binaries so that we can see that the architecture is correct +file test* || true +file bench* || true +file .libs/* || true + +if [ -n "$BUILD" ] +then + make "$BUILD" +fi + +if [ -n "$EXEC_CMD" ]; then + $EXEC_CMD ./test $TESTRUNS + $EXEC_CMD ./test-verify $TESTRUNS +fi + +if [ "$BENCH" = "yes" ]; then + $EXEC_CMD ./bench +fi diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile new file mode 100644 index 0000000000..63e5412ee7 --- /dev/null +++ b/ci/linux-debian.Dockerfile @@ -0,0 +1,17 @@ +FROM debian:stable + +RUN dpkg --add-architecture i386 +RUN dpkg --add-architecture s390x +RUN apt-get update + +# dkpg-dev: to make pkg-config work in cross-builds +RUN apt-get install --no-install-recommends --no-upgrade -y \ + git ca-certificates \ + make automake libtool pkg-config dpkg-dev valgrind qemu-user \ + gcc g++ clang libc6-dbg \ + gcc-i686-linux-gnu g++-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \ + g++-s390x-linux-gnu gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \ + wine g++-mingw-w64-x86-64 + +# Run a dummy command in wine to make it set up configuration +RUN wine true || true diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000000..9dc66e7fd2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,162 @@ +AC_INIT([minisketch], [0.0.1], [http://github.com/sipa/minisketch/]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PREREQ(2.60) +AC_CONFIG_SRCDIR([src/minisketch.cpp]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AM_INIT_AUTOMAKE([subdir-objects foreign]) + +LT_INIT +LT_LANG([C++]) +AC_LANG([C++]) + +AC_PATH_PROG(CCACHE,ccache) + +AC_ARG_ENABLE([ccache], + [AS_HELP_STRING([--disable-ccache], + [do not use ccache for building (default is to use if found)])], + [use_ccache=$enableval], + [use_ccache=auto]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + [use_tests=$enableval], + [use_tests=yes]) + +AC_ARG_ENABLE(benchmark, + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + [use_benchmark=$enableval], + [use_benchmark=no]) + +m4_define([SUPPORTED_FIELDS], [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64]) + +AC_MSG_CHECKING([which field sizes to build]) +AC_ARG_ENABLE([fields], AS_HELP_STRING([--enable-fields=LIST], [Comma-separated list of field sizes to build. Default=all. Available sizes:] m4_translit(m4_defn([SUPPORTED_FIELDS]), [,], [ ])), [], [enable_fields=SUPPORTED_FIELDS]) +have_disabled_fields=no +have_enabled_fields=no +m4_foreach([FIELD], [SUPPORTED_FIELDS], [ + case ",$enable_fields," in + *,FIELD,*) + have_enabled_fields=yes + ;; + *) + AC_DEFINE(DISABLE_FIELD_[]FIELD, [1], + [Define to 1 to remove support for field size] FIELD [.]) + have_disabled_fields=yes + ;; + esac +]) +AC_MSG_RESULT([$enable_fields]) +if test "x$have_enabled_fields" = xno; then + AC_MSG_ERROR([No field sizes are enabled.]) +fi + +AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) + +AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) +enable_clmul= +AX_CHECK_COMPILE_FLAG([-mpclmul],[[enable_clmul=yes]],,[[$CXXFLAG_WERROR]],[AC_LANG_PROGRAM([ + #include + #include +], [ + __m128i a = _mm_cvtsi64_si128((uint64_t)7); + __m128i b = _mm_clmulepi64_si128(a, a, 37); + __m128i c = _mm_srli_epi64(b, 41); + __m128i d = _mm_xor_si128(b, c); + uint64_t e = _mm_cvtsi128_si64(d); + return e == 0; +])]) +if test x$enable_clmul = xyes; then + CLMUL_CXXFLAGS="-mpclmul" + AC_DEFINE(HAVE_CLMUL, 1, [Define this symbol if clmul instructions can be used]) +fi + + +AC_MSG_CHECKING(for working clz builtins) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ + unsigned a = __builtin_clz(1); + unsigned long b = __builtin_clzl(1); + unsigned long long c = __builtin_clzll(1); + ])], + [ + AC_DEFINE(HAVE_CLZ, 1, [Define this symbol if clz builtins are present and working]) + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + ] +) + +AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]],[LDFLAGS="-Wl,--exclude-libs,ALL $LDFLAGS"]) + +case $host in + *mingw*) + dnl -static is interpreted by libtool, where it has a different meaning. + dnl In libtool-speak, it's -all-static. + AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) + ;; + *) + AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="-static"]) + ;; +esac + +AX_CHECK_COMPILE_FLAG([-Wall],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],[],[$CXXFLAG_WERROR]) + +## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all +## unknown options if any other warning is produced. Test the -Wfoo case, and +## set the -Wno-foo case if it works. +AX_CHECK_COMPILE_FLAG([-Wshift-count-overflow],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-shift-count-overflow"],,[[$CXXFLAG_WERROR]]) + +if test "x$use_ccache" != "xno"; then + AC_MSG_CHECKING(if ccache should be used) + if test x$CCACHE = x; then + if test "x$use_ccache" = "xyes"; then + AC_MSG_ERROR([ccache not found.]); + else + use_ccache=no + fi + else + use_ccache=yes + CC="$ac_cv_path_CCACHE $CC" + CXX="$ac_cv_path_CCACHE $CXX" + fi + AC_MSG_RESULT($use_ccache) +fi +if test "x$use_ccache" = "xyes"; then + AX_CHECK_COMPILE_FLAG([-Qunused-arguments],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Qunused-arguments"],,[[$CXXFLAG_WERROR]]) +fi + +VERIFY_DEFINES=-DMINISKETCH_VERIFY +RELEASE_DEFINES= + +AC_CONFIG_FILES([ + Makefile +]) + +AC_SUBST(CLMUL_CXXFLAGS) +AC_SUBST(WARN_CXXFLAGS) +AC_SUBST(NOWARN_CXXFLAGS) +AC_SUBST(VERIFY_DEFINES) +AC_SUBST(RELEASE_DEFINES) +AC_SUBST(LIBTOOL_APP_LDFLAGS) +AM_CONDITIONAL([ENABLE_CLMUL],[test x$enable_clmul = xyes]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) +AC_OUTPUT + +echo +echo "Build Options:" +echo " with benchmarks = $use_benchmark" +echo " with tests = $use_tests" +echo " enable clmul fields = $enable_clmul" +echo " CXX = $CXX" +echo " CXXFLAGS = $CXXFLAGS" +echo " CPPFLAGS = $CPPFLAGS" +echo " LDFLAGS = $LDFLAGS" +if test "$have_disabled_fields" = "yes"; then +echo +echo "Only compiling in support for field sizes: $enable_fields" +echo "WARNING: this means the library will lack support for other field sizes entirely" +fi diff --git a/doc/example.c b/doc/example.c new file mode 100644 index 0000000000..7279165845 --- /dev/null +++ b/doc/example.c @@ -0,0 +1,51 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include +#include "../include/minisketch.h" + +int main(void) { + + minisketch *sketch_a = minisketch_create(12, 0, 4); + + for (int i = 3000; i < 3010; ++i) { + minisketch_add_uint64(sketch_a, i); + } + + size_t sersize = minisketch_serialized_size(sketch_a); + assert(sersize == 12 * 4 / 8); // 4 12-bit values is 6 bytes. + unsigned char *buffer_a = malloc(sersize); + minisketch_serialize(sketch_a, buffer_a); + minisketch_destroy(sketch_a); + + minisketch *sketch_b = minisketch_create(12, 0, 4); // Bob's own sketch + for (int i = 3002; i < 3012; ++i) { + minisketch_add_uint64(sketch_b, i); + } + + sketch_a = minisketch_create(12, 0, 4); // Alice's sketch + minisketch_deserialize(sketch_a, buffer_a); // Load Alice's sketch + free(buffer_a); + + // Merge the elements from sketch_a into sketch_b. The result is a sketch_b + // which contains all elements that occurred in Alice's or Bob's sets, but not + // in both. + minisketch_merge(sketch_b, sketch_a); + + uint64_t differences[4]; + ssize_t num_differences = minisketch_decode(sketch_b, 4, differences); + minisketch_destroy(sketch_a); + minisketch_destroy(sketch_b); + if (num_differences < 0) { + printf("More than 4 differences!\n"); + } else { + ssize_t i; + for (i = 0; i < num_differences; ++i) { + printf("%u is in only one of the two sets\n", (unsigned)differences[i]); + } + } +} diff --git a/doc/gen_basefpbits.sage b/doc/gen_basefpbits.sage new file mode 100644 index 0000000000..d1e75a6e29 --- /dev/null +++ b/doc/gen_basefpbits.sage @@ -0,0 +1,78 @@ +# Require exact values up to +FPBITS = 256 + +# Overkill accuracy +F = RealField(400) + +def BaseFPBits(bits, capacity): + return bits * capacity - int(ceil(F(log(sum(binomial(2**bits - 1, i) for i in range(capacity+1)), 2)))) + +def Log2Factorial(capacity): + return int(floor(log(factorial(capacity), 2))) + +print("uint64_t BaseFPBits(uint32_t bits, uint32_t capacity) {") +print(" // Correction table for low bits/capacities") +TBLS={} +FARS={} +SKIPS={} +for bits in range(1, 32): + TBL = [] + for capacity in range(1, min(2**bits, FPBITS)): + exact = BaseFPBits(bits, capacity) + approx = Log2Factorial(capacity) + TBL.append((exact, approx)) + MIN = 10000000000 + while len(TBL) and ((TBL[-1][0] == TBL[-1][1]) or (TBL[-1][0] >= FPBITS and TBL[-1][1] >= FPBITS)): + MIN = min(MIN, TBL[-1][0] - TBL[-1][1]) + TBL.pop() + while len(TBL) and (TBL[-1][0] - TBL[-1][1] == MIN): + TBL.pop() + SKIP = 0 + while SKIP < len(TBL) and TBL[SKIP][0] == TBL[SKIP][1]: + SKIP += 1 + DIFFS = [TBL[i][0] - TBL[i][1] for i in range(SKIP, len(TBL))] + if len(DIFFS) > 0 and len(DIFFS) * Integer(max(DIFFS)).nbits() > 64: + print(" static constexpr uint8_t ADD%i[] = {%s};" % (bits, ", ".join(("%i" % (TBL[i][0] - TBL[i][1])) for i in range(SKIP, len(TBL))))) + TBLS[bits] = DIFFS + FARS[bits] = MIN + SKIPS[bits] = SKIP +print("") +print(" if (capacity == 0) return 0;") +print(" uint64_t ret = 0;") +print(" if (bits < 32 && capacity >= (1U << bits)) {") +print(" ret = uint64_t{bits} * (capacity - (1U << bits) + 1);") +print(" capacity = (1U << bits) - 1;") +print(" }") +print(" ret += Log2Factorial(capacity);") +print(" switch (bits) {") +for bits in sorted(TBLS.keys()): + if len(TBLS[bits]) == 0: + continue + width = Integer(max(TBLS[bits])).nbits() + if len(TBLS[bits]) == 1: + add = "%i" % TBLS[bits][0] + elif len(TBLS[bits]) * width <= 64: + code = sum((2**(width*i) * TBLS[bits][i]) for i in range(len(TBLS[bits]))) + if width == 1: + add = "(0x%x >> (capacity - %i)) & 1" % (code, 1 + SKIPS[bits]) + else: + add = "(0x%x >> %i * (capacity - %i)) & %i" % (code, width, 1 + SKIPS[bits], 2**width - 1) + else: + add = "ADD%i[capacity - %i]" % (bits, 1 + SKIPS[bits]) + if len(TBLS[bits]) + SKIPS[bits] == 2**bits - 1: + print(" case %i: return ret + (capacity <= %i ? 0 : %s);" % (bits, SKIPS[bits], add)) + else: + print(" case %i: return ret + (capacity <= %i ? 0 : capacity > %i ? %i : %s);" % (bits, SKIPS[bits], len(TBLS[bits]) + SKIPS[bits], FARS[bits], add)) +print(" default: return ret;") +print(" }") +print("}") + +print("void TestBaseFPBits() {") +print(" static constexpr uint16_t TBL[20][100] = {%s};" % (", ".join("{" + ", ".join(("%i" % BaseFPBits(bits, capacity)) for capacity in range(0, 100)) + "}" for bits in range(1, 21)))) +print(" for (int bits = 1; bits <= 20; ++bits) {") +print(" for (int capacity = 0; capacity < 100; ++capacity) {") +print(" uint64_t computed = BaseFPBits(bits, capacity), exact = TBL[bits - 1][capacity];") +print(" CHECK(exact == computed || (exact >= 256 && computed >= 256));") +print(" }") +print(" }") +print("}") diff --git a/doc/gen_params.sage b/doc/gen_params.sage new file mode 100755 index 0000000000..1cf036adb4 --- /dev/null +++ b/doc/gen_params.sage @@ -0,0 +1,333 @@ +#!/usr/bin/env sage +r""" +Generate finite field parameters for minisketch. + +This script selects the finite fields used by minisketch + for various sizes and generates the required tables for + the implementation. + +The output (after formatting) can be found in src/fields/*.cpp. + +""" +B. = GF(2) +P.

= B[] + +def apply_map(m, v): + r = 0 + i = 0 + while v != 0: + if (v & 1): + r ^^= m[i] + i += 1 + v >>= 1 + return r + +def recurse_moduli(acc, maxweight, maxdegree): + for pos in range(maxweight, maxdegree + 1, 1): + poly = acc + p^pos + if maxweight == 1: + if poly.is_irreducible(): + return (pos, poly) + else: + (deg, ret) = recurse_moduli(poly, maxweight - 1, pos - 1) + if ret is not None: + return (pos, ret) + return (None, None) + +def compute_moduli(bits): + # Return all optimal irreducible polynomials for GF(2^bits) + # The result is a list of tuples (weight, degree of second-highest nonzero coefficient, polynomial) + maxdegree = bits - 1 + result = [] + for weight in range(1, bits, 2): + deg, res = None, None + while True: + ret = recurse_moduli(p^bits + 1, weight, maxdegree) + if ret[0] is not None: + (deg, res) = ret + maxdegree = deg - 1 + else: + break + if res is not None: + result.append((weight + 2, deg, res)) + return result + +def bits_to_int(vals): + ret = 0 + base = 1 + for val in vals: + ret += Integer(val) * base + base *= 2 + return ret + +def sqr_table(f, bits, n=1): + ret = [] + for i in range(bits): + ret.append((f^(2^n*i)).integer_representation()) + return ret + +# Compute x**(2**n) +def pow2(x, n): + for i in range(n): + x = x**2 + return x + +def qrt_table(F, f, bits): + # Table for solving x2 + x = a + # This implements the technique from https://www.raco.cat/index.php/PublicacionsMatematiques/article/viewFile/37927/40412, Lemma 1 + for i in range(bits): + if (f**i).trace() != 0: + u = f**i + ret = [] + for i in range(0, bits): + d = f^i + y = sum(pow2(d, j) * sum(pow2(u, k) for k in range(j)) for j in range(1, bits)) + ret.append(y.integer_representation() ^^ (y.integer_representation() & 1)) + return ret + +def conv_tables(F, NF, bits): + # Generate a F(2) linear projection that maps elements from one field + # to an isomorphic field with a different modulus. + f = F.gen() + fp = f.minimal_polynomial() + assert(fp == F.modulus()) + nfp = fp.change_ring(NF) + nf = sorted(nfp.roots(multiplicities=False))[0] + ret = [] + matrepr = [[B(0) for x in range(bits)] for y in range(bits)] + for i in range(bits): + val = (nf**i).integer_representation() + ret.append(val) + for j in range(bits): + matrepr[j][i] = B((val >> j) & 1) + mat = Matrix(matrepr).inverse().transpose() + ret2 = [] + for i in range(bits): + ret2.append(bits_to_int(mat[i])) + + for t in range(100): + f1a = F.random_element() + f1b = F.random_element() + f1r = f1a * f1b + f2a = NF.fetch_int(apply_map(ret, f1a.integer_representation())) + f2b = NF.fetch_int(apply_map(ret, f1b.integer_representation())) + f2r = NF.fetch_int(apply_map(ret, f1r.integer_representation())) + f2s = f2a * f2b + assert(f2r == f2s) + + for t in range(100): + f2a = NF.random_element() + f2b = NF.random_element() + f2r = f2a * f2b + f1a = F.fetch_int(apply_map(ret2, f2a.integer_representation())) + f1b = F.fetch_int(apply_map(ret2, f2b.integer_representation())) + f1r = F.fetch_int(apply_map(ret2, f2r.integer_representation())) + f1s = f1a * f1b + assert(f1r == f1s) + + return (ret, ret2) + +def fmt(i,typ): + if i == 0: + return "0" + else: + return "0x%x" % i + +def lintranstype(typ, bits, maxtbl): + gsize = min(maxtbl, bits) + array_size = (bits + gsize - 1) // gsize + bits_list = [] + total = 0 + for i in range(array_size): + rsize = (bits - total + array_size - i - 1) // (array_size - i) + total += rsize + bits_list.append(rsize) + return "RecLinTrans<%s, %s>" % (typ, ", ".join("%i" % x for x in bits_list)) + +INT=0 +CLMUL=1 +CLMUL_TRI=2 +MD=3 + +def print_modulus_md(mod): + ret = "" + pos = mod.degree() + for c in reversed(list(mod)): + if c: + if ret: + ret += " + " + if pos == 0: + ret += "1" + elif pos == 1: + ret += "x" + else: + ret += "x%i" % pos + pos -= 1 + return ret + +def pick_modulus(bits, style): + # Choose the lexicographicly-first lowest-weight modulus + # optionally subject to implementation specific constraints. + moduli = compute_moduli(bits) + if style == INT or style == MD: + multi_sqr = False + need_trans = False + elif style == CLMUL: + # Fast CLMUL reduction requires that bits + the highest + # set bit are less than 66. + moduli = list(filter((lambda x: bits+x[1] <= 66), moduli)) + moduli + multi_sqr = True + need_trans = True + if not moduli or moduli[0][2].change_ring(ZZ)(2) == 3 + 2**bits: + # For modulus 3, CLMUL_TRI is obviously better. + return None + elif style == CLMUL_TRI: + moduli = list(filter(lambda x: bits+x[1] <= 66, moduli)) + moduli + moduli = list(filter(lambda x: x[0] == 3, moduli)) + multi_sqr = True + need_trans = True + else: + assert(False) + if not moduli: + return None + return moduli[0][2] + +def print_result(bits, style): + if style == INT: + multi_sqr = False + need_trans = False + table_id = "%i" % bits + elif style == MD: + pass + elif style == CLMUL: + multi_sqr = True + need_trans = True + table_id = "%i" % bits + elif style == CLMUL_TRI: + multi_sqr = True + need_trans = True + table_id = "TRI%i" % bits + else: + assert(False) + + nmodulus = pick_modulus(bits, INT) + modulus = pick_modulus(bits, style) + if modulus is None: + return + + if style == MD: + print("* *%s*" % print_modulus_md(modulus)) + return + + if bits > 32: + typ = "uint64_t" + elif bits > 16: + typ = "uint32_t" + elif bits > 8: + typ = "uint16_t" + else: + typ = "uint8_t" + + ttyp = lintranstype(typ, bits, 4) + rtyp = lintranstype(typ, bits, 6) + + F. = GF(2**bits, modulus=modulus) + + include_table = True + if style != INT and style != CLMUL: + cmodulus = pick_modulus(bits, CLMUL) + if cmodulus == modulus: + include_table = False + table_id = "%i" % bits + + if include_table: + print("typedef %s StatTable%s;" % (rtyp, table_id)) + rtyp = "StatTable%s" % table_id + if (style == INT): + print("typedef %s DynTable%s;" % (ttyp, table_id)) + ttyp = "DynTable%s" % table_id + + if need_trans: + if modulus != nmodulus: + # If the bitstream modulus is not the best modulus for + # this implementation a conversion table will be needed. + ctyp = rtyp + NF. = GF(2**bits, modulus=nmodulus) + ctables = conv_tables(NF, F, bits) + loadtbl = "&LOAD_TABLE_%s" % table_id + savetbl = "&SAVE_TABLE_%s" % table_id + if include_table: + print("constexpr %s LOAD_TABLE_%s({%s});" % (ctyp, table_id, ", ".join([fmt(x,typ) for x in ctables[0]]))) + print("constexpr %s SAVE_TABLE_%s({%s});" % (ctyp, table_id, ", ".join([fmt(x,typ) for x in ctables[1]]))) + else: + ctyp = "IdTrans" + loadtbl = "&ID_TRANS" + savetbl = "&ID_TRANS" + else: + assert(modulus == nmodulus) + + if include_table: + print("constexpr %s SQR_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in sqr_table(f, bits, 1)]))) + if multi_sqr: + # Repeated squaring is a linearised polynomial so in F(2^n) it is + # F(2) linear and can be computed by a simple bit-matrix. + # Repeated squaring is especially useful in powering ladders such as + # for inversion. + # When certain repeated squaring tables are not in use, use the QRT + # table instead to make the C++ compiler happy (it always has the + # same type). + sqr2 = "&QRT_TABLE_%s" % table_id + sqr4 = "&QRT_TABLE_%s" % table_id + sqr8 = "&QRT_TABLE_%s" % table_id + sqr16 = "&QRT_TABLE_%s" % table_id + if ((bits - 1) >= 4): + if include_table: + print("constexpr %s SQR2_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in sqr_table(f, bits, 2)]))) + sqr2 = "&SQR2_TABLE_%s" % table_id + if ((bits - 1) >= 8): + if include_table: + print("constexpr %s SQR4_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in sqr_table(f, bits, 4)]))) + sqr4 = "&SQR4_TABLE_%s" % table_id + if ((bits - 1) >= 16): + if include_table: + print("constexpr %s SQR8_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in sqr_table(f, bits, 8)]))) + sqr8 = "&SQR8_TABLE_%s" % table_id + if ((bits - 1) >= 32): + if include_table: + print("constexpr %s SQR16_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in sqr_table(f, bits, 16)]))) + sqr16 = "&SQR16_TABLE_%s" % table_id + if include_table: + print("constexpr %s QRT_TABLE_%s({%s});" % (rtyp, table_id, ", ".join([fmt(x,typ) for x in qrt_table(F, f, bits)]))) + + modulus_weight = modulus.hamming_weight() + modulus_degree = (modulus - p**bits).degree() + modulus_int = (modulus - p**bits).change_ring(ZZ)(2) + + lfsr = "" + + if style == INT: + print("typedef Field<%s, %i, %i, %s, %s, &SQR_TABLE_%s, &QRT_TABLE_%s%s> Field%i;" % (typ, bits, modulus_int, rtyp, ttyp, table_id, table_id, lfsr, bits)) + elif style == CLMUL: + print("typedef Field<%s, %i, %i, %s, &SQR_TABLE_%s, %s, %s, %s, %s, &QRT_TABLE_%s, %s, %s, %s%s> Field%i;" % (typ, bits, modulus_int, rtyp, table_id, sqr2, sqr4, sqr8, sqr16, table_id, ctyp, loadtbl, savetbl, lfsr, bits)) + elif style == CLMUL_TRI: + print("typedef FieldTri<%s, %i, %i, %s, &SQR_TABLE_%s, %s, %s, %s, %s, &QRT_TABLE_%s, %s, %s, %s> FieldTri%i;" % (typ, bits, modulus_degree, rtyp, table_id, sqr2, sqr4, sqr8, sqr16, table_id, ctyp, loadtbl, savetbl, bits)) + else: + assert(False) + +for bits in range(2, 65): + print("#ifdef ENABLE_FIELD_INT_%i" % bits) + print("// %i bit field" % bits) + print_result(bits, INT) + print("#endif") + print("") + +for bits in range(2, 65): + print("#ifdef ENABLE_FIELD_INT_%i" % bits) + print("// %i bit field" % bits) + print_result(bits, CLMUL) + print_result(bits, CLMUL_TRI) + print("#endif") + print("") + +for bits in range(2, 65): + print_result(bits, MD) diff --git a/doc/log2_factorial.sage b/doc/log2_factorial.sage new file mode 100644 index 0000000000..afc6d66c57 --- /dev/null +++ b/doc/log2_factorial.sage @@ -0,0 +1,85 @@ +import bisect + +INPUT_BITS = 32 +TABLE_BITS = 5 +INT_BITS = 64 +EXACT_FPBITS = 256 + +F = RealField(100) # overkill + +def BestOverApproxInvLog2(mulof, maxd): + """ + Compute denominator of an approximation of 1/log(2). + + Specifically, find the value of d (<= maxd, and a multiple of mulof) + such that ceil(d/log(2))/d is the best approximation of 1/log(2). + """ + dist=1 + best=0 + # Precomputed denominators that lead to good approximations of 1/log(2) + for d in [1, 2, 9, 70, 131, 192, 445, 1588, 4319, 11369, 18419, 25469, 287209, 836158, 3057423, 8336111, 21950910, 35565709, 49180508, 161156323, 273132138, 385107953, 882191721]: + kd = lcm(mulof, d) + if kd <= maxd: + n = ceil(kd / log(2)) + dis = F((n / kd) - 1 / log(2)) + if dis < dist: + dist = dis + best = kd + return best + + +LOG2_TABLE = [] +A = 0 +B = 0 +C = 0 +D = 0 +K = 0 + +def Setup(k): + global LOG2_TABLE, A, B, C, D, K + K = k + LOG2_TABLE = [] + for i in range(2 ** TABLE_BITS): + LOG2_TABLE.append(int(floor(F(K * log(1 + i / 2**TABLE_BITS, 2))))) + + # Maximum for (2*x+1)*LogK2(x) + max_T = (2^(INPUT_BITS + 1) - 1) * (INPUT_BITS*K - 1) + # Maximum for A + max_A = (2^INT_BITS - 1) // max_T + D = BestOverApproxInvLog2(2 * K, max_A * 2 * K) + A = D // (2 * K) + B = int(ceil(F(D/log(2)))) + C = int(floor(F(D*log(2*pi,2)/2))) + +def LogK2(n): + assert(n >= 1 and n < (1 << INPUT_BITS)) + bits = Integer(n).nbits() + return K * (bits - 1) + LOG2_TABLE[((n << (INPUT_BITS - bits)) >> (INPUT_BITS - TABLE_BITS - 1)) - 2**TABLE_BITS] + +def Log2Fact(n): + # Use formula (A*(2*x+1)*LogK2(x) - B*x + C) / D + return (A*(2*n+1)*LogK2(n) - B*n + C) // D + (n < 3) + +RES = [int(F(log(factorial(i),2))) for i in range(EXACT_FPBITS * 10)] + +best_worst_ratio = 0 + +for K in range(1, 10000): + Setup(K) + assert(LogK2(1) == 0) + assert(LogK2(2) == K) + assert(LogK2(4) == 2 * K) + good = True + worst_ratio = 1 + for i in range(1, EXACT_FPBITS * 10): + exact = RES[i] + approx = Log2Fact(i) + if not (approx <= exact and ((approx == exact) or (approx >= EXACT_FPBITS and exact >= EXACT_FPBITS))): + good = False + break + if worst_ratio * exact > approx: + worst_ratio = approx / exact + if good and worst_ratio > best_worst_ratio: + best_worst_ratio = worst_ratio + print("Formula: (%i*(2*x+1)*floor(%i*log2(x)) - %i*x + %i) / %i; log(max_ratio)=%f" % (A, K, B, C, D, RR(-log(worst_ratio)))) + print("LOG2K_TABLE: %r" % LOG2_TABLE) diff --git a/doc/math.md b/doc/math.md new file mode 100644 index 0000000000..cf46f193ab --- /dev/null +++ b/doc/math.md @@ -0,0 +1,117 @@ +# The mathematics of Minisketch sketches + +This is an unconventional mathematical overview of the PinSketch algorithm without references to coding theory[[1]](#myfootnote1). + +## Set sketches + +A sketch, for the purpose of this description, can be seen as a "set checksum" with two peculiar properties: + +* Sketches have a predetermined capacity, and when the number of elements in the set is not higher than the capacity, minisketch will always recover the entire set from the sketch. A sketch of *b*-bit elements with capacity *c* can be stored in *bc* bits. +* The sketches of two sets can be combined by adding them (XOR) to obtain a sketch of the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) between the two sets (*i.e.*, all elements that occur in one but not both input sets). + +This overview explains how sets can be converted into a sketch and how a set can be recovered from a sketch. + +## From field elements to sketches + +**Data entries as field elements** + +Every integer in the range *[1...2b-1]* (the acceptable data elements for a Minisketch sketch with field size *b*) can be mapped to a nonzero field element of *GF(2b)*. In this [finite field](https://en.wikipedia.org/wiki/Finite_field), we can add and multiply elements together, with many of the expected properties for those operations. Addition (and subtraction!) of field elements corresponds to bitwise XOR of the integers they correspond to, though multiplication is more involved. + +**Sets as power series** + +We define a function *S* which maps field elements *m* to the following [formal power series](https://en.wikipedia.org/wiki/Formal_power_series) (similar to a polynomial, except there can be an infinite number of terms, and we don't care about concepts like convergence as we're never going to actually evaluate it for a specific value of *x*): + +* *S(m) = 1 + mx + m2x2 + m3x3 + ...*. + +We then extend this function to operate on sets of field elements, by adding together the images of every set element. If *M = {m1, m2, ... }*: + +* *S(M) = S({m1,m2,...}) = S(m1) + S(m2) + ... = (1 + 1 + ...) + (m1 + m2 + ...)x + (m12 + m22 + ...)x2 + (m13 + ...* + +Because in our field addition corresponds to XOR of integers, it holds for every *a* that *a + a = 0*. This carries over to the *S* function, meaning that *S(a) + S(a) = 0* for every *a*. This means that the coefficients of these power series have the second of the properties we +desire from a sketch, namely that an efficient operation exists to +combine two sketches such that the result is a sketch of the symmetric +difference of the sets. It holds that +*S({m1,m2}) + S({m2,m3}) = S(m1) + (S(m2) + S(m2)) + S(m3) = S(m1) + S(m3) = S({m1,m3})*. The question is whether we can also efficiently recover the elements from their power series' coefficients. + +**An infinity of coefficients is hard** + +To make reasoning about these power series easier, notice that the series for a single element is in fact a [geometric series](https://en.wikipedia.org/wiki/Geometric_series). If we were working over real numbers rather than a finite field and *|mx| < 1*, it would converge to *(1 - mx)-1*. Convergence has no meaning in formal power series, however it is still the case that: + +* *(1 - mx) S(m) = 1* + +You can verify this by seeing that every coefficient except the constant one gets cancelled out by the multiplication. This can be generalized to the series for multiple set elements. For two elements we have: + +* *(1 - m1x) (1 - m2x) S({m1,m2}) = (1 - m1x) (1 - m2x) (S(m1) + S(m2)) = (1 - m2x) + (1 - m1x)* + +And for three: + +* *(1 - m1x) (1 - m2x) (1 - m3x) S({m1,m2,m3}) = (1 - m1x) (1 - m2x) (1 - m3x) (S(m1) + S(m2) + S(m3)) = (1 - m2x)(1 - m3x) + (1 - m1x)(1 - m3x) + (1 - m1x)(1 - m2x)* + +In each case, we notice that multiplying *S(M)* with *(1 - mix)* for each element *mi ∈ M* results in a polynomial of degree *n-1*. + +**Solving for the set elements** + +The above insight lets us build a solver that extracts the set elements from the coefficients of a power series. If we can find a polynomial *L* that is the product of *n* different *(1 - mix)* factors for various values of *mi*, such that *P = S(M)L* is an *n-1* degree polynomial, then those values *mi* are the elements of *M*. + +The coefficients of *P* are nontrivial expressions of the set elements themselves. However, we can just focus on the coefficients of degree *n* and higher in *P*, as those are all 0. Let *si* be the coefficients of *S(M)*, and *li* the coefficients of L. In other words, *S(M) = s0 + s1x + s2x2 + s3x3 + ...* and *L = l0 + l1x + l2x2 + l3x3 + ... + lnxn*. Note that *l0 = 1*, as it is the product of all the *1* terms in the *(1 - mix)* factors. + +Here are the equations for the coefficients of *S(M)L* of degree *n+1* through *2n*: +* *sn+1 + sn+0l1 + sn-1l2 + sn-2l3 + ... + s1ln = 0* +* *sn+2 + sn+1l1 + sn+0l2 + sn-1l3 + ... + s2ln = 0* +* *sn+3 + sn+2l1 + sn+1l2 + sn+0l3 + ... + s3ln = 0* +* ... +* *s2n + s2n-1l1 + s2n-2l2 + s2n-3l3 + ... + snln = 0* + +These are *n* linear equations with *n* unknowns (the *li* +values, for *i=1..n*), which can be solved using [Gaussian elimination](https://en.wikipedia.org/wiki/Gaussian_elimination). After doing so, +we have the coefficients of *L*, which can then be [factored](https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields) +into first degree factors of the form *(1 - mix)*. The resulting *m* values are our set elements. + +**Putting it all together** + +Interestingly, only *2n* coefficients of *S(M)* were needed for solving +the set of equations above. This means we have our answer: the +coefficients *1* through *2n* of *S(M)*, or the list +*[m1 + m2 + ..., m12 + m22 + ..., ..., m12n + m22n + ...]* +functions as a sketch, satisfying the two properties we want: + +* Sketches can be combined to form the sketch of their symmetric difference, by simply pairwise adding the list elements together. +* With *2n* list elements we can efficiently recover *n* elements from a sketch. + +**Capacity and difference** + +The approach above only works when the number of elements *n* in the sketch is known. Of course we want to support cases where only an upper bound on the number of elements in the sketch is known, the capacity *c*. Given that we can reconstruct a set of size *c* from a sketch with *2c* terms, we should be able to reconstruct a set of size *n* too as long as *n ≤ c*. This is simply a matter of trying to solve the above set of equations assuming values of *n* that count down from *c* until a solution is found for one. This is known as the [Peterson-Gorenstein-Zierler algorithm](https://en.wikipedia.org/wiki/BCH_code#Peterson%E2%80%93Gorenstein%E2%80%93Zierler_algorithm). + +## Optimizations + +**Halving the sketch size** + +We can in fact only include the odd terms in the sketch, and reconstruct the even ones before solving the equation to find *L*. This means the size of a sketch becomes just *c* field elements, the same size as would be needed to send its contents naively. + +To see how this is possible, we need the [Frobenius endomorphism](https://en.wikipedia.org/wiki/Frobenius_endomorphism), which in short states that in fields where *x + x = 0* it holds that *(x + y)2 = x2 + y2* for every *x* and *y* (the dream of every high school math student!). This means that: + +* *s2 = m12 + m22 + ... = (m1 + m2 + ...)2 = s12*. +* *s4 = m14 + m24 + ... = (m12 + m22 + ...)2 = s22*. +* *s6 = m16 + m26 + ... = (m13 + m23 + ...)2 = s32*. +* ... + +In other words, we only need to send *s1, s3, s5, ..., s2n-1* to recover all *2n* *si* values, and proceed with reconstruction. + +**Quadratic performance rather than cubic** + +Using Gaussian elimination to solve the set of equations above for the *li* values requires *O(n3)* field operations. However, due to the special structure in the equations (look at the repeated *si* values), it can be solved in *O(n2)* time using a number of techniques, including the [Berlekamp-Massey algorithm](https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm) (BM). + +**Roots instead of factorization** + +As explained above, the polynomial *L* can be factored into *(1 - mix)* factors, where the values *mi* are the set elements. However, since we know that a decodable sketch must result in a polynomial that is fully factorizable into degree-*1* factors, we can instead use a more efficient root-finding algorithm rather than a factorization algorithm. As the root of each *(1 - mix)* factor is *mi-1*, we conclude that the set elements are in fact the inverses of the roots of *L*. + +**Avoiding inversions** + +As inversions are a relatively expensive operation, it would be useful to avoid them. + +Say that we're trying to find the inverses of the roots of *L = 1 + l1x + l2x2 + ... + lnxn*, then we're really interested in the solutions *y* for *1 + l1y-1 + l2y-2 + ... + lny-n = 0*. By multiplying both sides in the equations with *yn*, we find *ln + ln-1y + ln-2y2 + ... + yn = 0*. + +In other words, we can find the inverses of the roots of *L* by instead factoring the polynomial with the coefficients of *L* in reverse order. + +* [1] For those familiar with coding theory: PinSketch communicates a set difference by encoding the set members as errors in a binary [BCH](https://en.wikipedia.org/wiki/BCH_code) codeword 2bits in size and sends the syndromes. + The linearity of the syndromes provides all the properties needed for a sketch. Sketch decoding is simply finding the error locations. Decode is much faster than an ordinary BCH decoder for such a large codeword because the need to take a discrete log is avoided by storing the set in the roots directly instead of in an exponent (logically permuting the bits of the codeword). diff --git a/doc/minisketch-vs.png b/doc/minisketch-vs.png new file mode 100644 index 0000000000000000000000000000000000000000..aed810de8a2512a2e5bfb33fc9e9a99c150b98b0 GIT binary patch literal 14212 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 YFwV018>eah{FzKrR8FK)$iV;q0L2 + x + 1* +* *x3 + x + 1* +* *x4 + x + 1* +* *x5 + x2 + 1* +* *x6 + x + 1* +* *x7 + x + 1* +* *x8 + x4 + x3 + x + 1* +* *x9 + x + 1* +* *x10 + x3 + 1* +* *x11 + x2 + 1* +* *x12 + x3 + 1* +* *x13 + x4 + x3 + x + 1* +* *x14 + x5 + 1* +* *x15 + x + 1* +* *x16 + x5 + x3 + x + 1* +* *x17 + x3 + 1* +* *x18 + x3 + 1* +* *x19 + x5 + x2 + x + 1* +* *x20 + x3 + 1* +* *x21 + x2 + 1* +* *x22 + x + 1* +* *x23 + x5 + 1* +* *x24 + x4 + x3 + x + 1* +* *x25 + x3 + 1* +* *x26 + x4 + x3 + x + 1* +* *x27 + x5 + x2 + x + 1* +* *x28 + x + 1* +* *x29 + x2 + 1* +* *x30 + x + 1* +* *x31 + x3 + 1* +* *x32 + x7 + x3 + x2 + 1* +* *x33 + x10 + 1* +* *x34 + x7 + 1* +* *x35 + x2 + 1* +* *x36 + x9 + 1* +* *x37 + x6 + x4 + x + 1* +* *x38 + x6 + x5 + x + 1* +* *x39 + x4 + 1* +* *x40 + x5 + x4 + x3 + 1* +* *x41 + x3 + 1* +* *x42 + x7 + 1* +* *x43 + x6 + x4 + x3 + 1* +* *x44 + x5 + 1* +* *x45 + x4 + x3 + x + 1* +* *x46 + x + 1* +* *x47 + x5 + 1* +* *x48 + x5 + x3 + x2 + 1* +* *x49 + x9 + 1* +* *x50 + x4 + x3 + x2 + 1* +* *x51 + x6 + x3 + x + 1* +* *x52 + x3 + 1* +* *x53 + x6 + x2 + x + 1* +* *x54 + x9 + 1* +* *x55 + x7 + 1* +* *x56 + x7 + x4 + x2 + 1* +* *x57 + x4 + 1* +* *x58 + x19 + 1* +* *x59 + x7 + x4 + x2 + 1* +* *x60 + x + 1* +* *x61 + x5 + x2 + x + 1* +* *x62 + x29 + 1* +* *x63 + x + 1* +* *x64 + x4 + x3 + x + 1* diff --git a/doc/plot_bits.png b/doc/plot_bits.png new file mode 100644 index 0000000000000000000000000000000000000000..6e907d6b20c0d88e38283ba0d66dd177070a2986 GIT binary patch literal 70202 zcmeFZby$>L`!;$5f=HvF0wUcgC?z1>N=Qq0iGXwu2r3~R0@5KJN_U7zN_R_l4mH5c zUiduk`#!&Se|sOt-hY0_cdN|c%ze*V*Sg|7&uiTi^g>YvABP+Vf*^c3SxFTLx(1$N zK-lQufoI%L`|3AGaXB??Z0xDuO7q~?8*gN_93hC{$JIZyST+I*@SkK(QZJoU?aZBA zjU3D%S6A1ER<_oTCPr_}9@;rrBiQd!wl#%01h-iRW= zerMY8Oym?Vp!GE#Cnr}x-#Jf-zWbA&q(h-T_fxOlr+us6EV01IzyT*?e{nS*a2Wpi z34U&e}JJ;D3elfTcpGQ(eoFWTQvn*VG!v^gZNq~z-C{AJZ)d$NMyq_4Z%(cV7&oQZ)U@_|m3Edwg` z^XJJ53|U1*#wNv$yuRL>LGNHr5)%Gmim0wwwxyX4mb8|`n^aU(4&MHLe$^DoNl6_) zGcssho@;B9Pn4QN>Mj@BKALOa`u6fE?80rQhKMrF#3wX=j{?lQKTQm=m6V#QBqG4X)J`o$tsPNM!H+pjSO2bZ{EmUw zx0%%Ky3@0>Z#ulM$3-jQn`0$RVMYk}`qD2duRXEoqNvbNeE#n?Qc?aaaUJY$`iu{HuhawB5aRb#^dAT zCEX60r-waUaNXV{{!9~8xG*Ac2elNfGH6dNnWAnxHIaH3%Pm}~~N@FOTicz$Wg6rnXTG9m;g zhO-fnu4eiZ(@95Mid@1~W49#{(ddly^zoT!vhwou*XPVm(B+eaJdYzb3m#uc&p;7V zpLI-APS!`@jE-J zq9I2b!w_eU7lb8ATW%J|p?+@Kk$y`^njeBzw!cmpEZyz|gs*HBJZ<6Z82ecK4$ZO_9@i3{6eV4@LskP=|6P zZM3x*f#GPOR(BkSlAN4!4bIZMgt&P1Xt$=|k8Al}*J{>4z2MR}bcYe>d8k8xnc>8JO zvE0r=)y~0YNs|Kwla(g{0m0II=Zpd!8n-yCt+mx(*JBWpwc9)2ZHhkmVZd;e{y9l8 zzIq(J`q%ZKt0U_x2k>Nl8g+&$u0yWEWK6 zd=Tz!_I|hAtkY>Fic-`~iWX}s1AVZhu4rxNKzMo+5A5PJmQ6G2 z#K_UJ1NAlBWW8=+dkb%#3U(?sH3i#ixxR%|&6ZbY!HvDciepf>tO( z$RfYx6=h|}yWPg~FkAW$HXsDT!os$sm{Pxw1WsZT5M(n3tkI}24BnK@vl5aks=k@P z14*Vu8_+03CJkSOqxPK{#o@0=EMHx8fdo(F#o2Dd$$fre1a};aoB!@Yq^xH=$Y+9{ z`et{9&hbfEW&)^SvSD8P3vC?rd+3o-x;_=IyZW_>$;mU{-{W@LW(k$$^ia9n#>HjS2$FT5xZUWquA)Nb5V1A#dzN|_ilC$6p=WwN$(h$PW!I#z z%jnRlcCSJp%abNm(RHQr)p0@tFNUbF-|#zbt3g>=PZPoBJk^}2kBUeOP-3nM?5>jP zEo{~~jB6lIbxXQXr^+JGROB7};2};q1b^xyX9!w9?+v$s3@O$=cAj1|YM-7pD*o8* zyu3Uo!ARBVZ3YGgHG-%nuMMBm5sSIoq@>>paGZK)U8$a z9{1l_4U~LY@LE#|&A9EJ`X_cREiJghZT6RGtq>=kgxMUZSbci17LX~t{l?SL+_t{8 zci2icuD;^A^=wCxf{w+1A(-14o*ij~c{EPC(l^h)LpeN?2^4q*85EYOn9cJ$U6 zcK-H#k2M-+F!vaOo-`smxlN^zgsZj1JR&y8ln%`>xR!USo^hNTzRdgDV`m9n?u*J! zuR~2zvVc-Co$1sR8bDHiH8W(MRG%*xkbIuU1v!qgk82?ZY1qj-2iw2 z@|cF!(gd;Qm*Whwl@5kQ;uI<7aXz;AUoWr}40UROo}NrQsxiEg;(C9N!u#$Vc;|jQ zRgS@j+@>4*vP&{q*aCRa`S?q-WAwKjxKhl^ON_J~HsSLvF4DKN>1fnV8AUb)cyT#5 zlk(M%pML<=JpQ(%E7rq?aO@32k99i71A?tg+fgXDTg$`8J|MS9ukO=MPX!M7deGe$ zlLqsEhU8+vRxB|NGpI@)S7LDFuqzyg+m|@LD_RiBx~g(+Ne06dZqc0|ma;1X{x$1I1qR(s6?0dajyr z`` zx4p~Be7}C>jHMbNrkvEhcA4#$Y$dvz-G1Lw{-k|CS%+N8;`M8Gp_?w^oYoL69i6&M zwnKvF>tUlXa-RHF)tyE(qK|C=N;CfQ1R)2g+U}=u=WnN)&KlT;+VrXm*dEzAmEx zb!JK)O;!Y~-EcwBB@cD7ekEWEa%q8Hyj3*)f(Qrrqh^!rBo{5Bdl{bWeCJ*0M`b<; z{s`clxsp~9CrfBN{mEA05GYLsdZ!z_(3aXYXD$g20e;FOM^h0^lU!Wf9kDHcQ(A6y zJYPgE1)@IHGjwhHqKB&wo{L63h^kplQB5r@5vIs0!}8U}HZ~b{H81-x@r1?Y=2t{7 zyqn59U0u&yIIF_^zT)W5Kx=o$o5MUuEtVku zn;8vikL3gJFEdJQa6*xhk>y#En6K6lN7ZY!_|4^3Lr(8Fl;6Wa*cIG`jz&J$8bZT9 zl1%&&d;|6ZWfpxa8*K!c0my^6rvCjofzND$&))$Hg(r>RLzIKZU2G*_x@ktbOHY{O z<2hY-rb*c}3TtsGn`S97L|03#pTdrxi}TL52V#jXa`L`{hB17G@garJgEP5Kri6dx zYAX!UFCm$kCIP$mh>6oz$Cn@8g#;X(0Fd1fF}Aqxn6q#cf%bx#1fCr9W?%{w0m4C>t?y}C~K7bo09DaoM&q?%ti1)rj~BBoC0P9bH1$veDOPWX#B zcCA4{EZ<(ZdICcRJ}X*{>J*Ros*!a}&er-@i~<2ODEHc!#jIl-KBR{qu~M90&vaRt z1=v0HHCP^`4#;xOS%2AQ^% z$9bYM_VS6mMcp=0*?~8ID~=rxTB64Hex>k2P7b{L5#%#rI08S9-=sUf52{u(7z>FV zE4;N6gO(Pho$B7F#|v?{f8xeCI>{OmQNx40apr&jP7gIPHm1y?`Xi#+e>kH&d#Y zIH3fCc2;`&Tj3s!Z_Gj#(Au>paPL%h9N5 z*Z5A7%E0n;scRQ>?b0e7+Fn!eK<4Fs+Ji}#`S^niR3U! zf>20O0zsh)AH?kxwPrOFOQZQo-zg(N?I$Aov(L$lD|x$do~R}zV`>|Dl({0* z4ai2H?Uofr|8#A}19vJ17WI?~K${$o9$H1fS)LFFZpo5r9NLd%e=%_mY+U-Z*3HqR zmI_U>V2Gw+)XFAK*ST`I3f7?2xGMnHcs(m}GSi{;wM?TGaVDr_-{W7@>TXcpe3zOE#ozT@Ihx>V08fPLlk9<9)t?|Dux z$ZB18c;`Ok&H_m327sB9f@;Kac+b^J-}w^2qdPn6w6jj&Yyya)Fn6N zj+s<;2V|(eu;C0Cih1Kf_)-k*03zuVxC~4KQu%I1VFvn&TR3UuclxM!RIO(1+HuAv zS4zqgQI{L)2OqN-zCNd%14KrbWf~+-uX}O4{+MIx=7E6Bx=t_)@Xl0YbdZYD=wbg?!b0wem#-Gl)*6L#ri!B4=Y&~ z%WQnB`%c$lg3%0fCce6|)nO<9jP<3Xqg%%pKc<&rd=QXaRQ#BKoEl5?3t^T_hjiC* z5*oE)Vpv3$W&~U|%QkB$)0OOu1Ic@p>_rL1ksIS1?+Uc1r>8q^?-;9Z&AqNI`x(y6 z%A{Pex)U}*6=p@~T z0wUQZRr3puSL*z4V{bU#s}LI|oiOl@;W~R#s>i_CbY^=G8LcFo7nLE4TvAHp4ZTON z$roY3bfCOE#-U#?n@Uv{OaHwgH#hJ;Zz64esQcl{w=VzpYEgAwEJmB7MN2c)H-lrI z%LpoyNlS$V1U^6dsQ4olaiC8QIJjEpXZj}OtmkfC;n(Lbnb*^V7XnG5UyoUcdPz#Y z3&WC6yti*ssA^0Ttl;ha&|xQwoOLWtEoxeLdgkEZpyNUFOlXI~_3PJTImm9_yt(!* zk1&+(yV^3j@(<%jk2+b~m~7vjM@-Gmrd$+vd6|(*bXGJ%lmy*@P0=ynRqWSSDmz#9Zwo_yz)g>n@E4N-mxo@M)|9E$<xb_GGNIn~c!gUgIa&m@-)b18L@wAy~ zFPH->=`d+FR8PKtBk%0%W3mu~X-W}o21(M=1pjJp52(N4UXUWF;4u)Ct!6N#PSr8O zbR1PL;pJ72h?$rT7!EF{5)o+v-nxGOtu)*NU^IS*1^Nm10&;*O{dRbm%1)V6sOt+R~re|i1 zAW=c~-N6ze1zm zk7P=$1`^vTZVNZ=LTRrcke4U#^umF%V&Hugi#+!^{T*OrWMoOxpqtREm(kn$U5 zt#M-onc9cA%Mf?MGtMd4Pi7nGjEI03v%+Wi$z-d@7xTP=yGOV07t|eiIMrLR3SEr6 zMO(uM84qyl3=|SDWITL)DctqtE~n$i>5oraL+WnCuB%?+;rVLG20o0KV|M6N)OXrh z<4UiSKDR61*Bz3o>2ufe5Sa8R`sTJK495D~HtDImgTa#_y*ii6Z9AVKAC#uP`|?s} z^uGs-}O49r$tl21FG0#)?u;{}lq39*olU;C6aQo=l5c^KnK zpWA%Cc@f}!n&N6q=;bl#X|K|pKiqFKrnKAFK&0kGm!6*{tHP$P$>jUB*HII`UAFN}6sS+1Iy6 zw+Qa;&kgq#IQAW#dzLhy#!EP4F3zWxZo%GLE#aSK!S^)V;|&2F=j0t@j%~RInW$r7 z`TE#k#r`(Cy5Pc1@fOYk)z%MG_LT=t8(V>tz}&a|DjRNjHm^IlEfu}qQT5XQ%j~{r z%~a-hLGGGXfUfLI_8gn`(x>{aK(nV!fcRY3kp0(PL=zVBFjh9MO%AQDtoIA{^91wk z5|%%!_TQ&#Jp-pF4;f=23RTAtmHO7wh|8yEOUDWJI|V8d-nb>7>n5V7H!@jQOwM3qOTdct0(?qy(DT?BC823sUH?`%zZpxcVB*JvUyxLQw_@z z3Q-3GLjL8-z%;}R@66YCzfGEcX$oQkvllD-@7V_}RQjN9iLehcUw)*oxEk@*pVL;n zD?;1fmzIjQGTGNbCKZ+T#zOjghNH|j-EkPEyk=+@Y02poBmBXwoOsMuzQ3+e4s{Y+K`D z5m#nuD+K*=rBm_c_H!Zt(_#_*nU9ADq5et@SXiR(Z^~^iWcfzIL|m&cK7HxxE_3XL zYu8*ZZpUA)FkC9=UODq;DLY>hYKgZ_1o|5P^fgi9?k`|rcLfp8b0q5p zF>MoxD|Qoc+h-g&3MbAI&nkf>)6ja*`muhlkbij`k74-F#Y0r1MX-?Q3@$R>NBvR> zx41x9Uc&UPclPJtrhC2Ni$bFYK{yXPb0g*4N6k5}VOutLb#26I>aXKku*N>y+}sr0 zcM@Z7bX+CSmgUgQE{nSn`-p^;l+<$S>EDi3mfd}cH`G(TtnWtamJ^$kSsO^TRF=|l z!mV7h!cz{wnY+v3nw z^7B2Rfhq@O1}=55#Fn=kT6|FF1_|=nC}fr09C8kY47fem9lO3|CT@oJUXoU9YDMcW zuZ!nFoNzHjO@eT^Y#7bogjp-)thOTI9P(UdH_(8$8g9JQ);^A;5v7e>`x#1s*!B7S z9kWI^>_69Q16*jJ64>Ddnf@`Cd8qKE{O8<@_j@7_ctUqm9S*`9wB!)sQ(}pa?7{VX z%?Buk$HwOB-BnSSLqkKT!&Fq-d$@y{fM>!BG70mbr0aI8WjmXPZyluHBAWm7*}%(tXqc@}a@z2Y^-5?j0}x2b%1mL< zUc_oP{CAj&;Sl}ppFay}E<+?~PzWzWk&2gnk7W4Hr|Wwt{snbZwJw>o&@FT^ox4`mCS4sA}qzD3mG~UY}esSP|T-zOe#pWJq zpj61Zl<>}X&>u~-jFS|k80LuHmhP^9T$#O#-nnV+$)!dQ5)8&#H>|3R<9YZI$rPF6`}-n;M4=&_&x}hntYVq3hU?=Hs+wjg{#SSuTdk& zslL8%{CR)oVz~xdWJFx8z!eC+S^;IIIO3wF%b!}bb3ree2B&*Hy{{|n2x*?2=%0h~ zd%#9xMkdeY|5*Ib2;$LX(H@h?uFji*wEClo*S~A8GX!8`ZXoFaT{2?l2AaNBumPK3mJJ+uH z%coOkpc4A^DpM4bDU(GO0?WLA%sOk*mIQST^zCK-GQ7#THiQMSh<-p{zgJ&~r{a4$ z_LqYT8mMMAak$n1mx=xLWeU)m| zz?r-ey+$#>(>h+p{_XxIE|($l>DVmYFJUxmHD71w!k*}C=m&S397(*L(#ZLcFc4J> z4F6{qYc~JwtdA~XADCft-wSYWhRgGxwNpRHJX6>A_lD6P8!CFU@jMv!_0^PV^HeE6RaygI?{ zxr6h5RVPyl_AI~X)DxJZN8h;bFRqN^qNbZtFx)Q~S7CkiMox^i@f0N`PmwoUsiOjM zixvA02MLq%6%fqC)K?>$ZDjb9qzy@58w+PN&|ZFuw5tJH z-o7#gRRk9f$VCEFhk{}JlQ$>JK#YFV4Gu=TS_p1j@Q!9(7E7W~__>xIoUoRn2c_2~ z_rnSOua{M?Uxf&Z=)XfGVP{&w$V{s;$-SX9{D}{dYX^@YS@_wxxd`yVyHMj>5^5Xj zm~DZYp?J1GnE-9!-~5DwA5V@SHM&T6?W zqB2IQZ-i@cTGN{Jo3MoT5Da9mASd(_M+n1Ok@y6`gIUYe=aR$2WBq; z>;V7_i|7^L4B4s6Q<>yEUNhIYJUP_9<;`{{c2_w9{n}L>W;FzzM98=CXF|RoQr}wZ z$R`~2^RXOd7rOjC{vj{*H|=~f3kef$>eQNRRRWkFpeywTg1pIt#2b;%Vi+vCO1yvQ z7an5D6D9b`71b_AGfa4;+q!%G%CKyi#*U49BNp>!*=iuH+xbzO0=@$l7O?)+At}Y; z7i{b0{?PE*9+kh>q@VD*wN#f}40b$v>`kfH_CvGQ_||-%h}GB}MwdR(R#KQwdt zK+@d)U*s(L)O`7WV>TC-Nbqa|-M~Sx2rae_zgd1yd{*j2VbmgUOghC{5T%bttS?K0iXaYQt+>L^zMv z#~$6d3)jQ=14jR|mh~m_$-0#wlt5@*){m}fTxg^ynuOV=5Lfi^KVU4;w;a6;(HfFb z@x7bPpD$ax=r9xRy4;AwaJwyq+bBqfSZ$l6KS3TDIh@yrap$d5_hPv&$Iz@POgKl=vd)K5svUZC`S98%3Q)_(Y z!gR*8kAd$Ht)td!8ham4G)E3>SA*?IMmDqQvLVzKWQzIXDNx#e}76TKsmhbKEM?_3#FyBqqQIb=*5KFn}+iu@Gv+g+WFwq?^_^I&lYC!Wz0z5 zvmd2F*wiQ87e-Q?J(!`w5w(kAi9*FwYv{T2J#*u1Q0|R~N19+0DV>5T2d$wN!$oXo zYBB%#D{kQOpn6{VB06lKRBiOLg}CeX*2U7K+V2M$yEaeNn~1*FpJVzJx79pNW~4-2 z0`$-FT}7AD5QjN_1;m$ydFG+}{z5hR2vMJ4Q9^ z`jbnO#j{_$mJ3gGyWk7$UM3JM{oSe>Kx=2_s>A;%v7~L*6@CX-X@>2f*d6}kc=?VW z(ew@Zw*q9h3?|_g+O6R?7C5v=H%F4@lzK#8Mh=Jc;$k&#)i|#94(F-bynemLEO6IX z5!8c!)&$nm+xp43ONY9KluhDSLUO(3&|8@hIb#~VA&yncg~QL0sPtwV+*_AX1s@0c z;5_n0sVv2s?{1f~Pt+ZhTZ*Z*7r1(pG6{GWbZ&JKU>F`>fQ?F$J0)6WACN=TR{(bs zq)p|kLJTyUKV>K;Era5i*pp}F41TD62m}D#Dt|#i$rjG3;#B0rIqWM3&(MOS)s}Y4dV^?sGD8Z5MfTUb|w0CR=Ok^Lc;5 z*+y>)tTfzzhH2`gyoUGD&+>Qxsj%EiMj~?7*Wn=gk?*al6Gb_O5-;&u%jKtpqUg>T zz8p^t@ubS!5QsT~x|SW3&-&r6KtHQMppLtMg5#Vv6&|9!5LTYOzMz zF(tfxgM|q-o*0{DcvlTi=hW>Ei6p6YFw~{%D@db6Uja#W|Nbn`RS5&g*c;#?K(mFZj#t0ZQ8=-|4#*eI zhlbKowCAoqGrExc+q(25*Qbvue4gE`3Yibza=V7Amu(pw|LnYKeUih+7(4f9;(e_L zIaqhiyC1&w`-2ASEiBRN|6p{c3pM}%V0e$NhYfo%znR(^(l4Iq#COn|%ab?QwCMS) zc*G@HlFHV!=ul+joN@ZmGF_PP^o}$xsDFsXXL@eu#24Q6zpl_>)~yTeTVOw8+Mo~U z&o8B-5MxcXdn;GWPsR5?%yLdV!aEI5~U7Ra>lEsIjA z1N_xC#M4F8W9e!}sOTQjy$9f;d|So4Lji+ueO=WMn+Weuw;`U)EJ~nz z)JVj!?=+3tSBYedjn^Pom1_IYETo_5`Jkr7_IC(M4wTH`ckS%z^1DK{`UfgM^z)G* zx6;s%j1=EqU!Tzmwe&1@OpH19G!52n>UQi&e*v5Y+k?aZc`-1y|>L`qcF_ybnv0@@7 z*@0&A^7cbrxyDBW!p}{t(GM9IXKN$T0dB@HEdJ+*->6Jj)2X?g($$WdvP+UO>+g#v z7PtDiFlS$5yKEJS*wRPPiad@R5dMqe|6$kJoz=buxd^K5CgKr@Q88O~Cy{Otg(+vfNyq)L#gC@=LKMIFs`|peATWdZ|-?>Sy#gpI-c}cxlHS}2goHg0heg)Xz z71Y|N;anM+H8-)kr~oU5et}yvd<6ilQ9VK#_G_qN#G|(z1^eoDuHZTwv<2Ox70}Jr ztfqce4Kgo4De()8LHF|iI`jM8f6h^^A^~4BW4s+@k&p9&49+D@Q>C$!%rNmrzy3+_ zqQ!-2z?W}4BFT3=O?mZ6J9wl|6Yc&t-Ahop}UYH=0CkT=NG#oPTI!Ypoz1FHxH{n zEc?21QQ?CE3s}roi8hp3$Qy->HA4@RY}whI|G~G0(iI<;?K@>*%@@UAGGlXAFA%at z+19C{w%0$qM^c6@+L4VcCb0`g(RCKIX)gSmcY64qEKcVkst(>10_BN1Es?t;w(nFk ziP(GgcWw%H%bbbzDAS0CxywG8$Y1go@aFy-&*&>Y6@{Z#Q-%Eed>>{Sehy!G_UWhd zMFVWQ#m;(LEsQRajkXe-=6NZu=K~bk30mZ8; zEp^~ldd!4p(eFdc5dgGkuTk`VRCvWKqT+kin2^W(!)r5dxwB1uS3NuRv5-c`L5Kg= zJxiv#^@HFu))~=r-!|7@fg2wc);%S?6@Y-&3~VKuY94}O%tr#n?%gYBCeZ9b_{AM* z>-hQS7tL+?g3OUM)OLnZ8tNV)o%Y{4#zmNM{~~;%1}!{-36z(+2Js*KpitSRo}k1* zs@~vL=~uD`_I0!4zB0%ai4=pG0VccJm*zz4eU0>HLXY*q>2RyRXwV-`o<&spH5b5> z85=&v{k+51E<zKLHC+DsIrhfgnTn+sT5@Mw-5?ZUA`+XYhI?YkWlF zAdg%{sW+ynl8;)aqN_VHJ?0=!7Mu`#%@jayX;JS1^_dg&7+$l zdnSHQs5OnJzto*RJN0anwDAo2wI0ML(y;WYYfJyYq2d+UZ&i}f+~JPnvsL}|<3 z5Mb_qD$m`7S5~dHY?0AQwiY8z6*+W|FmmylYFHz7>t;{G%Q7&P(QPtP>l+2s4=uDM z9^OAbp^tKS&qliTiAz)E5NEW=|wHZe9WqIF?d?|1jbJY19-1 z=W4Md`L^-2ud4T}jkJFISG)Viupnl=lDXP?_1t^Z+?=<7e`5VNEZDel?Q!P^Hf2>m z)Yp9nm9fhQ5-1@Jk-OFE*)P&GE~*o4N4vt8WMR*40##r*{j~Gr*4~i&{(!FBmn=d( zZ`Cym@@Gra`~1Kg@TSa*cW)24x@^wfVBsjTO~iVUYB&EW{2!k7mx%Y6`i43tK9f-8(6dJ@SMtVec332S#4 z`E|}7NQIzdIwrZy9LI04wgSq(X9*8cTT>cc8_GS)cT0M!rRG2|zSA0Kc(q$SXI0yr z+=bltoOAh_Z++y zU9#!pca`C;o-e56<$a!7Qj{OF(Ub}7=_;SmD@Rk1EpV+1yck!$sXF5y5nBn?K3p*4 zNwR|6KO|u_U&r9|JbwML*Tljiz=iOC2sjApPtV8nf|7v7Kd9=zR(0&;@z`y(ION(T z)p2I(qlvn8^M(Z~3n>AtrhXJq#P+gtlMSI^`9SfBN*PaFhvUXJhkA9c3Eu&7^&ycnB1&a*+J~ zQ*xX(VXNiw0q9j)ugF&=oKS!a0IVF`D}s~~G4XSae$n;%35Q1LYWOYWp|OqSTQz$B z1-@>Ww3EV$ak}nnHfPmtV;yxCg`y$ou~Ptia}J{n2kED($#{_BR-uDz*^1GxT7jx- zYKSd{N(`FR3p8S5n45mkJbb{;{(7?9>IWpB^n`eZ4NsH{)YI!KzHV9x!tvK&k? z%<_E@fVIZ=nyzN_7ak96)nBc}t)j0a?I(a;6%Id|Qg6)gXgrS(FUlGE_}jDIR^R+H z+0bjQH46bxzD|-Jpd20!XLv@j?al|sdx|WI@T9)l#PBa2*BKp=wz~0nrfp{Ttr-u_ z*iPtgD+;mUxrhR^y?%(OO=%eAi-ytF%@e@Nu2UJ}=36xfqi{{;y=n;d%TPb_o;Yg; z{aW@QzZ2b}2broTA1@GWkq@t0BTD)e_}LRo&kx+smuqjgVYdc<73ZR|^c=T~w0BO7 zn1L-3e__M+DXBhB6`!q1{OE3fhxnG+-Pb)#gsQ$PeW^Q zzr@t9Uqa{3m@_a-HJ1|+h7Nsx-_>zdJyhkfp{A z{QUgYXdop+-Vqnl12NG)mO6oBlidTO)}hHK2qPKZkmI>0!iL?Tt&m7w-zx0Yr}OAs z6E(xj4Y`@(GXM2cB10k8O-wH2b2Nro!P)(-_{<(7*Zo zZ!?^TB;_8#P3!du+m)SeVpMN_iETu;+njlS-&%FzU;zW0Qo)A{;Iy|*!t9icV_+Xb z&!2LOOK`6W>wpNsu0W>ZC4EBQEasbpemPWe>&AE#M zH5HC^lbIh{|ESdyQrtb^`eI+DrI*sEnbn9PD8Rh5y|4ps&TGP{y9`6cZWpa&_4S(90*Ar|`*x&s2Fd!#?g_3Lf}RUvdnbUxu7u)Bw_x z$rFB{bfLkI-|TIw2rS_e`n%Zzx7NL9r!`h0sJDo_hZ}o8>4GsMT0-;)G zrwIi+q*U(5z3LM9@`2thXbP!mtbZo1C{YY+Liff7J%ZwkUQkpdrv)W(DvIXM%|bY{ zt8XkXJd&ab-5P>#wK|iZ)YJ(>Gfb+p{YL||=hs;@q3Uww{$H&GFbOaqyI`Yiw7oaj zL_qvBx;Wk6z=dvcarz*=-lH3AfL1Sb+E*O%4sF)DX3z)p>*V~z)v`ILKRp6!GauvF zrI2*Nf?dKQ1&Q+nT*B;B zVMcEN1*$Y{a)yt7);ti5fO5$YJ117FG?zXvDa6)ghsC`%K4cf{z~ej57hHR+1yJXt z2H7UB!wn1aW>SnN>M7u>0x;4j{;jNgi)BVrg1Mwy}FW^PfV#3=FFh(q!=OsmwiNCc(=+rp}+7uEw zoH*+(jA8&S>kxddGO3&ru64p?u26Es<=*JLB?0$K^OzAFGyev%zNxX)oyi`UASmO* zztH{f`E=;Cby-2-EBi11aD^*a1e)zvEw&yu%s?yi+l&bNFDTJeXWN=P{pN116`Op$ zpJr!W@{k2jB8!ziU;aSmmGYZWLlNUEPct_peD`YiypGd-K|#T3=gBMS*f&8=LitPxNl~-mGn&_m z!+vax>_}KS9nAL8FVwrK$;GEqw-$a|Ao&)MQq-Pt4iQ>XJ2u6uFEK=hA})eai(ooT zIj$b$qFo49FwGV$o!aL#X5%9t3shtOmF>ZC@);$BCPSbQ7sWT-J8MKI#2_3SZyiyJM3?6~lQ)0e>V-{z%ds1r z!!PACT^UbT?kan7uHc%3w$}p1#Ktl>-4}lF2K2k2wM4aN6l$MNKHn}aLqkKE2dcIF zxXpJNrBe5#_@`LiS}GO81+uV1DDzwO{S2zL=d~XLy)S$=du)V!3V3Beq>a!@XbjaH zSNfzmoc1pliDRV5uGN7_$}l>+@<8(FSl*NWNx6^qNvKaQK7ze%FW2oB*Q?p+&|IU* z#O|z4y5zxvdYLW{cOL$#JJD{9J@5dH<;**X1pn3;!rt(wqc!Vgwiq$D+=pDSkL{yrN@z1tW zXL8ZO?VWnkK2_gq=x^)l&cV$IF1xd#o_JWI{t7ZU5pPAWny)6GB^cv3Xy{qfgHT;|?^q68*=0-*6x=75x%Um-zLIj!q;Lv5y+m^0@Co-vP@ULVuD*8H! z12z=3o#~_OnOx89S|H1LaC6CGd$>i;1p$?C(&a!5i|W?aR(*Z_n=z|D-E+n~N_Br> znfqt_>TbBPkM4XA&ia_4uDNs>8Hkh*S-&pyoxuW4kUU4AVyKgU8|x4oZj-UqoL(^X z{zFrc`E6Gh-ZFQV23sh8-G1^b(6CT1ES`}9tY85zm`jQ0- zn=JZMUlT4nm=}-Ee8y^)z<8mlX<=d_L63}oWCCtHPuzw5YT<#vcONX}T%Mn_7oh>y z-1M z{pmdQT4;?#U9PH+yhd*hkzK4|-nv)9q9C!|H3j3-v zjNdGDGA(i3+1c3=nnQdAB!Fn~7m!FukzHku8~r7`ZB~>zs_8!A#=qSw5Kz|%AGmu^ zMqkPJ<$wS|X4ni{8BLgyymtOHX7yEa9xY-bt^n|M{jo#^iO5lTrBIunMNJ2?gD%Hv z54BDM7~phXj85JF&M={g9CXl0PS@4ctaCziGGX-Rh7UOJ{s!~;?m%UU^tNS%<-Z^d( z5#;$w-!qI*5I=2`j>g>*O7TIu)9y4~9_!d(`5N72V`D1~4=S-Yk-UQ0hN9oQN!Xy+VW#n+u;tqVjlL_R0k4CzM7w|wc3cCz}i}AL<=-a7#CpE)%KHk&65?@odgme@LINkk_@dWd*PM{lT<56AqW8oKROf5 zL(g3ANtxCLUC^Jg7E*__>JpcfwAX+P)j@QCQLFGFas0gHX;B7hHffLWS*g$l9vh-Y zcQUueZ{lqN1Q5SGP)$;$RBN zjY3-hk_jVGh|LmrM>}pF9y2pDDJiLBVei_E3?ThOhEBR=(d~fs|Jh}@lytk5TY}{C zBePJPRQ66F$76oJeP}EJlYFde*8d~*%V4BP@pbU{h#QH!eW9iEv9a$S?z zXc4HfZ>x>4E)M3w6ZqEmC1t8AAgO!>V>_)OXa+fSjTWk`tPB@AzI%4XRDQ)j@*jt4 zy+Jo@m?ECX+1VL<|A;5}vW(o{M(*H_JQHJMLkN!cFG*Pb&QG~#qz5H^*9rFY3urHc z$i`DW(OiQQj=9XLNcAP*Ou-`IB$gzX@FVpkOV3w&eGk^(!#>32g3Y9np<>7HUyn1K zPuj(iR(+M%Fu8X5vzY0PRi>y;BC(!zB1B!XsbvwhI+u3AUhq&6V?GCs`t@{mr0}}t zDu>V6b7yBB!kvWv{udt7;42~eK#X1oUpfPBWF;z)h!a^<^xt9spE^$RvsI!Fda#Er zPsSar{)0kx+>^kGBQAUgMoy(#M&+z+IT7y%AFZ|eHeYM0a^M|9Zq+~%sW9#D9}>4u707J=tu@h22(w-!@BF}*S~)URL1{{ zt+$Sfy4~J~2T@Q$47xj|8zdA3q#J1v0qF)oU=UOqq(vI(4(XKc?v{`aX$EHIy+@z( zobUOq_dnNiG4;7)?`vQ2STC(;fW9y{?TVaqWL1pv+ZzD)+*{~o%CZ=NS5vb>fB4

O4+b|*PKsynTTsMf#Kgq3 zKsLb;_V17>6tP7PI&1bhd8j;I7f~xx;bH9RK3AT=7!6Uu{&k9OGkukGz@`!TpJH0y zGCJwU!fT(``$Pc^n&iCalecp6akL#RZNACXnRPiezpo{E3AHm@57NeJ+#fROYTW#i zvLis zx@=S*VQBoyx_1L;9d_1;Wo(yNn(QRh($;UYEBJVKwXz%}8*Kb?{X1#!>Uym^<2rvz*biQ;hi2 zpK2g2U9BG0-+bSo9c6EiG07$|!uk#S2NqBqtbw(U%?(KuutiPoPHyZL8cprK)@X}i zvDVmCw%2#mT}v|)DUtecICt^Ydx{Kuhk!X=M;(9kEjTr`3lvV#gq!d-u9SPu8#vEf zK0l(JH>hVk@A`FfhA0J|O*>SrblyG>d+bpqvrrown|^?QrP%9E71=l>eTp0O`~_UI z<%`2NQG^gi@p#Ppm81LfKX#W8dVIAzMWFT`BUTaL3*24({wn}6UYqh?Vitj|DVAJ0&j*+t z?8I-GCq9|Kq1qC(q_uWALGn6Gul55=SiQGL0hhUnqo!Q+7SY+Z0F5Z`lg0t^giL)j z-!DmqqVz+Ky_{~ZC`80SKY(Gp*x;dYAR;Ur2^A3$(cOHXoJ_%qboyK+WIM~}x&M5NgK!sk zzYA5u>S??_lI5~1rKxrZHTF0f>(l!XYN`BlSi-h{lbk$1l(}(Pqr;Y0vqy0^OI7w= z$?WqVyL`U=gW#|?!Meu}0h;_xf2aTux^CuMsH&M+lHrA`IEOrXK zd^Q(XmX{^H9-k}4$%5Y2&r#Q1mcqwv9-L_HbKUfpb>SQQD5j{U_Ne6 zusj(-?w2haaM)2H47u6Za8GF$sbOQIFfZY4n`w3?Lro=g2k54Vd+Ic2?9LG-Y4SpW zOgCSJ^JDQEsA>)$nX0F`#LYJwgUQiM%Hm!Z4pf2=;1%{z*wNa0r=_LEt_gU05m>Tm zi@Z=$x=Xo$HjGK~b3*~Q1xyigr8(&=zDn82fhMw2aT|W-GLYUc9f`v&p6&Nt&C$em zBC_k~OvgIBizaH&;+5fe7Xy<8{w;<2Enb>~Los1YjOx%1_mNAuZ-H4S7_a@h6in+D zdp3J_!V-eN6x2%qX*DN2Kcevn(=NtXMrwJ2k4yCn(w`!hczbERFaF#N{Sc_GRZb5! zHm2b2QdsvOlF&zb77!+FqHm;)F96ricJ(U0Yl>XKzR-VPkVKA_l+GfyCZd0Yu-4gF zMeA_qd99%<1>;ZI-Zg@tYG<$QV!MYadO9ScC653!1}@8TdF%I-PZ{m>aDD4rxyh_z z_Ee|(0`PZKq$OsZ^*1P6kA6UaAxZq3r7ZdQGad9&=M5#NUVwCe(%m5bAD)18Dt;Qi z2}}J6=NJxrMBX<0$zY+Fc7b@FyQZJeT6RoH9^Bkr@I?VZe9|qSX#2X^*H-V$5BS9e z?MxE5iVliX(k*7c1TBjz)PfE&UvAc%^?_ZrB4pFG&j5e2yDCfRV=nkCLZYG%FRwty zb*_kB`tIB=AF(h$k8_&xzXb#=(-{arRL}uCjo0qOq9yN-TcekIOc%v3^21o{UVUjY zBvG$P47{n}en0tbrHbjj9H-DGgGq3e@}7r~6qcHcvq;87kA=iY*=7G46L9u)taW1X ze;3Amug;9rx7=U>r*Px_=*|Z;*%vR;ds3nq6+JhS-5C|WNFtE$F)6>~MFVS?-^*11 z7Xc$`4h{|sFTMY1M@YO!g%?BJzF{oqeKx4M?^!o+JF7q9&Z}72)Du!+R@0OEEv@Ul zmjOHxWwjh}x|o~q!iQ*Hsk-3Go4MDea#MiG6iW`;$xx>4FS`?~zPLj?`A{d>MLgyl zv}c1HdwLkY#`gC1BQM^T-j#g&R;bozO+Ur!sAq}a5eVsv8+lRFq?0v-X|o!7Dg734 z{>ZnRYjVFF&0!+Xu+9wI((FxX37Em`6Jv(x0ekMMiNwtN-O0qQ&uJT<1-W)Rve2ui zd*%T!B`useeF-KJ`#bGc5HI2z&t|G@$;!|`Cr-s>3>j%(Yo>GMaoyH%w*QDkoHx5B zCILbHO%%ZXZQ8Ra9iyd$3wL6ZCYX$%Pq|XB{k%X()v@tLEH@SwNpxw(QyI4n@kE7Y zmP?|fd{Q~C*D(P#5h_aPLe=HveEV*5_q^zEH(A5%UNg~bO5plRVEASo^rO`{{A|@t ze*5lS8~Bo*;c}Mfnn=Qw0#F4^8@~-q6Z*2ad2d8u1#0+VSvLG8c&|~b=i^LSHftfh zEBchC&ZZQ-fwa%kFBA{S073(nYA2_f66BEBx}V-f+uBTYT;gUNXo$gy0&E`is%)N+ z?SXmo|D`8UWIF>LSQ>`0@43kQEew0!1lr4}X*WCsRN-F8l|y;X{R2kTPrDa=AGcSp z@NjxXep{K;g~+~dpP<~aP{4JnjWs$w={rgJEQOTVUVT7E88zhJ>v0=x>?FCXRSF$a zhxb22C53=3a-c*RhbY{h{Z!xLYb#9~ZC|f36pH{eRodXpT!n)r#jKGq8OlC&qXM?I zqLo>4ubl}pcxc{_WQ^rk-ckuI4~_dzr=~e+yF>W2y?2?F8PU**eT%{1%3th0Ah=Vp zjhTpafuUd?+Qs2G&HSxTE3$G3zq7t~whG13tcSj})d$)A@K-}uPMVp`iMG=7emou& z95M%|yo}xHku$9Kl|xl?)?|AL0;Iny$_YOf(bLTXkV!HSK>}|?uI_UtD+m~whFG~o zhs2_;;j@rUH#DSC`|Tb#{2Xk3xB;jH0GKn^=QYSD0v;DEb*@MmgQY+B~6@c zL;qjDK39j-+J002Eh_dwzx6`N<|@C}R#MB=Dq5HoNFM;>)At3P1%59{3uQsYc7f_= z4r)yYo=A)T*HFn5T z(yR%r;twKMt{*+KbGp8em-FUhz2up1pC^0(x6BJ7f4R0hT7_OQ8moYX;>klG1~7SN zw#0m>wX%%HH}x2P)-wb9YsMyXyG%)g^Xg2ml>8n-lRDL@+O9W23aQ87T8CE!i{X`jnZ>Q;U=iE)4n z<_yuRp2fu%qc7ntxgXU&H^Wd{WvdjhL3lZIQqK&Jp{K z)F5BLd`z(t{?Hg-cMiiMlUnJ%m+iC27=vfI5huLmq|dj_f0X6e$@Q*%YD|=)CDYkb zk1Zs}FfgKS28~RQ&OP39!u#ioc)uvfzhL279N=}Jhs+)OMBYbQG)3+vQr=AISP3yN3raSQ)BEeySP% zG+2kIF-$lclF+e)+P5ufRkgc@tIlSxZl&T%y11MG?6Ix2)n!-8?PZk>$-am!YG{+T z8s0hO%pK zE@4S9D}fubiyP7RgOVgPXTJ;Nxuq~JNF3}_r!o&xr;4k^B%< zTgt+0hhFZGdPA~q!`H&jsiIWQl0!3<100F`C~S7XY73n0+x=O#HiKY?NDRyKAlGdD zSAinj16ME>2|*uK;eB-o^L&<{!6V+$^FUYzT6a$mjc6#P7%47*R8XxlN_!L?WdG3A2=E2i1Kd2qzPm;eC&XXSnOGq4L&8zw$Wy>Wvb><|%C7+AXE6 z*th5>~DM^X z(u%}Km;^bshudO|sqTps_Wyhr)0P||racTOc@>=s>c0JD18bL)WpP)IAtY4chE``7 zY31dk8PT8$S}pcR`&Y{JjV{^L_gvQsa<*Z(?0w8(r*Urq`8-r*RVWpjbU(Sqvimn& zde?&Zxie@Md$yim?UjqLpU(t+@~;5R812Egq4|53m420k8(La!IXqy7I%AqE+pvK8 z900#)d;yxUIp4VYcflP6_DZlCdlCq!p267sUUkx46nL2Ck5L<%jHb_SyS!tGl@+>k z3hQv>|Jr$ZTPx{4#Xzhqffw;lP8MsDngda86#AA_nU)IGyy^*QH z{6N>y!GW3;32bIwsL*k9t3D!Jokd;!b^i80H33z&aq{qBH!vHoJGSsKyVG#_%Yz>$ zogySuF3=axOaX?wPxn3h5t)$i!uf<}{G>nD`=M&1G40jZ01_`NJ(WyqLyrN2#yUy!LEGhv0@hQbwCbyyO)4#SskUcD+xr`{u1V9O=Dd?f zH`4G(Q4>pve@A)Wvz!&MXI|j801kl1uqWt?3LbWlrk+C`waRw) zbR=$&d$$|I3IYVMU*3)3a(>P)Sv>WKIh=}_CF6JEwdKWPiQZEs60RS=dati-z1Id~ z=LIl{aAS>rjGfp0RpeW7v3x1IauL0Rg1HYx7BMVES|}+_AxbP~NQ5it;BeSlpP8BY z8Oq4W$T5!2#Kbi3%Jr8yh8kS*Wjk{XgqrQ(ECWK&vF&#S=VPGK(qE8}P%C9r#aEwy z9d0WfLj$S+*XxcOV8`!{x!B@ep{KIT&e8Do&+1`hu+|2NwJNwf=CH1EXY<34w-UW8 z05=>Zzngmghbu&SP~o`R`D@Y@BGNuMc=sQ^Nm>~F6BaC&x-ekpnGdj_g4Q06N9QeD|HJcrNtaWWW zpAPv_iE0&I);eV^0H0d@ZV9k*EORgcY}m3MfI7Y7fa)p_jo7hWUt2xsk(JXj&Xo>1 z+oNK^=9xyo^Z{2*WsuLSI}KF*Aqnr;hMJ$4FeW?)xWn#mqMH*tmwNAqSefooH4$y4 zLQWX|mK}|qJ0svQf6M3+KSR*~D@_Gtcypq4&WCKE<*d(TAE;mAU^AW_0P_S52Ehc& zog?xR^8Qu0WU*mZuBD#L?zTWC^ah}W0TlVa$8x7jxFcG#3VP|TSDJ@Gh*Qujoutq8 zU#5Bz)heucL6NRM$MhEVI40h^Mu}!!>cT7qivG?J94DfM3cJ@l>w=W7K$0NgJ0AWEtw-hQB>!&ajI($ zdMdNE$NERCV+Q>x*}X3rV|z3AjK?H+>DPms=7+xUqg2wonP}5Bj^6-q+IgM&BAOA{ zL;Q>zLqoM%d@E7O3KK}l16Om$mRQy{AKF_?3Qp4zcdwklO`u)iW(tQhE)@=~9&1Np z>P0E&4e}X(<#1i+7SK*zfI!SMPwLDdgdFM_dhmi^H{|um_6n4t2tmI!1GH!vq zMU}Ub7MHt)g8Yz&{#6Nx5osbpN1{vEyU>Iz_}axd1@Wlep5giZnOjWT9!vck_5|fQ zg|?AA`*mobHK-9p+uXsR=H`yfzYcoH^lR-q_AOIlb6{+@meix1qmba&hle`p@S~Nk zm2g|Sep6<9C5iFv%Fvj8uq?NUr~|tkY>gwY9Xn7XVO91O3|UD5#@ZNZa0&UbX|%^; zS>3_+@96$9gG}8`Dyw(jbAHc;h7Wdz-GSgp9@if<$)CW1!(!#$Jz`>6x6<$5+r6Ow zOwUj(%k3?4f3Hb(^ip2BAAHzK8i*17o|WmZ964}sw%vMi3JAJ6O^@(=zI8+85I?zv z!3C#Z$m8;;>bXoty{<*w=@9+O>2SBrkfX3T-uk8T0Mi43lZBPi1YM(%)Ywk;N2W$y zM(l5>0MG5(Gg6^&Q{A8!)MhrCWO8e)?hggwysps@K#c&hDrT`ZVqaj59J6_~H&?wg zZ2{b{UsEaEob~z$|I^;n&wLyTCG=xLjFBhs3Ty+zutp{mWU(eavu&r4MKTX2*J5 zSoPU8cN-^5x$2Vlr!1!N?uQdiYEcj;EwDV_w~8%;8vg=i+df_S`JCB(v{@dhsnNsH zCD1+1DygPUe(Yn5nJDf3jD!@TLAm&_kmZJ#~apjK!ByAr+0p^ zMD?GubP$LG$04D@bu*iofawBsOn}$9T?aLM9dgqA(nDtMpqHT^6l4o9Gpo3ZI1)QJ zFJ4&hS@IY6>-E+z!dXO&u2tw3dOpLwI*h~*=qi@`SAGa{EAUV&rObEAbVYswXU zczBr0VOi4YRasdXDdi>N}rI_oJ#tc0RyYtHQGe)KP8|zP-izgF~FxFj9>x^pE zz97fFVs8630iVAWPOBlX#ZV^Q8VKhhv1-?y$8l&rEy5R=1FI? zHtnWqNMhiokq6L4GcEu+@p633tc@zBza;Ae`-iayuOjn!Vp1>A1PrqQA+LgN<=X?d zCi})*?(U|~h~wd08^BBf7sr2%6aLGA6-=gUtcnVQsBww$JT{;=%?Qq%Q{QP8W!Jrz zW%2><3Wg{QeCvM~Z^c~Fe@M!M^_zEKcxaLVYl$h$Jds;8^Tx-5*wKc|0cG>Hmz=`b z!$XIFH27r8bp(=&hc=3d1FS;cNMn8e`G;uIrnoU3MJY3Z1fq1DQ^Zr2hlbSfJvBHo znb5mf{rA`u5V}c`gyB-KZ%(MtHkTIndcO`2K9S%OO0==S_pTfGLPujyIKp;eUqB{D zWD(ud4cLc&znK9aq-7b^>PdbC#vTBE!Cf2~sLqe(hU({CJuWxxX#OiWf*RNca@vj- zU0FN72H+={reX$|=V4xb4#R*N<9On}NSN;^U$|Y|t;4{K4QbH$$I9X1O38aWvV2u7!fl7HTK0{VMIVrF@QA2kSLaGt@z|9+9?bXiKS}DVU zDi4&e2aS^s#n<5qBfrzagFnn!#A%Xy(5ET}PF;v_F^9-@Hr0OO;Jk0vJ#9Jf6V0uH z3GN>;VsIgS{PhOPJjKiC{k)GvsHLbdpSP*@!~^i3B; zQD8NGa*Th*2hQPRA1?dUN{$hz!<$Jk4CJ&YkSidlNxbM=dDOJ@p)p_6yv(ng z&8jZm)q%>`Z_`spWc#onae{ZlyPrz5@%}Yw0Om$>|1x30%Qp$R$B_DBhQrlUjY3)M zV5~}-Fd!tr>5hG)jsTG5h90UU2Ktn3CxgBCeGxvMtF_P!z#-ZlEv-p}-40+hdf}8W z`S0uhHG%N$14GI@w6}sl*1ix($@Bn;UHQB|u6?pS-ikOJlx)QZ4{kLZW>U_~?3=~@ z^f0mmaAi9U@#E|JU+#nY@C`7#(EDSv1A!=fOqUS=1=^)sN*O3 zqop}&H-%N=9n1hd&RXO(U}J$46EZW< z6B0wf&j&b=z!?pt-%sBn2RaE2xSnU>WX+uB4+^bw(Py`lqC3pO*wvlP_=CaE1hxja zxm^N%QUi_x1tniI!N6woeu&!L!1M^LsQ+vK!_Ye$zo0CGCl|DR_IJ{(ZegJ-~DQwb6kPja}&dkp0I#468fxY;|Ij~M(lBlexX#3kL1ct>wZfkrLdE zlsj$qa-M0{ONH0#nt6r|h3UOyRA>vd0$}Ip4mY}rn%XN{TSeFAtSp8ryoz92jPCfDN4sl==%Zz}>FIOUn(}GG-SVsgC zu;B;Codng?)PSyOBn%bs(8h)GpWFWPkWuz`k+Ssf$_G_XC5A01&SmsCC)qHOAFvU^ z?D64emdvf=n(Fp{efUtYf{LesAjBU7m-Z{74NpR21_2!1)$k54RwF7XAsA^=pKoO1 zW;zpRq10SjW`ikWqF3LE_#wdQDr_M|Ws*i`xJ0gX#0fg5&-V;C~vT7ZlX|Ze9=_bamenVh)i{% z(6@e2n!TJDJT2zNb}arDbI_U&Y154Cd~Fq^T^*Rd)LIxIpCIWt_E!0WOFLJh*Kqqy5EFCtVt_er3Qe{1Z%M5Znj|HC9dV?VFDrL z5G|`)^8IU7AQJG$s=!|(o3HLSP!UzH*zX__5tp{<7{;D4J9Wokkq%)K+N6G2<)tYh zvA+6h@*kf&@CdOUC)`v3F(8JOR+=s@E&=agFc?Ukas~lhk85l*Ywb>nnj(KuTt^5M z_CpC8(092%s2#gzEQO~~+VIt%?GUf@m7sa+5XCIrDZ4UBHT^bmS*y0o-o5irb} zkBCP)_1UUqXX?L?x&iUMY>lRoL~Xt_e}FyepALCbkj8p1mg&-H?g@@QTX&~IiYiG& zKAk}N(P`H24-gu(30Z=N4pzn=a;bnJv_6g$L|~As|MK~SUG+F#n{*-Y6;L_j_}(Fbl)Y>K8yCbs*)G&+vc{Irfzc_Y z*WnM)QsPpGoXl9HD$2^D-Yo6!er}6li;i9RZf-Y16u>rf8}u!-xv<2gK5b48*yTWC zvslvAhGZ6l5Fd}IrDoRC-UhOF4j+cXhiGH`p<2jBp*F?PY`guxrlEh3CX6&ajOD|h zZIhFeghe2Oj8j}Z#U@qkUDZd@!NEZ;h{*qO{K>VBHTm)@?eW%Bjum<9E&Z*My~^(T zqk^U0W+R=tGy9K;Mt!Mg0bN(=wI?$2?wU#N#ZN^yp8WS|{aGDJ(a}}6TtjWJsAwt@kS-p>$o|E0p(d95B0%fIkTDsTd-aNEN1S}+F`eG4 zt7feFpZ7OqjTR9FXH>|EC(*EteQ84|Q{5Nl2d6s|G1C@Yn z(zy*478aspOJPHgkAZ)vz$d(_BSe#}$@}R6LQ;>TA8tE%Nmm2G7jO5IA4SS`$5}f< z9o*RtiPM|#W=1tO`xc6(oois?Ye-GEf}=|HC7C%p)~oN+=a2AW7sk(N^rG6ToZJfz zNj~s)B%F08@C*RTmfNKQ87Vzf+7KpE__<(LJD@!;X=|&(f^NtNmn~`^^qBizd*#zs z0iQD_Xv*dn)Vxz)Fa5Z@qSaSVVj0A@2hS|?en1tG9qjt? z6sP;yf*jpa z_-**lV_DpYGw5srDSSP6EY&OVXyP&WKl;jM!;p9Uqh=pcc@<$)tF>N5Q=wtvyvQxl zE4=1QGIVmJk?dJgV19LjdJVSE&sQdbNBKcsnqI#iO6yv}a$1<_v0mJ#EBdw9@oH`D zW{>=mpKPzLo0cr@Tbob?$whSzS-zHfme=meInRbnqhn>T>5TB7H-5VMj=cl{8Uhum z=&xUU7lc5RR{3vmxoOSlJR%%}TU%KfL-_oFJ4a}1D)lEvAFwu+Fw`5qq_g`hV~sg0EUHrx6s)QjAh`gRWYS#{agLG!@W zCt4iSctBrUW-vrVM>NII&j7W~hf9CmC0VE?U>6c79_{;z(4j{ziKfbD9_|&2Rpv1TwK2{m)N%fuf!C0Jb^U>P-oe&e-6(kC(Sjq< zvGkT}2PC&2*YkG=k!B~-L0NBss|9lMfzAqz# zH~}-x4(o}?AN(|9R?6Ng@ypb~ULVOZiZZ$0%GG~Og=<{yJ!>I$+?{g`!hROXslBMV z!LB?;XS7X&E*T!CU!VPSsj6+zZ7=4RR+I9yGyA~mrWk9dG(L!>BdMuTLsqYx@%}PH zAx>WCTmXfn-BeBz`mt~U5(UGkBz!=UR{jtMJ{BzOeeXPk*jF3`MKlU00W-o$Zpt@`Flq8X>hbTXC=$Z_e+_w#(psK`6}F-dP&Pt0CZD72}|27e8{k$8m1wp}4|I@0Ix7(XJAJHC zCmeMdlQ+{X{ede97R6fib`+s|F(KAWvfh36*k%I7V!u!RBbE^27dm5#R8GA%t3Swn zIod$ca=bCb^c4Y;+NLZvKBiUf-)<84C>djS*$`6fTa*DRuJuJP|#iX^U`JsqY z9;P;KXswBO;MkuSo1!)EZPl8pt<0ByQcfd5tETB>s~+nPT{pRi*nG-OaO!a~l|>?Y zI@3H_u#RuFDzTr0X1oS=eg5>=dHKDY;%WQI`Cfb79xp~?{ps?rKRL3R_cU#*H;u8v z4-DT7+F;6`YVU0+kB3m>`1qxeihB6+@j6Yzh#}9Vek_E{ou#vX%fweB_HD95$q7KF z#7WZl74YYZ0Gr`pvGH+@PiTXd7J2TTD>z=jPxi}!N${XzAhS*aMny2li`|6SUhSVL~flN5dXtaTM(Rmsryfr-lcPMNB7EuPwMh=$Gsdx!I)M?IWT$+yr<>s0#d zDvoo_&FCrg9G}}_h-G&6DJAnj_|uiE^zoNyT9;Ya@5uJ{o@V07i)Y>JFf#*P%hYGj)i`{}NtYb9nd=OC3CIze8TR{uvFP zzVdc#JlZ)5EH9K%wwcPkGKyG~@sV@K_H6RahaNQla=ToP1O~+Qc{#X}A(_}7Nvh0x zO6rjk1*JXp`~}|4Q%!60 zxIUv>J6FX#a~=F{CJfDEV2_a&B(37{i9R~V>As$OCvuu;>^8j(psRL=9la(xz1MycQ$=_4hFLS+l-Hl*O757v z%!{bVmv^9+U_IZ?0YQ~Z8RF@PR;LMUZ>Mrcl%C8OKvDfDT2pY-=Nt5Yd~GO66BIRE z>q5F!Vk0M*-NFZSuMe=aV4Q&u>@KoahG^LR{XjpKg^LB;6=<3y@?@bPlR#dXs%LQQ z&=)x>ji!lQnF~5t=%_WhUSFdRZ^&( zj+BA#TGTu0myFl)8{-x1TX{`$0kQ9_I+aVhXY1!Y%_g^9ZCZD+7O~*jyHQ;SBD4kd zq7kD)PqeDhKF?nmoLXR!h=p{HYfAG${L-2i;j}CFPX$%SE%ILCyP{LSSbBO+L#UK! z^b`vif^p%XoUh`e0y|BW7hdOVL{0sC=Y(_m3p1IGkUa0z1QM`AGp2*s94dRS$9};w z;{7q}^W~7hBHX^&QLF2n=R&Gf1aEiKy?DHnY_#0w!^(VFIh~Ost;H(RXKM!Bvv17| zK-9~;ND`by^+RK?yn{{QQKVNqj^siur%SiK>v=}ZU_3>=&WjAA0x#GY*5+;TC~WmD zW{=aE+b4>Ffg4TJ!ajv)!(UXMbuBtHpKN-gr(IihLqEXy88}5A`cjZ(gJW-1BD+B3 z%^@D{txkT(Kju(}_dL)6n{#Ek)F#$f3D$dTzX^(!UK^L&b_VT&3d?Q=!Zpk}O|VLS zOpHCvSOhTlHGpjPr*nhz{}n!5mh2RfQbid-O|}I%D7)7Zg>3wZ%z#5Phj9u6Vnok z5@eW}lHKB&lsPE83P^pmOlB29@j7M}%Rl3v)LUh_@`eF*Iw_-M>T17K$xv~`A8De-G_DvOi3a3WTcn*r(d-gxQ;}MFqZAq z$1eu(=nDQoNn8*El=#b!ibXOs32+P3zFlh?zBUEK>hiXM7FgBBPFa{iLFmg)FYkO? z+ma3rf_Icd19>GW_r3$M-O}+Dr}GjH%(>e&Ov;a6>r*AOBX&I*G6L9+FDPc)KxVUV z#ea0k;UUHKU7Iyuk9NXcbs+xwkyAvpIRM9Z`9iAq2b^`gnY+2yof>ZS)Z58Vyc8pC zD?1?AY`vXXtJ!g}r*{zR`BhEHe!xF9%P}c%C2KQhllg=UmdS>f&%lbV3TwLN4x} zU0Q1DZ#L^Sf2>*7$Wk^7eoBf<{NL{|&CdUG$Q|_^Afkp1h?R5JEBOkyF{*XO+Ot43 zmF~?8ACvCYJpuXPO=7s5z1}H9G|1_CraGqSGp>Q3#-~x8;Y#c)YGzVoeaD@aOA?9> z*x$u%W#&}JJDV2cxRQOlw?!0~k!ii0lPNGT3%geT8|Hup^s*llvLP(5`#-vB21r}K zT|3|w5Iog>-?_0*Qv51Qth75<$#z@A>{W0gxsXn30n+gMnL`m5Z$#k81YH^ipuMrf z4PZ#^uDr({5>i<_0emQlA;!djCw7>Uzn1;5gs@JxAnd4m5XYxc_H7or&cUJx@5S_3 z@+SP;(0Z8>j%>)hsBjNIb=T{XgOUyL>y53}HmfBvm`1R>R-{Quv~?nl78u(#G0ZI> zy7+6qgL;jxe9K%n2n7svw4sh!Lca#AmS{XVaPD1BG!gY+nVGRSX#$MKj8l({Ijp~|V z+{$z{xbvry^)j#R-UqHmQ7>SmJvX5uc^X13BoIExtpc52G(#Wm4?3dP)+awEw4~3x z$Rtpe@1!8f8iCB`1r}&<^l_zeIHCzZah&guYlY#YRTOYz`YQipMcCHH{I$f62!Qw# z0==nk-gqwtym34uvUV1sL}#^^Sa&!4V7zk~+H7MbxPr6w_CPbc+EyTU0%Tym2cfiA)q zHBg7$oghk7QsNxt>y=mq`ahs1N_KfLaD~abv<-ya6kgvo%7ltPRSd>2pWdeGYqO<8 zV7@Im?XJ(%)YP|p6(L}oQ+%uYaTf~{6FHRRH!@8RJlsSq@5-<{s^c4m_h>#Jl#yUt>9ff-38G-bG;w) zX7?AfuiGkrQ&l%OUl-2lyyGiAozuXVT>KEJB`K zX1>RC)B#g!CVQOTN8w)&ieE{e=O_zx=R6c+4^B~q{(cvRsf;7l=kcPKPmC(E`__w- z1^h4y1&2CkwsJGULDII~J%ZotbNnTqoqPna`{&HTo{HpGcJPlSdLQ0hImWGhspNm` zl$}ZPS)Hz#4O9yyUypctZq`_sKy)uao;$mU-&@FkCiu~cXxkT&&EG?mdxyM@^1Rsk za4fSX%|tn4CmwbT{`=Ao{i zw+(3A^(qS^^dcSV6JYPf5t%ZJ+ne#^mOu>I2 zPrr^0qzTi=;;XHqgnlY`X=;`s0U#nOa;wq{XeQ+M0SK|enH%y2EGqy?fB!p+8b<=3 zYJj3S7y&04@F_pFx~Tc;mR}2E-HqrOIqZgNoB?hKhs2Tg#(4aCuV8iW=c&p@Tfklh zrjYwyOTzax@h+&W-W@`X2d+t&5VjtxhnqHH5`E_;>+z6)gPxI>BmxKtAJy7)rF(zs zV~}J67lv$F0H{M3!x*5)?GWHog(IjL4{X+r!|^}z1Eow|KKqh&t_))P9%tE*^d*ZU zb}w=E<(uLUqVuUu;#h2?`xVKW$W`>JYqudYnBGQH!T50n;7gegF?Z!CozDButoQKL zQz5}179uy)kJB=yETM?g5>6rN;0of{j3i@dLSUIO$x#JI-4Y(Im1tC#1ybT)y@pfK zd8eTIHPmWpv~UTWM&aMNC6!&eX1zJ_V2@m5cLg+AuXnBxC)?Fz*Cg+)-Tedyj&7-t|z*?YV~9+ zqAyrNLTkdOC1SZ~+1fOGcRkxnoC3!AzR;{Vap{{!@5c{J?)&wteh+s|5uq8&o^GO! zLd9BgNas`cb|ffqH{W4^>>(U-q2bBNs=`7XBI18Eri}@Mkzjz%wU-?-t3q{jM4I}p zM<>l94k{P~2>g^KBC{J6hrL98N;4kF)>wp__zr5?-^}okO`knhCecG>eg^NfLHUxw zcW8kL{+)+qr(jyMALl<+OKxa7f*f)JLR@&*(ItU!i^L)RbZ06jCuenk`;P$qPqK_> zLcV$;C^ss8h|W*>)E9QoWS!n>bhOOG`UT8n{g~79PtMuCZWFxOtBqrW%fR3bJQt-3 zi)TbDyGK}AFAw5p<~@p1HkMjaTrF+rion}LS2WSPkaVcK8N}x0N??K*G_Y}OARyq~ zIPZ?#b~8*FL|cc~1OI=pmNPb3=^nDJAPgIzxt})RS2nzWqMZ-O*rFR>*rV))INbs@ zVi8DAzEuRB-Mp7_Uwt1;I>szd`xoH1c^;-85W4Si-3`-fYKxRPdV${LBNCx_n>~!z zfd%wE6(Hu0dpQ-|7qz*i1t1`QxoY$A%i9W=p!-mGulKv!e;1n{=d`T<#Dn=@+s%Y` zOkU;zus@de{;vFNB7{i^?`*Nvdrbm%K46dZ$dy$q`0bsqJ~w$X!GtiPOTg^dP1Imm z2v4%TxPXF%rQ!*ig7WfmFHz-Z&u(%^fIjER=4>OW#W`r_yaD$yPjW<5W|Z3E%+Oke zYL8d6VGY>k**uEv z$eA`RX*I?Oy_dfxCDd1E8^OhCP_JMhnXvbeT|=+E`moFwxZ{!4j6yH&Z%pKw@O;b< zD$7y)W}oNyGF)b?^7LSh26007z2nZFzSZk9oqV&aB<7LWM_x6q+kSw^KHuAIh0kGg zjr1u)3s1B(mq8u?0~b&83m{YLUjc6udb9xnya}w&2_(K>zmk@>4W~;v{_*9Jj9B5; zvh^!5lWyS^l>bKd?wISyYTf$5a}buL+4%OlpDT4qW91qZ6GlKG;b}uO5s&ngSk-+~&eOaf0C0L&z}d<6*v4qCvh zLjq?Y)rGHRvcmRzL}gAi$mXMeva!ZbHu8|GXs>5DUA5>p@&KP~`w{VTUJ5Z+HiW<| z0hq&zTR=dfGh;Q6)i`s;fecEb{ScUiP#Mte0>HZi#m{I|mj5mhi}2??_S*5h92>J< zLkfL^jUxw^zdQQa&5wwK#1O++Qr3gfmaheKv$UR86}rCyG>!NqubZWTRfPjpT$uDN zFd^n71$pbeN<}v&frX==-;MuV^I~k@K6CH38UlYa&rKf7wcusIar&BxjPx$tKR9SS z`*zVLd^)jkT?m-A_rVc*~F=OH6}&i+`tPBQp_(BhIKqqiin_OxX!{2C0GI zV!gnS9I}T1n~^g>M<~>*4&RN?Qvwd}D85adPJ;a44S|h-FYhVq6j(F0EaMR1+&9ZY z(sew{X*lNBCm*Cm=@7UeA-a%cQUg#}ZZNNGg z!x!aL{dB1J?P3nj6<#^#Zf#$?Q<+-DN%MI(r4}p#Z#qiFZ216PPkQ5AJs1JU)3*&l z3Z7^LqztBk2{Fr(Vs5C%f}$@Jb&i5vbNi;>hYugNx2@(i41k_cQklBUj1_oV8g|E^C4$vtTw3i7^}k>}hcRMFXdbTzv8A z=wmJ3=SNwNdzr|jz-?_Mdc56x-=F;$^|Sx}&(Hq)uhe0`_K(!zMGM6%;E(!G z{RSScJ_%sb8&G1E44KIlH>?iBmXY@N*Uw>y9?*IzcQL%^sw3Q9Qhd6|u^)7S97<-{P>(1<(qmGC$`dXH3k#dAqA_*{^R2U47T~pMRtL2AsP@i7w}=I*g-#zsn)& zyPW*@yFhSIPPlEY(knHmeS8;*U9kdX{s@bJ_|YUP;Hm;Rg{)b)(h}!@kkM|>HVoJn z;Ftb^$_ZF;)54}$i60opb?EiHf}6Jyss zA_U~5GZsjyj|8~Km&v(EOB%p!3{6FS3Ro1HF%#O{%*@Q(oc&T$V{!3aZBh(LI$5+= zkug1{8?k`$yCOXWpr)89?u9~}vWF)NkGs~14|#5_zt||0;eTdJAX8PGKnA1>V!pE{SwDC7w9UE`H&y5? zkAh4=Xdm&vn^_mb*Xj342QURt`oi`8C3*2fYNp3-yVFDBSC2$QNZl!LB#o5N@&}?m zLRHF3jIzIO4uOh-bMx{BOj#RF`>0a$S5$@MrT8RCI}Y(0&z?L{#WvD^`1;AB9>~^G z=VGiIjMLeO`zIHXdvf9C#T4qG!pDWggU$XCn+U{gq$IIV0>6fSv}NwljLU7ebZ|Rs zR|dcViM-96aB8D(%MKHRB>>$+;`7{k#4&NA{L-*-XiPKhL+4L< z{DYkb)*k?l?H3I|rwMfxL<4@^f6C^TPTvRJTldK%`z4*6c=oyBs0nvJTk@*8WZU~6 zdl=W`q_N2jb-El|?G@L|MgGH;6bR*}}r%!iU60W}@ zNj5^jI{nG4=GR%``@M-M z;{wh=%FAM}Jidh{%fMClwNR+@rbr~nppx@-!33x2za%+Gv`IQ10cP4Him%Vybc5*)Wi15_;lh`@eDbD`yS?n#c8qyJ52Z}D%l#KO zB#++1Aqz?^R8=0VZOV8oli@4fhtzCr@TjKp6tY0_Lm!>}Srt%N4Hhm}UZ1+Kbj_>8 z+|x!x*k-QOqu)A6a_vJF@uRUZ8EA^EB-a@7Yi@=(=mx$>9#xI8;r1IGQ$TrhL?`PH z6M&Nl;~F1L446bfv;!TVoQx?}1pD`=TRq*~p%;sA;h;guiSgQA$6*bDjSl0rYYRBT zn>VR;Rba;U^UV?V+cq<>^aAN$n7b~do26_3Z^_>`xeR2jxty@-PcQcQXV!9#ujl#J z%&3)Jz|aUoHu>|G@r?&er%u7eVX+N>XGcDM<(C2#vEcA+me|g)(K}&xL zCxRTk8VDl1)zuai-ehhd++>K2nv0BxxJJaBJNJ4UQ}hG`o7TXHis6uWHt540kMRgC z`?bBfT;gF3ne^3#WQW()Vq9efgM9{Dc=IIUy)n_uqtg7wmRtcJ>weT{pGq{RA(xz} zX?UK7ZUxPudM>Z6jKle@itDe`UHND0dx_FB7R=@8s!J;_L2mV{iRKoy^@s?0h zQ#)_?1!0Kdz02I_1~t8RZ%CPZKGx{M^n$ch3Fu@{Fmx?Tn-8}2 zBK`Z`$VaapOvJ;4n10=xc%Vx-iO;S#rE=UPU_H~WrWv{Y`sZ38UCHr)cUBqRf6ig5 z_3)sboGhk^4L@J9Q0yT^T74>JsssMzzoZ4Mts@$C9f+fz39~%Dm-~J52q+58+Wa?X`K@|D#_55QMx@pA#`L=|P>{o0}tM4h>3v>c;bXSMMRj{0ct zg_NnWNz4ELM;x^RO>}(qSdTcn__wGx8bWD2Zzha~9qNcfB zLK3>AJs7m{uh@D+?YCJa0x(?lku_@QsUD8|sidIH^d!asiS`5tW|f+reeq|2aC6iJ z=gp<@g)hs^J`B>=3Yc)O-oLlLN@cV(R%iBfVBq!(OqfEVqpy+4v}QM@57}u^kku+- zusi<~16!ZDd#(M2=3&E#Da-Nc+Atzu)5mCNr}vgvvNT}~2GfYXLpl*sv~Xn@*L*|v zEGQ_MKW56yPpwNUH8mB+#^XoL8mkaSB^56PDevl-A#gtg*2?h<1MUl@IyySrwyadi z=QX@L<+m5OBO)j5sDkREI!vsAT$YF$#l-BLFi60Joo#Y&=QyOxz5NP(ej=1({ZGpq z7_ZXXi!zdQH~aT$f4m)MNFJfw_}F$A>Gm#l0EIFYefFOGA`YUSrI{J~j0=!QUYxR2 zjhObqf<-lWd^sdy9*{%H9QMZVuJr5P4Oh+nZ0>ks3)tJ?%|!qlrMQvS6f5#&Zih*J zfRam@JX*w@cHj3B=Y{SZ2L0m=-u^}MFflL?6B-)-kw_ZAUitt9`$4Dup13neX$ZMl z^ig8gGg~F;q9_4^(8ZfAfev=T0x?b$6W7`7+r4V7yq*lFU9zS9=f0xYWyHRVq%yKk zX>MX7$uuV9sC=krdwYAJi(mg|8~4v)p#9gLyfMcrwqVG^2xnc;fwNaV#XBDDqMkBJ z2Fz4C{W+Y8&cA+~rmooe)-mtGwJh+`2l19632%rins+;J5@%3n?rpjP{j~7foK|5{ zgrWlZ|5g&e=iKj=>5fmoRBztA>cP-kQ+TxCjj*9@T$GUB=hnezOh8APNzdxUjo?sX zfv>Xk<`@$@a@2mN!QSD}?-<;P+~4;16xT6pRvy0g7Ov1~8UOt&0pn@|t`G`;Cltu& z0dH?_gdbaA)1O4t$hqZ#^IUJMI5Zk<%_IV9zBA5u)9Ndx&N4ns>zD?(^E8m0i*IMp7G&IZGqJe1eqKB*px ze65m*mU%^<-_^*s4-LDMt zo)>=Wd%aEOP5kR@7fwL2;r@7v?1TCsxpfk;5*l1G&D4{ zr@uliMWRO2N0U2dKkOC8JzZ{!Cx5yuJgBq{zCNb|^RmN-uPINO90+dy`nN+hRoLJ6 zaJNEM0F}|&T(9HH1xVHEFr1v6LMs)2seh0NXM+crK}lAIidBsztYB@`8-MltA{C^haf1i#!!XnxB;|e7uK#~i_&JPrIC^L}S-_YBs z2tIk~#xn6yWG6xaHgUHQi4W}K>&y2SaG2dqrd?>RM&(DL*?_;j)=2i@^uk*^@;ktj z`-^FOiA{XnlXq*w@U<-!j;4%}IBB>&h}k1-*@gNon^5@>NoT}g?&z(D1K&TGP4?Kv zZjWE-zznZy$8*u`etS?YQxm$pxI=+!qyz;XT?f*7z2fA_lagy+j~THe-|V5GGCqoJ z2*i_XaDQ>nJ-6`b)yX*@bW@*Wg#lgy@2_zNaooQMbin*y6{qR>$i5+}^r7U(vP^Gh zh6SNAI3cm$8pk~Ie^bSNp=!s@G!{_ThZi^f{E|ZXNR|rl30p=BWK?l{v}9b$Bq=;CJwnTgm7}X zj_v!)#ezWN_2F3>3aZ6RR7_2+A!nO#-+QvZc?~f%dGh^W>VcVW5+l!am#V(LO)qUp zXLustj~J2{YvJ~3WH2G_+Pq0BJOB$F-?}t(Jh`tEwK^_Lk6E(vIV7Y1*Q0U}LqL9% zHEJX^Ia>V$^Ue#8E&7s=kD^|Yw2>#JiQ1=|z&`YQ8hZE9zHTn%)JnZVC(>hh9qoD$ zK6F`W*mOrS({r?4QP@V?bbWph#0)?UoP+?qo=-rYf2>QvAI}1fJU$mE`i^azFObNn zP+rw>$ry?ZX=RG>{qur5kKF!j-D_fHRbu>&iiE3^5JU^r4VAj&8Em=!&AjQ0gyZsK zj9skjn~tghc*9V~jHl9j|M;BX?1uE3=qN#%!ax1lYIYO_wU8tygXvFUXO#_%y3pmz zzt=bH6!xzS@2~pfwxZreVoG0n>Vk=o?<(KQw$`}t87!hzsITVE;DEa2Ye{%eN4hIU zKNVU|Q}IYkdw~QD=*k-;QuV%s*^^dL|HrevZ7AsVCOH@Z5BbBkm)Fv*6X`@8ZCR+6 zz;}?%fgf~F1ys=B-Rwv~X3T5E?tN=e+A1)%W#vle#;{hVp+zCS z_Eg~{d=K%gK)w+1vjbg7EjBYVvnT)oI1c*utk8;zr8{{^BKi|}mw^IX(tTF&tfJn` zI6i}&$$6E5H)K6NrL-FK$A{o|1_3Jr&GDAEqKf=sWiV1RhI)E15fT=xP==5gp?|=J%3Bpy$E7x>OP6I~(1T#1}r!hFlx|dY6wY8n7eXkgsnwnZ!1waQP zVpr@7fI3p~>+eGCzJCioF%}dvaG6wUI(EL(LIXzc9b@0vi8*IANmOBwSv}&98yt)x zIskQbECx7(1D@4^2V}(Fgol5itU#t(K(hthM9-#cybFBDt}R>Ow?Txh^ISft@p($p z4BEhM97R9Fz4OQ#kuUg}^xIWv(|KI$CNkjZx0h2vjPLmIop*YEyK-(e9MrY|G=;Y+ z$!peemCTtzBS*i^L85>;#~@(w(EV96J2!jn}i#Q zVK&(Kw1>WSkzNhbM0Adv-he@mK_uH~yyTFT;NamY9sB|x z=_cr+?9%Un6E=CGP09Cm&*T3U9&BxGzavtcW3LKRfrTzx!%PINj=2FGw7#aQxte0{ z1ULL;-2A}ijVyf{5vTA4Kz+7)a{Q1KRpvn6n$i_EPYe)5p?_Cc~UfwBo>! z?hQ4{WfenD|BgwmN&n~hQmUX`q`Oq%KoZjl4h#DP88_G-FfuGw>|!8^MsGWmaBy*P zkxsQfkYzLDMlIbtS66Cm3iq44u75=@mX%n3#x8;@IHym-&aQ+#T+!Cnc7*}Vdq^nu zx%l8SU})49BhNR!K4l3$MIgz;2(Cy)^S!Y@ir!PB4azij-A6}vUVR$j9GRQ5PNn|# zl%WRt@vY*%n3-4Vq;!V9?UNHm)S5~Toaw=ev?C=ZGOjmc#rYW{*A2UKSqew~lov!B zgPFFpC!AYlm*(%K9@=J-NS8Io?y^vwtCNI^*=%HFqgD+q_tnmwLEmk>ZNIwgzWC=@ zej2&MTKWRd>{VWO9yXHYyOVaRoSt^U^t4Hl;H_vol2aTr>8Gu7tl-5)5Oc(!4;E%V z(1s`VE6V=}uB6j&rB?zuzl-BXQ8{F*7k}^Dd=c-yeSRovp~Uj$9Wu)YhGg+xq!rv6 z9>ztFy!G8DE-v*B4zl%$mG4_y17EC1$IYfk&aM8alh|h?;E{Qq`8rE2{;x%kT*{l0 zYX*0-k6TtDY8*BjapGWaDkycc-_^!zbS&)iW?9JN3XjG-QXd=HN$2IVDwIdUpR;UP z1NZxJv~cTZY>&tZXvH!d+B&X=u5T52?l-7UL%}lC*MkNxD@iRZ02%?rf71)tk%Rl1 zahyybA&On`oAL-oqV(G42994w{iD`v=fgfnG6d7h*LAI0MB&_jU`jl1m@?Xt!kzG! z-|tVu|4;pX-0njL-{F*IjeB671Zef{|mY?F<-{`kjxquE_wv?OkEv%jul zx4qU3L;zU=W@=&cu8YL^iE>^4RnMK9nn=A1)KHXGQIALd_{p-pFB)G)X(#1o2#@1! z_m${Pc;&>sthtEBXZENFM_scH*bHI>xi0T+Qi1hXN*^%)^&GX7!>g@M%*N`G0x}(r zs0x@S0;gIKWD>w?HD|lyZ`7V0zI@T8^B192>Lwn`#rx%!E49*2ilXXv(Hvo>tpd@` z(@t}@tgNi)=mG$}$iwIsJ5GawZI*tCUqcJ^u1lS0zS!!5#HpuI6dtpUzhEX}FMV_O z6vY}tBfV?-#`l$OHJLXDDg`)$8hVcVTuC#UxN6=q(zvc4#`C7um23IW8-VI80*yR? z(6fgtL=n*A6X{(7z;O6uhhOgva*MY?(jhJP+_HWbaUAu2mKzl5KHK_Ckkjuv)(k#w4e5W9X0cCA|Cl&S5yivj{OC8$4O3IG&D40$)MTs zP2X~e%KPqGOnVZ*ptV}Hk}mZIF~ZPp8!)}`(tl@XW_8Tcl)j{=mFV~aLQwVBV0GoRKv%F#?X%*dUu%L@0J##!^r;z(HP*G?4-$DM1Gjd+G0p2YC714*y_+ zz!7$REj1%UmhQCtu8I?HgKpf@KxJ#r>T?0S6LK^yHq9+Cr^PnSR;)RQ1B&Jo(9?RYSr?4x=K&ak`)` z!x#oxbm!v(7;qW^rT1g|)(?tVw;Cg$c4o9cqYpQo%+1M(6tm%of>C8MmzmV$ z+GwC_&tTG0Qh1Ujpyxb)5b1c&aitU6kV3e*x&ZP)>)Oz|W%hb|S?!dX2b1A9&b2%~ zHOF}IhDrxfQKTFvKG@@S{||u(G$`IvctGmLLB`C;7;B59rKdlIG}FmtIx#%Z-471O zG1XLO59nL1o~*myF345LxARO`ag%fIu?%+ z7k7a>1g27EC7(V$^ztg3exDglH4H7cVlsE`XQfEP-=-5 z;&Gg{SoOwR$pg#Zbk4vjJ|p|!+u(uSJGwtA21MVxQ>~aQJl5ZRKQgt0(Wz zhBWX!{0v<)Fj$rOkeij7S~MB$4vShxU%v^nD>hgpdbBTei;z5aP>x{fKSmedcXhiZ zfSF=K;Hi*EQzK8zswM#go_3A`r1k7gHf6KxQg8mIh3&Y$K0L^mG}YIOe9G&p zK{~s-KtySOSe9zaOV7=@yq4N5cURXCbTX1GSdks_Mx9me#lQz;RspAM-`2P;mP?f- zskb|^+)Q{6NlNl=#kNe+RS=R)j`xu;1r2re%F4=f%I`&w?z%+1=>f1Jf4_{3Ox+sE zFV^DdVk3AMx1^!5lXUb%BL2n*&h?XiBT8JMnpfeaEy_%WqDfeOyQIa5_fHA_uGXVe z6@slbNNdeD2$l}pGC!9!tqz1lU*LqQzo`#9!W78?^9HvC&xXz5%Ce73Us-4NN{t_k z#Xi0EXsee#jNV@-d9TJQj8fg=OP$TT_@`R$0Hv2Q`9us^p~GY|3<-dd74FB6+QBkt zp)3VlaGEnJV6R7RL`zACi;HK3fjA-zo=Q*FpgUPb$R=tuNlN}w=7au&C$mJChdNFus zvBl&SYh#Xd56m?JbkE>8k0y~DZ{zej7B620L1x_cudBUdQCefw62(JDLBibL2-bSnZ|v5Ani)BK@ghHb>l_&<8VSFn8V{$*s^-pK;!G+Pt25NyAo> zY+;YL9@ZRh3I0@E1RhCW^t)23j9LGVG;-jZeHYJB-qFy_Ai4X_0Dat|RX38W^aglm zX!|LVk$<`SHy&H<0idkyS*de~W7!;lMM_^*R>VK$fILsJ9`toLCqc?AW3qqA=x{d3 zyN<6IHqc)$iciMa){Hy(5E(2SPuu7=pP+KdN~9Y8{p!Noo$RoYmTNEBy*qcZKYii^ zr6Sl84D=PS!s{?m@89EJk}Z1igmWNh>gGlKWp&EDpMlT9h|5QO!P!Z2J5#usGjRsj z-f^b%^ZuxvO4$ejcEQ^Ps!DC-d``vZNS5`o1_H#g(Z*s;@|IhqY_~hvm0uZRx0TH4u`c8 zy7Wx;$WJp!wuVTd0ciP8%Je|w_|f5u;-3vpn3p}f`)+9q8Z=3*0yPd-3+YHe#7pu6 z9TY`DR1*;Ih!!V`jTD>M4>126|-68%JdZzP46B&Y)aR8?~)0)wan4HJ;aHeDy7@2 z+_!nUme~rDYVn10;~nIMq_p!=Q8?-kN68%g<532E%+>)sZv{z3cPq_mh1kh> z1RvIc%@ko)mr)*CYzevl2EYHdb$Ms_=abtv&YnGbk9%tv*KMUVmicCZH@G%LyrID$7evBf7hqq4i+!(+Ala$8YwQ{4;${u#7ZrQ9$Pr5XZ zAd}X_@|v2-?6tm*5?txpmZ5v5bAKpRibk?B;OdaKN#3XoJaG1Y&w(Jl?UE5}1R{=i z^CvrBvfg)ek9hSx!0LC zUtz#?R?W85dt3@{w=Z8TD}nvgPUw(zs}?|D@U~5ruGs09e}mmI+XsctA^}9gAv^Ya z?+$wL54WKS*%>QGz!bHXxpzP# z{Oy9t*AgeWmZq@xT%3lOG9PaYp2wcUl|PY3M?dVXBu(QW-q#q&5nE%UGIWD1fat7q z?1UnJ-{42pH_buy<87$?z9Kn}g1Y3zlA5Toxbj{iZ~DjgH+8ssB|0C?9m>t8&qeB5 zER7E6d_K#!Cl|SlTte9Cv?N5$R0d`F5anWm?pLLYY9}nC>qgqpFL^~REX>xl=#M<2 z6#hJJVV}#Se=)wcSxxB8W!Bi4-2ZyFI_Y*F#9Qy#^}VM~G!S(J4xf*^BBIMoaNiD2 zZ<-=B4h)2oV@BJ`o5)(|dYwf(<(*#M%YpaM3O5g?yyhjntD!bG>df6>%H8CbRLD+y z;vE~3VTdV`c)Q%IG?5i}IzK;Z$cL4(OLnfZG9lD`0~xhW3a=Bn<%ukWtY=Lf7MU5x z<@S{8)_p*$pWaMlWBWO?(_sjS&Y|;$c71MRbo$LqemM@+jt2ohZ&dBg%bknq`8+;T z%ITZC!flCite4LX$k=qd#jBnpxQPZ44W)`h6z)%x9=i9n77B_>w{O2+*9Z*_oof01 zUDi%$ae4XMc$PuIvxtc5cr17NY$Cpy$RdXtOmg2VGMW4^B!zdT!vzG8+E$3H8{bjn z8Q2uxCqr&j2|4wLDWQ8f-L)5{urCeT(28t*DLi(xAA7f4-fS|3wSk9D^`Q=xj`!H) z+3Y~U{sXVz-#o|)8v;6%9=jP+#I;U~juqP(Z1!NY*pAJ<>UtE97CoNAEmPZkKu=;Q zeovJXdSJx&^OKYxuAIaI9x%Ma4Zvl8^BrWiyV7dA;P5+yC?F|FxFF2qr0~KP7zG!i*$gy~5;BGUrq8!mzVF^A))_ z#LmV?m36u4!`9F=Uo7ONLzpSc~LJB4esd+?+5U12@xC6^K`m&i%( zlqYo^P@?B;;=Km}0`D9es?i8u=H%I6H?(C6b@z-%zl8oQCTrOIZW!|pvm{rnJo1k< zi6Hh&h~y)NM|We@ll?Xd%IYX8dIldxNPh4T!yBeNTrrH-%-^ls@BteXQ_ga#>me;B z*FiG?R&+|SppW3CrR4*FC581KGun-eItL6`sW`n+L6sy7Vm((d7=@UrnEMh$${-?6 z!u?XdX3sxwFnk+Z@`|pmhjHCtuQs}r{$a_0p7V}IH=(@1ApUb3_U=i!ZCGZgv`Xs3 z4fN&)7Z;%o9QUt#)FgI{3w0qcPS({pnJALJQ?W+6Ji zo9WP9)RJ{$MDEjdnHjo$>Bta-8Ln=p*2=fs%c}=B^LP)xl}GoK>8jm&%)!DOWM}FuF2&4matx6%7aToeickfDAbqh8fOnCa%T;lid-~6-I{hwd-?6W;B zCiaE}zqOTq8@fIL8IQBF77T;pg!FD1gW!|gjeVS)gP(2m;y9-c{2CieaLuf!xa4(e zdb%&E6h@mndH^yMuqQvP}eSw6@_uXL+`wHw@W_O=wKX9Oq);Hv1FqDHi zw9G5Zl`ub)R|#aKX`2^7j9r$AR~_o^e$#*Xn{HKAmFBCgh&OL;nv;7UIpfrI|ELVa z>j*BY7!Lb;n*7|`4mR!|-&kb^3e6H@HuOKQwV_pW?w<$^@o3-H#h91aC$jUysuV!S#qbF%vO;4b>1phIOg=*)n@7DT)!z1Hk zfC(PR?RiXQNPp5l%zdtb3Z46`D0s5{^YIvG z6w8=ovyQblwvF;O_n1Q0zB?4Yh8@7q%*LiMd~L)O5X(<7lWFM)`$e~N1QKc_%QDlI zE+`%H4i-P`%Suc~uHCp{w(p*?@nJ)befz%RGg)9c?e;b( zb!#hT_L$A@ezx!E9U(Th@b;x<7XpPh5U5smfp9fbyaaNi{CZsG*V4lXJhNcEV;f_? zr_hxW=TC+Y!d`wrzs(QkFRnx0vW3I?20#IlR0V$8x4uNg#2C7g$L4$dSgiD=+iVBv zQ#Be+gaCJgu*fwxi0*%8;|E7b$=N=BqP;zk-Iyz3(EqEC+Ax zmMl_40Do!JTs?d~!PuyZqD9rR&*$t?RSakF#K=L)mKjFmh|%37XbFjttGJ#HIb- zjX1G8{*d|=6bvA|s%A{{&}{D}@3InT(a5`E+H7xh$g#I9VN~No$EZrI?}L3 zglxc|wk#kWatFRWimlf{7)ARt!t|k1JkeB{qF?*{Ig=c@_jS6k zrz_ohc3O`R7e)fkfv&@94kPKg8!<8==h-eT*WBt7Dw51BN&2#d?phs0>($2v4jV!h zKpf5m3+qBNxKlIP2CiabrL+_kKXOc6DZV_My}5^+=lJobS{emx(dW*Jh_tJcd!J;I zWS@?wq+{JpdCADgNC-SL(t8}7>nV|UoQEIlSb#8w77v0@Ag6f?3fWe1oEGM7*hPUl z$;@=6b(G0BzT^Bs*Odjv!k=CbyyTjhb-d{*p$z!DFgjyD)19d}@&UB!s|GmIXqKPM z2qCrugP?D|L$(%eEcKj>NugL{z%m}~QN#w*c%kdQydJng9qh7yehBdin z@4fdZAK%qP(gBoTZ>kZl@oX7{K=XvYKy%HS|2~Ln`%Yv8duc}_Jd#dxwFz|B_ntq0 z{-(HP^C4r;R3x^3&Oi9jkq0b`uRA+62YN`U4xcxduwN+o64bW8c1PJgyu6NmbETcY z9pjE$^JWVo>X#4tSwU~v-n=f9m%rSr>P~CyqwIqQ5yFPFLZZ)*6P}Vi-IbMB#to+K zB+@)CL{#O&Pw7XypU%MP*XNoyM3;PoP#;(=J=H+LfIG<D)^aa_1Wijj_k8^LQBy-)=$4Bnn7r|@ zd=~mUgL1 zL@4lvgoF&-F?$5CPKQLh*Yr6ps^6vV>q+>^)E18(h~2aO1;Q7+V`F&=&ru*0-t>b% zReN%Si1VjQq8}(v4rp-{qd^I`McF*2oacQ_3ndZK4vpa(L7thVi^0xl zfZyICDDvCN7cvX$#WntO9YXL^I77p(*(yXX`I>=vM&-n{_RZ&E$z@yRx719g%k!4_iZ(;HZTRpJ71Go^& z;GM~01R+N>3-lTBY3t~P&^}#y!-M3kt6+?`X#HpRB zjZ0crcWR|GyuJ$Dv+&)NRt^FGLRR8%zTi4KZ&^ZGI^%A7-Oi0b$auJk;LQOY zb!tjVA)VZ@!Q{EVPqRf<1fb1oIsiB4OG07c#&Qd00e&Y6j_$6suGq`FisSQB{l8qk zImwD!Q93nCJg-eZ{T&40{5vLK|JpQ2wPwv}gC?iIv!Q6yIH7$^Uj5B3P=OBU`8dWgWS zD~88ShFk8n`_`2Qw?I?Zr0X{2KNcnrlEws*M|5;_u#f$>E;vrbCL$#EQe$JIx`sw? z4D7NGEHKdtW**G`7^aDB(A@(2fcRO3aX6dtobMd(UArFAUc(n6hnDR{PioD$cF*G` zQ|E}H#I38B)h*l zqm|YTCGDE!g9kZq42FR!lY-{*hal%(qt$zH5+z4`aO&cf@=UUv?=X2Vm0RoSI3(P7_W$krf zbM}n7$UXyA?FbiYHDl@G;tdlJ1y4vv@Z(AQeUvKGBb-yNT_WB;a^~$_4v?sm@}Fj-R^Mn zciImRh>?whmhIH$tZZ+?ozbA@D>WBknJ)Q}cfhGHcBl2yYnpHE?YVF-6R2;Tdmpdy zBiB42PKVe2YNyA_?ItV4smj(jsfS3 zF^!xsa71)vUl6g}^MY1+rZ0+*kViq?oktgscfO#rVwZ!xYkXbH(hUOZzc0eHs2@Lo zNupsS|`4Otv?FnmJ;p@6%LWEhmbJ@_3oXsWBPvJcRi+M2Y+UiJv_^`+E_&`#pM zeEG5#A96EOx9TO?c@fg-V@!dhJ4`elP%F9;cPrQIdA2_6P!bzfU}@V(L*})Ad+BOD zHTvT?zv9Yu!Ylm@k1VN$$<9UVs=WSdg0<%aVzw^K)(oc=Nd4#1)hNAPWbf-6xbpgd z=sit)``r7}&{%a}nYygVdFarM`A#57sjI8gpQ!cm@d3p_cc^L&;x+kuGWBZcuk904 zB-BtMVP6Pq{m-9cdE1+}!-?Jbc8nfP0f$IsZ` z%(!)!KhAFyxWP&)DwF+jtd?#rd;!GcPb>(!v9bG&qdwN0&zZvYVNN!}KWnxp3k5ClFlSaNI!37h zmwCQden;F{4QWSp_c`LB#FOzz+Uqu#+blh@QeIdn9@YmV1P3y5-bUN4 zpPUeap@i)Js;$k3dva+#(aX=1?kHR#{ z9*%SD(jMYAUwQPBsON!8)suf$qt6~5ZEyQ|oX4v-SLNicYu_%g)t_vRf{Q4wPP>p? zb(!7D$BLl4e|GeW*jXHTC=ipU2qB<{NcRdui;=&<`F5he{Ju~5#*jq zMya11T4IwW;JSvy;vE~hL+O?QzDZ7#rNxj-t@n}AaOw7ksVLk-%@YNqza;`|6$*`V6 zzc1tl99yQ|wnH0BP*1z6k(-b$xPIH;Hx&{idF06B7v|26jst+50LEfs5?@ksAwU{D zjT4L|t5#NgW(L3+43HT(HBCdN0J7mPG+$2k*cf>VkN(V=Gt4j3nGis7AI@|d|6X}Q z!$fl5Qdx$F;p*llcDG&jZMojTmxm-O8>i;xGL$@}DV`l?gvFL^YTK88_s*RYEY&qN z&I?0PVF5!go>3r?Zh+S{65Gf-OWnyx;qooTB>;Pb2>m!BAHU^Ea!utoZrdAYX4@B6 zSG!h?L`=AyQ@dhlygy{kMn<@*-7x5FMKyH7^E8hgy|>Y&z)C2P*uE>O%k`gSU+SsX z3cLY4Fx8PVTg-eaZzWt|{H*8o6>ro2F&R~t2<6`blpiYVAv9PlwJE8*Mvb94mlT&EXqsRdw}@+v%d;Dt%ouSIH}C z@NU=Rqak&Y>X#@%Kq; zAFjd_N3TQA5#LLqI+h;l>Y*RWE53b`$v*)~c4?OUDu@fZ_8|PO;RZ#Hq*>X_f@^GGh^tA!Z!*01k-sgo9 zoCyOa5u;=1#`tW`{4esVCre*q@|wr437mMX(C=d)h+lB>a9A~5pr=()))_- z(Q@wBm6cid__f(J0L#7WDw2?xm@^2bS)U#!9re7@9V?{rPV7Z#E)TFr6O38fHAzUe z<>W%vH#V^%MAda6&*A-Fz}59LkG_1!rXqgmc!~rI!4^+Tj3za{*r91NBCFF1l>psAPj(4j;5hnAC<_<9zqg4J~-g`}R!p$oni=Bxq<52cB}4^H4L zw!&|_M;HJzJC>>Jl=3^!sR#{q&!rpWZhhqw7e#>rqHPBIC2tpPO%W zI}v&I>{;;MBO<#2#x~ZIc3F&lOPv$iRB$u=WG$Sdb2>>XF{kz;{J6_G&ijB|z=()2 zBJAXvHZT<#7w)z(HW((L-CnvfoqUhF9#{ClheK&CH*{ob>e|dt$hNc=SgnM&!`9FK zoHxDeTN!$sbt_y1VR+ zBC?x~j_FskclK8~PyCA70YYBZE*kg!T3l(t;I!vY;k_tedPppJGqYeQjhu`zD&))J z&pM8+AZ2dy*rd^)Kd-EZihc7)eOTf!`Et<*(VAmT#(Fk>Kp(PkrDZJtl?%-m94jqk z>;c)7d%KylbB9HEdF9;6NMt3}B&#B}UzFZ)pEJ~vR*9Jv%2QEy+Ttk{HTm^B^DI?9 z8`6GX{UO4JKtrfK(OTg*NY$iWf=bVuqbaMLnAWjPxaSx!XEN?SE`uh&Z@!uGR%$MM zMI)17o>jmnOU>@%8}c9hhRA!z`1utEPw?y^9|qUWjIpV<4}L6&Utg{Q*(FjIeck-~ zEhi@@{$5P~U z=bJ7XZ_;S^0XMgV(W4?~&uYB^!Rh03vH(RK7hLGc;udgrcJA3Ce?T@oJj|o;qJBsS z=+3nA)YmDIaIJV@HnM+#Ed8S}!hoIfn>i&D&h&0`0GCqFJ&?rvpSAvtRdr3x@8yAt z#^-(0m|sp#P9`TLs1A!m0s{@L>m;m^M*x-}39EKNIGC8u+QY6FNDmHXgucS%&l$={ zD_1X@#9cXntXLU4P-xW%mu@NR@UcTLEeg(``_6=12@lC zLtC!nRxh>HDkDPGn=47CieG6cw66TptkMX%sgs;u!i%zjxZTdq_ew7VmW zoH%F3y<+a;x^Z=?GF1vb5YN>hE}_1X@NhOMT*A>El#o3!*HQhJ&I;yt$z1$?mR;`i zY?FE$q++eN$I8e6K~hG(S63Go6^Y*Kgg%FVrILRp=uwq8PXC;qK8Z(!jtdFde1EoY z&q1+-`jz1LjY0^*n)?qNIGM2q#D8G2D?VLiV_}iNK)%|Wt!Dvh0Z-rf`TAa`WT5PT zF4)9DyN;wLIQ(NST;Y?F@_^^XR@mLEDt^WTW|_$o*8 zuLE%A1&Ujrpum~So>oYu{|Tj((yE-_ky-`EMbE7@b9V;XD>dAt3ggQ_4FYr{tf_r`2sC zwK&6%u}yG{{Ey2t1;oJu5yYdzCDvM{Xkwme5CAS#Kff7kA*3IT=YPRhAID!W&Z8d+ zw9KpvL)~#19*ZjBrZL|Vc7ACGIGtnVmY>L;LeWZx1jVq#dcLPOp_r(|9W7;#*#(&z zU}5m*v`&Gz!}^&8B05s$-x;&u$z4H>S}SgUjrd=X5!7diiLX>F^QN#vIxNd>rXuqZ zVe+VF8KJ%QpRe&&zd=q_Th@tFr}4If!rj$*-j3n z;S?}FSzo@*@>VN@5sOa;@M4I7JtFi~^S}EKe}Bs9&ftDZS}6T5#f$UpE_8^LM@?Js z?^^%)ba#+C#exW?Fz9nLe9M)KLN|F;G6)v|X-4O{`IpQMnnrG*Rnsm3Ie|%%}@a`n| ztyo@PUteH008t|@E)M1-_&iU+x%t4!X)hgJdcrT*u8?elP?BMZlakI|yFQqXo)!d} zN8JCb?z`i${=dK9_8wV92_@x9Z zLhL7Z(9S{Pf_tN|f|4=7Q}~5-q`WPKL*5g@?^RrYw8#tt+91BftD>R;f*wZpCq`l- zBO^bTw**H|rDtV1RTqLZP47BDF7D!AStUG|B48M-i;D{zTRQ%I)JknCbxx=s_c4y% z>YKcOPPhC?&L9+?Z&msTz7kUUUyzFSzmW=lkjUj@IBPBLe1gh}asPgbkjHyG1q+b; zFtD<;ynT=v>hTS$^!QDt|^0j?{1h z8b($Lk*uTR)9E}8MB^0j(GaW36FjfHF5f|E}Dr(tFL( zq5NO@czBwfnb|vtaVgoPA6Hm}@q%Bjhw*Q~hsfXs3QIWet(8s@2L}U#NA9mcfyV>7 zNdPl3NPhVG)hj=ZZ|`H#p=TZE#5H*`JA1j`V4*_B!!&4yKq~7#KN3Zq@K0`nxYLfO z6_DG1lM_Ajd{J-Dmp)G%YgT zoE!82jMfp@LF)K^39v635AV##}}GdZDG45@S; zwc?XXCnO}am!1ODOIMh@&O@+&kG9ZzAY&@=8`Q%2`1oc7`kFI(?zmDTLg5S}EI-*$ zdpiC8V-$0)$*PP1k6FBHUe}8$$UnSq(sw||-gq@^6g{3HP^&s`9Q@!xO4y^f9(#GS zh+kD` zSzXmzkMih(e(>>>&LWLZ#x1Df-jHrN>P}D-?DiYs)bc zk(7K)Rd4CwyUK*j#R7EA!os5F2N5ig{_UpHt{pvsCz}bnp@OBpWr)Y3If6P;H%lAi z%H2Q$4dihZ@50k>H4JUfcKK|_gl7KQC=?lbtw&lwg=n_slwW(96#z0yn~S$@-5NgQ zM{jNV&)hh`9pHffnHyV3@ic!>2V*ddWeZRsS2Vbgs>9z|WZTJNq_+>x!p`ovx2{6t zU7O3j>`+7^ftn3|L%PFIwcV~E9>cbFD6Q|NdaA+5;U%k{T_|bCZ~gm>9(V;=xv907 zZN~wJPqUcY+OnZRK^IRZiF>HM>U(ObfIaQ}UQ{>s)a>))DDF@JCzyUXZ?1X+A0q;T zJQ-CT+RQci_c#-hISk_cfM=FALRdofwY2ik|KLlcO;6kBB;`orxe8WaH?r0W*;dKA zFkH#Wg-0Ne${lL9>J>zW8(JMxRJVJ5*ro^N!v+w=W%`u-YwLT+S&C4wsXRQyja771 zX`k-+NV1=#6W zIXr&%X&p;1A0O$18UxS0u9_d9r*G;yCLqwmjxRe^+uh{`vVc&kuZXeZD2)3>tT2S~ zk7=Rd>p`Opz76xAQ-yRHjcvZ%^oYyNN{R~b&MEU(x49wJOuLeyCzkUVfI`4v$o#S0 z3ZjKLznR|C(&uh3rOItxHXRWz&_fgz70;6Fn;}e&=V6uY&QrvDET3vYY`u8Wwj-7! zFH;LCnjw_`Pr~_A{yzyPp~16Pxh~Z8CpVV|(_A|sH*muBN~a~QGPSn1xO<;vUzhE z5ch|du1QNvZ^Z@KXc~H52XL5{9M{m$fW@YCULrh&e4L!jP#;zgP>tjQ$_Btz4JPlc z+|${Db~?Y`_$0|4{rWF6J`0Czjjk*UVFS1olU7hUqdJLDmfJ+(JeA=G&>#|*I5KN!+&ygQJh+HN_w2!^4cAud$b6*;lqrz`^_s;CNDG@sY^M^?x z*y;Evzp}ir`%Ra`=IUQL;>V948}FQ*P}J+h$5|w=I`(IWi-$6I;>Caf)11NYU zvwi^0%=S$DzHhDs{ll3GnYM@PRpH z29wn3le^1GW8e?+m?R9K@{%G9M5U;&Jt=6I`IEC`P{o2D5kX-d&sNebv{e zzlw*YRw{ zPG|M(FgUugx?96|ghWIyzM$+sjSgOC25T@szx~>cqUZ;K{XmOc@uY~Eyiha^q}QyE z!`yO$$bU^Jq=i4pua9COAb@OY>e|1o7XqJ0R&@Mt=9m)ehFj8hz46*@y#9WSEL8X* zdfG?hJTP(rsLaCONMiTe?q$#XUhK=%zHMnJkN*YNKi}aOkqcj*?0z5JB0IBp32+eA zhLxI`XnI1~Hz=iCQ2Or_k{!W0cFYdFjhFE!!;L?>V14uNH-x@voy*(Qys?PAul3#M z-K*g0!L&yPWdNqq(xR4359EFF)#i>3rIM^Ha?{LK`eH9|%l0!c81y!`eg=fxFFf~f zU*{RyUTFoCu+4~%{Pg?IH#T_1p_@?FK)K|(wr6*e6x*%U@bxt)gU$nzBC5)0raahA z*nS|7qrflI#Q9GXghnNFLL_tGy(xDxCPkL^xB69=V^2R7zGeWW?e-mO4rIx~-v&KS zESOQy8vVDFWmdL0ELF@-kB`vUO2%@aq_71poF_53@Dyd>pSx4sot&H$0v6!>SDJ%o z<^XqH3d2+c7sQZ7zqreMjh;vU$p}1OBq;|i922n(qfiq#N8jZR5UDQ^L(2hn|d|E}JW zMe`_Odw({(sRmAYQj`|dtkubke@_hY$G>>h#sOX)h$uWq+yC(hR|--D{XFalGQwOY z1R)7(B=7Oj&xwBW_nP*0Z@OfGyaIMrr8EUae}DX8F`zyFmnY+L6#@E(T3F%}EK1NH zu=#Hfa;3P4w`cum2N5j-EPfN^=y^&)oa#N=2M{3#vv%O=kTicb3bNrRNvYu>z+?a2 zWCRRtMbg`QO%-@}I1(XV`tj4U$#05=`Xx8T1%$eqK>Uo6aGonZ*iwvZ!*71z`>hs+0`>->;!CIS{h z3D(#1b#Fh(W*)GsvHX?W11?KTyZ2KBUBO>B_CI(2{04oSzixzo?%+d!!sI6pr{5a1 zC(7(>D3}x&V6MXL%9Uvno}<`RkggPwlB%t$GFc1}S^@NXymHKko_7lrOLLwV714H) zIg+~by`fqI>0+8p#E?w5w0}t2YnhOj9~g~P(_#~olckr2ONLABRFsv;_X7vUx&o2Z zJ$?1(h8qB)f-KwhkN}~?isjhY0~mie8e61ZF?~5!6Mv2h3 z7?qq%bMiJO1)@TzlYo!13~>cTeyjzMKJia51Oma*x|YP=hz#31wg&5Ag=+2!$*jzQ722i(c0dgAa%o2 zS(%tep&kM(z$ZKR=bP7Vfrd8Yd+ArQmySZ#T?X}|{3RDLeCbI=A?IH%u*B0K#iMS-WhtcdU%m}^(l*u7OdH3v^i=!h(BV7u*?b&ar zwa;*98s)drIHcNKxpKv%l9oFS*m z?E?YC@i5Xe-w@sYx@ zdsv69tN}v4dql`V7HD(jDmliGNI3yKVqL zz1zE!p)#|zHsrTXtY(EMDk&AyZM}HSvX6m=*Q5yPBGX{4xdpOlP5%4Hi5h@UnjIy5 zbAw;FU*_jh6I}jpJ4^<6=}N8NTDGCm1E1|V2x6@VD^S4x555BqKwXJY8o<6eVLldHz_{C$w?SJaf~68 z2i6NN98iU$c}&3w7;9N$FW-|tQbDnm%!zkF(T%Q^Qk0*6=9y}$P9CtgxVZzixfu{B ztFqI4X`<$4{{cX=g{sal$4jV+?S2dOEppq<(AJkm}|+5Br)f zLOjXvRb6GyfeC>^{_@&d{zUZZ_wUEQ0_;4Uy!#UdyB#+b=I7TLV(7h?kY*C-q;&H0 zmoLyjI}3G|qF`5EFv!%W?}5P!S9s0S!XI?c-nIDdtR!!)d|Bx+T75K9<#rTHV9|R` zADr%Xb=9G*T8kI>tVHt_mpdNV-z`t)+- z^7=ZHmhKu5$0{x#9L;5r?4dbx=Z^C-;1g&(?rbZb194_RM4N*H^V?&x;R4LBLA|dc zIx6ZI%~;BpQ&O>BPb%Cdl}jeh;9WX{Nn8f0wz{9o>gHYa6JPPlvyy#x#O7fWSC@4{ z(Z-=byE%(OF{YK2H!?9%vvE9#ua1MZ~V!b?#Lm0#hYsb#-Jb}dgT-8D7(A<1L@XukvW??ZQq;R+}zUN zfgbb|V8;LadSUlS4voq{0=S@yp_qaT^{4VR@~O@{2R?88(z>ELrz&&T#D)X1V?qbr z<;eMWmHJ0JRs;_|B`RP!(U^LCpO>K1r%*PTf(6`+8mp))?v&=MzPED70lrU!K%B@i zd|1QH)4w!OaQ||%32xJF{ zlAFaNKf`9aXRWe+J7l1^qQX{Ynv9p`dx~h%;SPu{=d>dfVv_wl;$YEK40gcow%3R1 zZr!j|Sjl=rvn)=vXy`DwzP@e{BH2gEtG`vYo75;A$a~^pkM!m0H@Af)9I@CDaE;s= zIW2wR4?~`bUz>!Sf!u7jFH0Nd9u+I~lNO;>ej|}wyy-TmAH^AFD7@1wAS!4X<@^Nv zJ0LhPL<0zRcY$kJTp@$ysAhWyD#{vQl>#h?A|=S!3ylj8Z>PNV&p3|1<#D=S&u_`H z(<~h_%>XRt%$q(81&3O)*0;3iXqKz%!bnp^gCQa%@i;M2iR&0uN=Zp!Az!X+B|?b1 z6esDm>{M8L#&-u4awpkl8_z9YCx0dGG5aVYLT}6c(Ah%s+UzUWuUEMx^}~*uc55{6 zFnnq=P4*4OQIsUyTk`^R9oS9EXZSK<(>!yXXngwfy)AV6ob}d4G(&jGvw%Yc7r##2 z?&uGi!;)2C!G?SYW0j!U3=(gY2N?=)YsS7 zk>rt@jjb&gE9XG!m(z8x4cuU;)`>j3E*?6BYY_jSuS64vhujM(p=W0iofK_9z%|%g zU#LOApz5z1lzxqb1PCI8C>S9#K|eP+-Tf#h8)QJlfQq7NV$PUBFb_BBB*OvPD;m_=9CD) z{8;SlBAz{a78_#q7k(1v+vS;vRQ+0;ky&KFm*6wdr2|XED`!u6lnE=$EK!^$LsI!PkC&qhI z@9T?ohhuVclRzpor#0n^RXJNLXwkH`KnhJ8m6eeZZlVq;JMjD_{^NK64%_1yz2X z2`C(lmql?RC8edjyu474J(571Ho|@CQ~`huK}@_YJKgyjA)#?`rqrZArD2sR=r3Ix`s=I1YG$63bnD%?o*Z0qR>$CAs))Pq0uUF}gp zmF$1$7dAJ+qAO}oLAjf`=>>S7`*ZaW4CrnaSvJ8o{0>*S5yE(Q0bgDN!E9+%v=PvOt>G%!zuoiuecH|TkVC7O!oX=E9$H%4J z#BefqxZH+%$Z3b(d+{t6s}wYi5gk|U`AzsJGrKS^CE9oBwi|@cPlce^4>y<}7R)dR z0bfphjrNn4a-uWr5Wo<`0Y9g>y1E(w>?4to4S!E?soQ5-w>qPf3+m6BfWY~nje9>g zU9Ad^Vs?S1p7Z!-%h`uAe0+J&ku3am?B#8X%+AUKvW#NK1szic9sa2Q`W#W*cFENbs+ckXXhLS0DX=h2YID1 zN(j)_@3Z-!l(4P*&BO!O3!pD7*mFGrdoWPKi#1Ru$MxsNH*2q_Z_|`0qNzZ&WM7nc z``ZK9aka+o?odimK(wLq2F=TY+|1D)Cm-0B1|Jl8c!YUtN#n`1!oHtB-TW(FYPt6>%nM@}5>4eN@+MDjlg z%Ra(=;RR$hz!TfJ?~(zTHP#~1$qqPHC+J3?2l|J8B9fx)gEk?7=3;(c-uux})9Zw* zvN;A|X|kyY*xs?RG4_L5Ov&*( zpeVh}*%tpJaM~-c1kW27ek{U{FX>ebLXd2O_UYM%8hPzEP&!0iXo5h>9B@2rf`Bs} zU=~HD4Je?h_=~Qt0u231YatOFg-=@aVx7;y36X}L5dNn~$VLavZ-mg?*5O9Fl4Z0< z@_-r%t{LEt-D%~aXV>`1E@A|h-4_S^3g#)FF`-BPWbCaz;jmDHeMM zn&u)HF4|N!47}@_`%s$@U=pny?34r*qVW5r-?U&Ob;b+R*LC?J{8DF4?%%)P{aCD2 zDTqn3^^{Z#Y*(IbjhuDMg%r8Fic^dAyV6m~Qa9cn=n_AAbh3o7CkT7j>@&p7myVWQ zv#f>*gN*UhBD35@k=O`dJYAXW%h+0P4=Apg@Ucw3+JE6EjAO`H5R;?%41jkrrFt4u za>#>N2*~2DfJZaw_1-$~6}f_G5)>CJDgf%SzM>LU&C*pSC@Dg)`84|0`XxHf#bZ-% z2$F0bMQXCN%{t!xvG!F(pc6D^UMI!{m`C8PCJ6Iz_24HMNhkycF+pJ_Bq7lwocfSx zn9oZ0jlN^_wlA#E{Dd6ri`rPkK%x{5yb1b9rQE{j%YoS}=hZNU=`_WL$`qF*=QUhi z`FU?a7l`5%PY~JF>yGoCot;o!+|Vp`9u>!m18H&V{P2@_4uq0m#$|zO)!qw&V>tOb zcTNilMfx~TR$DGiz7B28ZE00>iwz2*7_cjhbj-AQ06=x`k)CUgKoghqgCq5hh~Nnm zYtM?((yt@sC(9>c##Y{^`O2}+Y#krNvWmZT!n4F+?mbZneXFMp)^6$A3`b72DST0( zUK}59*x{@X2@UmKU@~D51q8x!0?K+0PVPh@914$eDuti||8~4%Zt`u3!|jl)M8GQo zh)W|&tOnvwJ6Jh5a68_qgB0QE3Ji~XG4AhZH&itYXo00rh3;29x3XxUz{kL(PU$SbgNsBAa!W^Y2;Uct{tvc1srgoOS9roK8~tiz^h z@g7^Uwx(uA1b`v~uI)uyqPldez$-hdTSn^`3;aJ8CZ=Zk+;Ho7C`&q-g1S_m;8rrf zC4|%jkPyo(^FRn?8^XS=Y=F7RU+K_i3q3}Sp>tvRLnNAsiHXTS;*+SF8L4%uuPr7; zM$#)O&DODzxjWiTXVQuZsSC;Y?%dq!yvXWR*3jJ71shz`dL7u8`iVe|?pXwiyiq<_ zieA9f`1SrCjmJ;_}?#(k5dTMH7VzPC5v|>8bq{f}QeORsjaW1Cxbvf@_(^1qC=c+(=VlMTjw}ObH`Hgwuz#Yh(gSubZ6R3Z;wXu1n4rok&?VUNWDxmqbbax-; zI~GLt=WSZ|p%+cXBy;cA$$eLycyX=o5)?@g_ZW2#2kd~Gf zfX336p}z8vS;(I-7A&WxFoNLn2yQl_5--MZ%QLH$36<-m?8DDZR&#ufg^uc zd{Jd<6zJ~yyL0D-D})%nmll(ur*5R@Os}6$UwiJIM&VM196IZRI|C zc&jcgVH~S-sq^-+b*6nh3P1=BO!IYodg5%xGo@%Y;)3TSr9dSqQn1ft0Rl^*9LUgg zvgYFSO-x$S*2l*!%wuC?Q4za`2Lu{{PBA(OlBqDl*WdNkg9oG@#;+B#(pVHEj(&u8 zZENkGm-@q%4TkT#&H{6ptMtd#mYPd6kn*S@?|?9puXdG=$k2T-);`egqP^WI^XFCW zGiWnKDI8irlsvL2B#d&rdk56j)KHiz(1v>}Yqz$x)+R2c6!VVRQ2q|C5~!we}TP6z3ECxyp4p(T-qTK zz7tymg$V)}=wL%11>qTqlL0fiUn7X_N*y++^CUqrcce_n-i{rymY0>qRac8uzXGg| z+6mr_mYyD6!o{}IcpL_z46Vna;kTZs#Ol!f^HmP2gdQ$0Dq>GIslIY753k)oLNYWd zH`fVjkCb}CONA#v(bsWjLHM5^Vg!gRK=g>v=E0VczQx%*O$6w-7N$yCuLEM{)Fp#Z z{xC>_w&1wYkr8no9!g?46ny`+1ng#wO-(RqVrWSvo%b&MEtF5~?Cb=e1qB7Q@x|iQ z8vFVpkFCi7KAU&jlX#wT@0IaoD*z+$-+T$mr%Q9U;m`8&c-E)F;e@rsniw`p^B96# zffL7s00s1+-hvhiE{R=RIHZ1S$#?g=uUZBjG5lQ&P=8{~y#qSXkzK*Tln2t=Op2|D zZeWZ%U!U8nY}nhuf26qC34lyHjzhcC44~gCjZllUWH!lcgAiM$#^Y=AVH&5mdBac0 zt(6NYD_q99>R!%#{+!u62bBFgQRpSa`&AEVxlMmutG^e`t1ZyK*TF=UCy_`qnlg99 z3MiBg!=Xjsvq}_4Pi_pwlK}07!pjdD_A`b)li5a#~BkHY-oM<3xqlcHsspD;^rL{CMcX zvZFs}(L^9AO3zL!Dhiv0QQ|#|&!0=`GoXUVbLU+1VcpS(%ElqkL$5nEpXR++p4}?} z)sW!;bgQbVaULp?t2rSmS_J?#IQh!*^2^Y%2*(hdcG3PKVv%+nZBhLpR$0Qh;e0tk41KnUo6J*QyD=DdIL;`aQ< zC}VShenOkywqOwqq?$vBpL|cH%Y`T*62;9s{bV?x)2R-wEid<)BOnlS8yF=BTqJ%BdW@)fr4W-LTakWikPHVrcmT#uLq-NUrRBG8 zur_Z}IsUcfX=~03Wii z;O+rNSbQGBC;!28{fl4gHa{l-UDW;I#KZ)YLoWKcBTK+q*faM?287|qnMeHAEwmmW z^yI@t2$+!O6tUl}B@7V`xZI4Mv;C?jCI$x9c6M3NDm@t@l8uFR6*lhUqR?@36G-G6 z07hyx^r;P!T|(*jy0_Lpr$WH>&)2{>0Oea8ZyW-xV+^O&MWDmC&B%`Vo&h-yJfG3= zGiQv-wjz$HmoXqNwQEdFqRJpxavVgQ4_CW&PmhB5+TQcD=p+3F z=My|?om#S(>FJ*U5lZVj9HnaCGYl|5tjW+;IRs6RC|1{4brTxjfY`M|Z3#Bin*sfi zmtrIRucn7I{rSkB_z)9UN#NmJ_T*qh*nBoPqQU~hjOsk+)9iab?J}SX`du`CvoIOL2Oa|}Qp_tu+k5gHiC4Ld} z=#f1w04XArE}0VLq6D_4e%rmVq3WV;Y zo*i__(QOYBF|rPu>IgV{UIBq{MyTOHeGR&$L3;2Z3n3zKWBpRa7LYcVmzT3Sxw!$M z2?(9^EkIIg%bW(CK`94DRpxzyNcE?>b(t{O^+4)mszOxeca#%V*lLbX3)^6FDxGaQ zczX34E4(DS7jP_`NMKMTSIE_`;yXVIfj~OGStSqwAzaI71n5W) z_<$!mRSvVVZUa7E?$FoV)%9?d;PO46=b%dh8@tgF9g>;fBexs@J$hofuR&j+>}Uin zi2pMz^qsO-aetUD1sd83(b2@azpoj*&0hwJ-ucUs{W>7fU!fOe?}nNUdc*B~g)g#y t?y gZuLX**|yK|J?b1cmbW(pE9L9WiA#iAzq literal 0 HcmV?d00001 diff --git a/doc/plot_capacity.png b/doc/plot_capacity.png new file mode 100644 index 0000000000000000000000000000000000000000..b4f760da367bdb440587916b2256363e4823e31e GIT binary patch literal 77020 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%0D0Nh{aAm$UdHh|016s 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 literal 0 HcmV?d00001 diff --git a/doc/plot_size.png b/doc/plot_size.png new file mode 100644 index 0000000000000000000000000000000000000000..b21921776a7a9321acd4d1a2b01f455e864e93f7 GIT binary patch literal 67743 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Ξ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

RClVCkhX$N;$GnJLThn9h*>_vzEyyo8 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 literal 0 HcmV?d00001 diff --git a/doc/protocoltips.md b/doc/protocoltips.md new file mode 100644 index 0000000000..610407ebc2 --- /dev/null +++ b/doc/protocoltips.md @@ -0,0 +1,30 @@ +# Tips for designing protocols using `libminisketch` + +Sending a sketch is less efficient than just sending your whole set with efficient entropy coding if the number of differences is larger than *log2( 2b choose set_size ) / b*. + +In most applications your set can be hashed to entries just large enough to make the probability of collision negligible. This can be a considerable speedup and bandwidth savings. Short hashes (<128 bits) should be salted with an unpredictable value to prevent malicious inputs from intentionally causing collisions. Salting also allows an entry missed due to a collision to be reconciled on a later run with a different salt. Pre-hashing may not be possible in some applications, such as where there is only one-way communication, where the confidentiality of entry origin matters, or where security depends on the total absence of collisions. + +Some element sizes are faster to decode than others; see the benchmarks in the readme. + +Almost all the computational burden of reconciliation is in minisketch_decode(). Denial-of-service attacks can be mitigated by arranging protocol flow so that a party requests a sketch and decodes it rather than a construction where the participants will decode unsolicited sketches. Decode times can be constrained by limiting sketch capacity or via the max_count argument to minisketch_decode(). + +In most cases you don't actually know the size of the set difference in advance, but often you know a lower bound on it (the difference in set sizes). + +* There are difference size estimation techniques such as min-wise hashing[[1]](#myfootnote1) or random projections[[2]](#myfootnote2), but complex estimators can end up using more bandwidth than they save. + +* It may be useful to always overestimate the sketch size needed to amortize communications overheads (*e.g.* packet headers, round trip delays). + +* If the total data sent would end up leaving you better off having just sent the whole set, per above, then you can send the set in response to a failure but leave out as many elements as the size of the previously sent sketch. The receiver can decode the partial set and use the data they already have to complete it, reducing bandwidth waste. + +* Additional elements can be sent for a sketch as few as one at a time with little decode cost until enough data is received to decode. This is most easily implemented by always computing the largest sketch size and sending it incrementally as needed. + +* Because sketches are linear you can adaptively subdivide to decode an overfull set. The sender uses a hash function to select approximately half their set members and sends a sketch of those members. The receiver can do the same and combine the result with the initially sent sketch to get two sketches with roughly half the number of members and attempt to decode them. Repeat recursively on failure. This adaptive subdivision procedure makes decode time essentially linear at the cost of communications inefficiency. Minisketches can also be used as the cells in an IBLT for similar reasons. + +Less efficient reconciliation techniques like IBLT or adaptive subdivision, or overheads like complex estimators effectively lower the threshold where sending the whole set efficiently would use less bandwidth. + +When the number of differences is more than 2b/2-1 an alternative sketch encoding is possible that is somewhat smaller, but requires a table of size 2b; contact the authors if you have an application where that might be useful. + +## References + +* [1] Broder, A. *On the Resemblance and Containment of Documents* Proceedings of the Compression and Complexity of Sequences 1997 [[PDF]](https://www.cs.princeton.edu/courses/archive/spring13/cos598C/broder97resemblance.pdf) +* [2] Feigenbaum, Joan and Kannan, Sampath and Strauss, Martin J. and Viswanathan, Mahesh. *An Approximate L1-Difference Algorithm for Massive Data Streams* SIAM J. Comput. 2003 [[PDF]](http://www.cs.yale.edu/homes/jf/FKSV1.pdf) diff --git a/include/minisketch.h b/include/minisketch.h new file mode 100644 index 0000000000..0b5d8372e8 --- /dev/null +++ b/include/minisketch.h @@ -0,0 +1,367 @@ +#ifndef _MINISKETCH_H_ +#define _MINISKETCH_H_ 1 + +#include +#include + +#ifdef _MSC_VER +# include +#else +# include +#endif + +#ifndef MINISKETCH_API +# if defined(_WIN32) +# ifdef MINISKETCH_BUILD +# define MINISKETCH_API __declspec(dllexport) +# else +# define MINISKETCH_API +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(MINISKETCH_BUILD) +# define MINISKETCH_API __attribute__ ((visibility ("default"))) +# else +# define MINISKETCH_API +# endif +#endif + +#ifdef __cplusplus +# if __cplusplus >= 201103L +# include +# include +# include +# if __cplusplus >= 201703L +# include +# endif // __cplusplus >= 201703L +# endif // __cplusplus >= 201103L +extern "C" { +#endif // __cplusplus + +/** Opaque type for decoded sketches. */ +typedef struct minisketch minisketch; + +/** Determine whether support for elements of `bits` bits was compiled in. */ +MINISKETCH_API int minisketch_bits_supported(uint32_t bits); + +/** Determine the maximum number of implementations available. + * + * Multiple implementations may be available for a given element size, with + * different performance characteristics on different hardware. + * + * Each implementation is identified by a number from 0 to the output of this + * function call, inclusive. Note that not every combination of implementation + * and element size may exist (see further). +*/ +MINISKETCH_API uint32_t minisketch_implementation_max(void); + +/** Determine if the a combination of bits and implementation number is available. + * + * Returns 1 if it is, 0 otherwise. + */ +MINISKETCH_API int minisketch_implementation_supported(uint32_t bits, uint32_t implementation); + +/** Construct a sketch for a given element size, implementation and capacity. + * + * If the combination of `bits` and `implementation` is unavailable, or when + * OOM occurs, NULL is returned. If minisketch_implementation_supported + * returns 1 for the specified bits and implementation, this will always succeed + * (except when allocation fails). + * + * If the result is not NULL, it must be destroyed using minisketch_destroy. + */ +MINISKETCH_API minisketch* minisketch_create(uint32_t bits, uint32_t implementation, size_t capacity); + +/** Get the element size of a sketch in bits. */ +MINISKETCH_API uint32_t minisketch_bits(const minisketch* sketch); + +/** Get the capacity of a sketch. */ +MINISKETCH_API size_t minisketch_capacity(const minisketch* sketch); + +/** Get the implementation of a sketch. */ +MINISKETCH_API uint32_t minisketch_implementation(const minisketch* sketch); + +/** Set the seed for randomizing algorithm choices to a fixed value. + * + * By default, sketches are initialized with a random seed. This is important + * to avoid scenarios where an attacker could force worst-case behavior. + * + * This function initializes the seed to a user-provided value (any 64-bit + * integer is acceptable, regardless of field size). + * + * When seed is -1, a fixed internal value with predictable behavior is + * used. It is only intended for testing. + */ +MINISKETCH_API void minisketch_set_seed(minisketch* sketch, uint64_t seed); + +/** Clone a sketch. + * + * The result must be destroyed using minisketch_destroy. + */ +MINISKETCH_API minisketch* minisketch_clone(const minisketch* sketch); + +/** Destroy a sketch. + * + * The pointer that was passed in may not be used anymore afterwards. + */ +MINISKETCH_API void minisketch_destroy(minisketch* sketch); + +/** Compute the size in bytes for serializing a given sketch. */ +MINISKETCH_API size_t minisketch_serialized_size(const minisketch* sketch); + +/** Serialize a sketch to bytes. */ +MINISKETCH_API void minisketch_serialize(const minisketch* sketch, unsigned char* output); + +/** Deserialize a sketch from bytes. */ +MINISKETCH_API void minisketch_deserialize(minisketch* sketch, const unsigned char* input); + +/** Add an element to a sketch. + * + * If the element to be added is too large for the sketch, the most significant + * bits of the element are dropped. More precisely, if the element size of + * `sketch` is b bits, then this function adds the unsigned integer represented + * by the b least significant bits of `element` to `sketch`. + * + * If the element to be added is 0 (after potentially dropping the most significant + * bits), then this function is a no-op. Sketches cannot contain an element with + * the value 0. + * + * Note that adding the same element a second time removes it again. + */ +MINISKETCH_API void minisketch_add_uint64(minisketch* sketch, uint64_t element); + +/** Merge the elements of another sketch into this sketch. + * + * After merging, `sketch` will contain every element that existed in one but not + * both of the input sketches. It can be seen as an exclusive or operation on + * the set elements. If the capacity of `other_sketch` is lower than `sketch`'s, + * merging reduces the capacity of `sketch` to that of `other_sketch`. + * + * This function returns the capacity of `sketch` after merging has been performed + * (where this capacity is at least 1), or 0 to indicate that merging has failed because + * the two input sketches differ in their element size or implementation. If 0 is + * returned, `sketch` (and its capacity) have not been modified. + * + * It is also possible to perform this operation directly on the serializations + * of two sketches with the same element size and capacity by performing a bitwise XOR + * of the serializations. + */ +MINISKETCH_API size_t minisketch_merge(minisketch* sketch, const minisketch* other_sketch); + +/** Decode a sketch. + * + * `output` is a pointer to an array of `max_element` uint64_t's, which will be + * filled with the elements in this sketch. + * + * The return value is the number of decoded elements, or -1 if decoding failed. + */ +MINISKETCH_API ssize_t minisketch_decode(const minisketch* sketch, size_t max_elements, uint64_t* output); + +/** Compute the capacity needed to achieve a certain rate of false positives. + * + * A sketch with capacity c and no more than c elements can always be decoded + * correctly. However, if it has more than c elements, or contains just random + * bytes, it is possible that it will still decode, but the result will be + * nonsense. This can be counteracted by increasing the capacity slightly. + * + * Given a field size bits, an intended number of elements that can be decoded + * max_elements, and a false positive probability of 1 in 2**fpbits, this + * function computes the necessary capacity. It is only guaranteed to be + * accurate up to fpbits=256. + */ +MINISKETCH_API size_t minisketch_compute_capacity(uint32_t bits, size_t max_elements, uint32_t fpbits); + +/** Compute what max_elements can be decoded for a certain rate of false positives. + * + * This is the inverse operation of minisketch_compute_capacity. It determines, + * given a field size bits, a capacity of a sketch, and an acceptable false + * positive probability of 1 in 2**fpbits, what the maximum allowed + * max_elements value is. If no value of max_elements would give the desired + * false positive probability, 0 is returned. + * + * Note that this is not an exact inverse of minisketch_compute_capacity. For + * example, with bits=32, fpbits=16, and max_elements=8, + * minisketch_compute_capacity will return 9, as capacity 8 would only have a + * false positive chance of 1 in 2^15.3. Increasing the capacity to 9 however + * decreases the fp chance to 1 in 2^47.3, enough for max_elements=9 (with fp + * chance of 1 in 2^18.5). Therefore, minisketch_compute_max_elements with + * capacity=9 will return 9. + */ +MINISKETCH_API size_t minisketch_compute_max_elements(uint32_t bits, size_t capacity, uint32_t fpbits); + +#ifdef __cplusplus +} + +#if __cplusplus >= 201103L +/** Simple RAII C++11 wrapper around the minisketch API. */ +class Minisketch +{ + struct Deleter + { + void operator()(minisketch* ptr) const + { + minisketch_destroy(ptr); + } + }; + + std::unique_ptr m_minisketch; + +public: + /** Check whether the library supports fields of the given size. */ + static bool BitsSupported(uint32_t bits) noexcept { return minisketch_bits_supported(bits); } + + /** Get the highest supported implementation number. */ + static uint32_t MaxImplementation() noexcept { return minisketch_implementation_max(); } + + /** Check whether the library supports fields with a given size and implementation number. + * If a particular field size `bits` is supported, implementation 0 is always supported for it. + * Higher implementation numbers may or may not be available as well, up to MaxImplementation(). + */ + static bool ImplementationSupported(uint32_t bits, uint32_t implementation) noexcept { return minisketch_implementation_supported(bits, implementation); } + + /** Given field size and a maximum number of decodable elements n, compute what capacity c to + * use so that sketches with more elements than n have a chance no higher than 2^-fpbits of + * being decoded incorrectly (and will instead fail when decoding for up to n elements). + * + * See minisketch_compute_capacity for more details. */ + static size_t ComputeCapacity(uint32_t bits, size_t max_elements, uint32_t fpbits) noexcept { return minisketch_compute_capacity(bits, max_elements, fpbits); } + + /** Reverse operation of ComputeCapacity. See minisketch_compute_max_elements. */ + static size_t ComputeMaxElements(uint32_t bits, size_t capacity, uint32_t fpbits) noexcept { return minisketch_compute_max_elements(bits, capacity, fpbits); } + + /** Construct a clone of the specified sketch. */ + Minisketch(const Minisketch& sketch) noexcept + { + if (sketch.m_minisketch) { + m_minisketch = std::unique_ptr(minisketch_clone(sketch.m_minisketch.get())); + } + } + + /** Make this Minisketch a clone of the specified one. */ + Minisketch& operator=(const Minisketch& sketch) noexcept + { + if (sketch.m_minisketch) { + m_minisketch = std::unique_ptr(minisketch_clone(sketch.m_minisketch.get())); + } + return *this; + } + + /** Check whether this Minisketch object is valid. */ + explicit operator bool() const noexcept { return bool{m_minisketch}; } + + /** Construct an (invalid) Minisketch object. */ + Minisketch() noexcept = default; + + /** Move constructor. */ + Minisketch(Minisketch&&) noexcept = default; + + /** Move assignment. */ + Minisketch& operator=(Minisketch&&) noexcept = default; + + /** Construct a Minisketch object with the specified parameters. + * + * If bits is not BitsSupported(), or the combination of bits and capacity is not + * ImplementationSupported(), or OOM occurs internally, an invalid Minisketch + * object will be constructed. Use operator bool() to check that this isn't the + * case before performing any other operations. */ + Minisketch(uint32_t bits, uint32_t implementation, size_t capacity) noexcept + { + m_minisketch = std::unique_ptr(minisketch_create(bits, implementation, capacity)); + } + + /** Create a Minisketch object sufficiently large for the specified number of elements at given fpbits. + * It may construct an invalid object, which you may need to check for. */ + static Minisketch CreateFP(uint32_t bits, uint32_t implementation, size_t max_elements, uint32_t fpbits) noexcept + { + return Minisketch(bits, implementation, ComputeCapacity(bits, max_elements, fpbits)); + } + + /** Return the field size for a (valid) Minisketch object. */ + uint32_t GetBits() const noexcept { return minisketch_bits(m_minisketch.get()); } + + /** Return the capacity for a (valid) Minisketch object. */ + size_t GetCapacity() const noexcept { return minisketch_capacity(m_minisketch.get()); } + + /** Return the implementation number for a (valid) Minisketch object. */ + uint32_t GetImplementation() const noexcept { return minisketch_implementation(m_minisketch.get()); } + + /** Set the seed for a (valid) Minisketch object. See minisketch_set_seed(). */ + Minisketch& SetSeed(uint64_t seed) noexcept + { + minisketch_set_seed(m_minisketch.get(), seed); + return *this; + } + + /** Add (or remove, if already present) an element to a (valid) Minisketch object. + * See minisketch_add_uint64(). */ + Minisketch& Add(uint64_t element) noexcept + { + minisketch_add_uint64(m_minisketch.get(), element); + return *this; + } + + /** Merge sketch into *this; both have to be valid Minisketch objects. + * See minisketch_merge for details. */ + Minisketch& Merge(const Minisketch& sketch) noexcept + { + minisketch_merge(m_minisketch.get(), sketch.m_minisketch.get()); + return *this; + } + + /** Decode this (valid) Minisketch object into the result vector, up to as many elements as the + * vector's size permits. */ + bool Decode(std::vector& result) const + { + ssize_t ret = minisketch_decode(m_minisketch.get(), result.size(), result.data()); + if (ret == -1) return false; + result.resize(ret); + return true; + } + + /** Get the serialized size in bytes for this (valid) Minisketch object.. */ + size_t GetSerializedSize() const noexcept { return minisketch_serialized_size(m_minisketch.get()); } + + /** Serialize this (valid) Minisketch object as a byte vector. */ + std::vector Serialize() const + { + std::vector result(GetSerializedSize()); + minisketch_serialize(m_minisketch.get(), result.data()); + return result; + } + + /** Deserialize into this (valid) Minisketch from an object containing its bytes (which has data() + * and size() members). */ + template + Minisketch& Deserialize( + const T& obj, + typename std::enable_if< + std::is_convertible::type (*)[], const unsigned char (*)[]>::value && + std::is_convertible::value, + std::nullptr_t + >::type = nullptr) noexcept + { + assert(GetSerializedSize() == obj.size()); + minisketch_deserialize(m_minisketch.get(), obj.data()); + return *this; + } + +#if __cplusplus >= 201703L + /** C++17 only: like Decode(), but up to a specified number of elements into an optional vector. */ + std::optional> Decode(size_t max_elements) const + { + std::vector result(max_elements); + ssize_t ret = minisketch_decode(m_minisketch.get(), max_elements, result.data()); + if (ret == -1) return {}; + result.resize(ret); + return result; + } + + /** C++17 only: similar to Decode(), but with specified false positive probability. */ + std::optional> DecodeFP(uint32_t fpbits) const + { + return Decode(ComputeMaxElements(GetBits(), GetCapacity(), fpbits)); + } +#endif // __cplusplus >= 201703L +}; +#endif // __cplusplus >= 201103L +#endif // __cplusplus + +#endif // _MINISKETCH_H_ diff --git a/sources.mk b/sources.mk new file mode 100644 index 0000000000..386a4fcc23 --- /dev/null +++ b/sources.mk @@ -0,0 +1,58 @@ +# - All variables are namespaced with MINISKETCH_ to avoid colliding with +# downstream makefiles. +# - All Variables ending in _HEADERS or _SOURCES confuse automake, so the +# _INT postfix is applied. +# - Convenience variables, for example a MINISKETCH_FIELDS_DIR should not be used +# as they interfere with automatic dependency generation +# - The %reldir% is the relative path from the Makefile.am. This allows +# downstreams to use these variables without having to manually account for +# the path change. + +MINISKETCH_INCLUDE_DIR_INT = %reldir%/include + +MINISKETCH_DIST_HEADERS_INT = +MINISKETCH_DIST_HEADERS_INT += %reldir%/include/minisketch.h + +MINISKETCH_LIB_HEADERS_INT = +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/false_positives.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/fielddefines.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/int_utils.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/lintrans.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/sketch.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/sketch_impl.h +MINISKETCH_LIB_HEADERS_INT += %reldir%/src/util.h + +MINISKETCH_LIB_SOURCES_INT = +MINISKETCH_LIB_SOURCES_INT += %reldir%/src/minisketch.cpp + +MINISKETCH_FIELD_GENERIC_HEADERS_INT = +MINISKETCH_FIELD_GENERIC_HEADERS_INT += %reldir%/src/fields/generic_common_impl.h + +MINISKETCH_FIELD_GENERIC_SOURCES_INT = +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_1byte.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_2bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_3bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_4bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_5bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_6bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_7bytes.cpp +MINISKETCH_FIELD_GENERIC_SOURCES_INT += %reldir%/src/fields/generic_8bytes.cpp + +MINISKETCH_FIELD_CLMUL_HEADERS_INT = +MINISKETCH_FIELD_CLMUL_HEADERS_INT += %reldir%/src/fields/clmul_common_impl.h + +MINISKETCH_FIELD_CLMUL_SOURCES_INT = +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_1byte.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_2bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_3bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_4bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_5bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_6bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_7bytes.cpp +MINISKETCH_FIELD_CLMUL_SOURCES_INT += %reldir%/src/fields/clmul_8bytes.cpp + +MINISKETCH_BENCH_SOURCES_INT = +MINISKETCH_BENCH_SOURCES_INT += %reldir%/src/bench.cpp + +MINISKETCH_TEST_SOURCES_INT = +MINISKETCH_TEST_SOURCES_INT += %reldir%/src/test.cpp diff --git a/src/bench.cpp b/src/bench.cpp new file mode 100644 index 0000000000..f55944a448 --- /dev/null +++ b/src/bench.cpp @@ -0,0 +1,122 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "../include/minisketch.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + if (argc < 1 || argc > 4) { + printf("Usage: %s [syndromes=150] [errors=syndromes] [iters=10]\n", argv[0]); + return 1; + } + int syndromes = argc > 1 ? strtoul(argv[1], NULL, 10) : 150; + int errors = argc > 2 ? strtoul(argv[2], NULL, 10) : syndromes; + int iters = argc > 3 ? strtoul(argv[3], NULL, 10) : 10; + if (syndromes < 0 || syndromes > 1000000) { + printf("Number of syndromes (%i) out of range 0..1000000\n", syndromes); + return 1; + } + if (errors < 0) { + printf("Number of errors (%i) is negative(%i)\n", errors, syndromes); + return 1; + } + if (iters < 0 || iters > 1000000000) { + printf("Number of iterations (%i) out of range 0..1000000000\n", iters); + return 1; + } + uint32_t max_impl = minisketch_implementation_max(); + for (int bits = 2; bits <= 64; ++bits) { + if (errors > pow(2.0, bits - 1)) continue; + if (!minisketch_bits_supported(bits)) continue; + printf("recover[ms]\t% 3i\t", bits); + for (uint32_t impl = 0; impl <= max_impl; ++impl) { + std::vector states; + std::vector roots(2 * syndromes); + std::random_device rng; + std::uniform_int_distribution dist(1, (uint64_t(1) << bits) - 1); + states.resize(iters); + std::vector benches; + benches.reserve(iters); + for (int i = 0; i < iters; ++i) { + states[i] = minisketch_create(bits, impl, syndromes); + if (!states[i]) break; + std::set done; + for (int j = 0; j < errors; ++j) { + uint64_t r; + do { + r = dist(rng); + } while (done.count(r)); + done.insert(r); + minisketch_add_uint64(states[i], r); + } + } + if (!states[0]) { + printf(" -\t"); + } else { + double total = 0.0; + for (auto& state : states) { + auto start = std::chrono::steady_clock::now(); + minisketch_decode(state, 2 * syndromes, roots.data()); + auto stop = std::chrono::steady_clock::now(); + std::chrono::duration dur(stop - start); + total += dur.count(); + benches.push_back(dur.count()); + } + std::sort(benches.begin(), benches.end()); + printf("% 10.5f\t", benches[0] * 1000.0); + } + for (auto& state : states) { + minisketch_destroy(state); + } + } + printf("\n"); + printf("create[ns]\t% 3i\t", bits); + for (uint32_t impl = 0; impl <= max_impl; ++impl) { + std::vector states; + std::random_device rng; + std::uniform_int_distribution dist; + std::vector data; + data.resize(errors * 10); + states.resize(iters); + std::vector benches; + benches.reserve(iters); + for (int i = 0; i < iters; ++i) { + states[i] = minisketch_create(bits, impl, syndromes); + } + for (size_t i = 0; i < data.size(); ++i) { + data[i] = dist(rng); + } + if (!states[0]) { + printf(" -\t"); + } else { + double total = 0.0; + for (auto& state : states) { + auto start = std::chrono::steady_clock::now(); + for (auto val : data) { + minisketch_add_uint64(state, val); + } + auto stop = std::chrono::steady_clock::now(); + std::chrono::duration dur(stop - start); + total += dur.count(); + benches.push_back(dur.count()); + } + std::sort(benches.begin(), benches.end()); + printf("% 10.5f\t", benches[0] * 1000000000.0 / data.size() / syndromes); + } + for (auto& state : states) { + minisketch_destroy(state); + } + } + printf("\n"); + } + return 0; +} diff --git a/src/false_positives.h b/src/false_positives.h new file mode 100644 index 0000000000..44ebb3e94c --- /dev/null +++ b/src/false_positives.h @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2020 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_FALSE_POSITIVES_H_ +#define _MINISKETCH_FALSE_POSITIVES_H_ + +#include "util.h" + +#include "int_utils.h" + +#include + +namespace { + +/** Compute floor(log2(x!)), exactly up to x=57; an underestimate up to x=2^32-1. */ +uint64_t Log2Factorial(uint32_t x) { + //! Values of floor(106*log2(1 + i/32)) for i=0..31 + static constexpr uint8_t T[32] = { + 0, 4, 9, 13, 18, 22, 26, 30, 34, 37, 41, 45, 48, 52, 55, 58, 62, 65, 68, + 71, 74, 77, 80, 82, 85, 88, 90, 93, 96, 98, 101, 103 + }; + int bits = CountBits(x, 32); + // Compute an (under)estimate of floor(106*log2(x)). + // This works by relying on floor(log2(x)) = countbits(x)-1, and adding + // precision using the top 6 bits of x (the highest one of which is always + // one). + unsigned l2_106 = 106 * (bits - 1) + T[((x << (32 - bits)) >> 26) & 31]; + // Based on Stirling approximation for log2(x!): + // log2(x!) = log(x!) / log(2) + // = ((x + 1/2) * log(x) - x + log(2*pi)/2 + ...) / log(2) + // = (x + 1/2) * log2(x) - x/log(2) + log2(2*pi)/2 + ... + // = 1/2*(2*x+1)*log2(x) - (1/log(2))*x + log2(2*pi)/2 + ... + // = 1/212*(2*x+1)*(106*log2(x)) + (-1/log(2))*x + log2(2*pi)/2 + ... + // where 418079/88632748 is exactly 1/212 + // -127870026/88632748 is slightly less than -1/log(2) + // 117504694/88632748 is less than log2(2*pi)/2 + // A correction term is only needed for x < 3. + // + // See doc/log2_factorial.sage for how these constants were obtained. + return (418079 * (2 * uint64_t{x} + 1) * l2_106 - 127870026 * uint64_t{x} + 117504694 + 88632748 * (x < 3)) / 88632748; +} + +/** Compute floor(log2(2^(bits * capacity) / sum((2^bits - 1) choose k, k=0..capacity))), for bits>1 + * + * See doc/gen_basefpbits.sage for how the tables were obtained. */ +uint64_t BaseFPBits(uint32_t bits, uint32_t capacity) { + // Correction table for low bits/capacities + static constexpr uint8_t ADD5[] = {1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12}; + static constexpr uint8_t ADD6[] = {1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 5, 6, 6, 6, 7, 8, 8, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 23, 24, 24, 24, 24}; + static constexpr uint8_t ADD7[] = {1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 7, 8, 9, 9, 9, 10, 11, 11, 12, 12, 13, 13, 15, 15, 15, 16, 17, 17, 18, 19, 20, 20}; + static constexpr uint8_t ADD8[] = {1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9}; + static constexpr uint8_t ADD9[] = {1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 3, 2, 3, 3, 3, 3, 4, 3, 3, 4, 4, 4, 4}; + + if (capacity == 0) return 0; + uint64_t ret = 0; + if (bits < 32 && capacity >= (1U << bits)) { + ret = uint64_t{bits} * (capacity - (1U << bits) + 1); + capacity = (1U << bits) - 1; + } + ret += Log2Factorial(capacity); + switch (bits) { + case 2: return ret + (capacity <= 2 ? 0 : 1); + case 3: return ret + (capacity <= 2 ? 0 : (0x2a5 >> 2 * (capacity - 3)) & 3); + case 4: return ret + (capacity <= 3 ? 0 : (0xb6d91a449 >> 3 * (capacity - 4)) & 7); + case 5: return ret + (capacity <= 4 ? 0 : ADD5[capacity - 5]); + case 6: return ret + (capacity <= 4 ? 0 : capacity > 54 ? 25 : ADD6[capacity - 5]); + case 7: return ret + (capacity <= 4 ? 0 : capacity > 57 ? 21 : ADD7[capacity - 5]); + case 8: return ret + (capacity <= 9 ? 0 : capacity > 56 ? 10 : ADD8[capacity - 10]); + case 9: return ret + (capacity <= 11 ? 0 : capacity > 54 ? 5 : ADD9[capacity - 12]); + case 10: return ret + (capacity <= 21 ? 0 : capacity > 50 ? 2 : (0x1a6665545555041 >> 2 * (capacity - 22)) & 3); + case 11: return ret + (capacity <= 21 ? 0 : capacity > 45 ? 1 : (0x5b3dc1 >> (capacity - 22)) & 1); + case 12: return ret + (capacity <= 21 ? 0 : capacity > 57 ? 0 : (0xe65522041 >> (capacity - 22)) & 1); + case 13: return ret + (capacity <= 27 ? 0 : capacity > 55 ? 0 : (0x8904081 >> (capacity - 28)) & 1); + case 14: return ret + (capacity <= 47 ? 0 : capacity > 48 ? 0 : 1); + default: return ret; + } +} + +size_t ComputeCapacity(uint32_t bits, size_t max_elements, uint32_t fpbits) { + if (bits == 0) return 0; + uint64_t base_fpbits = BaseFPBits(bits, max_elements); + // The fpbits provided by the base max_elements==capacity case are sufficient. + if (base_fpbits >= fpbits) return max_elements; + // Otherwise, increment capacity by ceil(fpbits / bits) beyond that. + return max_elements + (fpbits - base_fpbits + bits - 1) / bits; +} + +size_t ComputeMaxElements(uint32_t bits, size_t capacity, uint32_t fpbits) { + if (bits == 0) return 0; + // Start with max_elements=capacity, and decrease max_elements until the corresponding capacity is capacity. + size_t max_elements = capacity; + while (true) { + size_t capacity_for_max_elements = ComputeCapacity(bits, max_elements, fpbits); + CHECK_SAFE(capacity_for_max_elements >= capacity); + if (capacity_for_max_elements <= capacity) return max_elements; + size_t adjust = capacity_for_max_elements - capacity; + // Decrementing max_elements by N will at most decrement the corresponding capacity by N. + // As the observed capacity is adjust too high, we can safely decrease max_elements by adjust. + // If that brings us into negative max_elements territory, no solution exists and we return 0. + if (max_elements < adjust) return 0; + max_elements -= adjust; + } +} + +} // namespace + +#endif diff --git a/src/fielddefines.h b/src/fielddefines.h new file mode 100644 index 0000000000..510cb81f42 --- /dev/null +++ b/src/fielddefines.h @@ -0,0 +1,560 @@ +/********************************************************************** + * Copyright (c) 2021 Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_FIELDDEFINES_H_ +#define _MINISKETCH_FIELDDEFINES_H_ + +/* + +This file translates external defines ENABLE_FIELD_FOO, DISABLE_FIELD_FOO, and +DISABLE_DEFAULT_FIELDS to internal ones: ENABLE_FIELD_INT_FOO. Only the +resulting internal includes should be used. + +Default: All fields enabled +-DDISABLE_FIELD_3: All fields except 3 are enabled +-DENABLE_FIELD_3: All fields enabled +-DDISABLE_DEFAULT_FIELDS: Error, no fields enabled +-DDISABLE_DEFAULT_FIELDS -DENABLE_FIELD_3: Only field 3 enabled +-DDISABLE_DEFAULT_FIELDS -DENABLE_FIELD_2 -DENABLE_FIELD_3: Only fields 2 and 3 are enabled +-DDISABLE_DEFAULT_FIELDS -DENABLE_FIELD_2 -DENABLE_FIELD_3 -DDISABLE_FIELD_3: Only field 2 enabled + +*/ + +#ifdef DISABLE_DEFAULT_FIELDS +#if defined(ENABLE_FIELD_2) && !defined(DISABLE_FIELD_2) +#define ENABLE_FIELD_INT_2 +#endif +#if defined(ENABLE_FIELD_3) && !defined(DISABLE_FIELD_3) +#define ENABLE_FIELD_INT_3 +#endif +#if defined(ENABLE_FIELD_4) && !defined(DISABLE_FIELD_4) +#define ENABLE_FIELD_INT_4 +#endif +#if defined(ENABLE_FIELD_5) && !defined(DISABLE_FIELD_5) +#define ENABLE_FIELD_INT_5 +#endif +#if defined(ENABLE_FIELD_6) && !defined(DISABLE_FIELD_6) +#define ENABLE_FIELD_INT_6 +#endif +#if defined(ENABLE_FIELD_7) && !defined(DISABLE_FIELD_7) +#define ENABLE_FIELD_INT_7 +#endif +#if defined(ENABLE_FIELD_8) && !defined(DISABLE_FIELD_8) +#define ENABLE_FIELD_INT_8 +#endif +#if defined(ENABLE_FIELD_9) && !defined(DISABLE_FIELD_9) +#define ENABLE_FIELD_INT_9 +#endif +#if defined(ENABLE_FIELD_10) && !defined(DISABLE_FIELD_10) +#define ENABLE_FIELD_INT_10 +#endif +#if defined(ENABLE_FIELD_11) && !defined(DISABLE_FIELD_11) +#define ENABLE_FIELD_INT_11 +#endif +#if defined(ENABLE_FIELD_12) && !defined(DISABLE_FIELD_12) +#define ENABLE_FIELD_INT_12 +#endif +#if defined(ENABLE_FIELD_13) && !defined(DISABLE_FIELD_13) +#define ENABLE_FIELD_INT_13 +#endif +#if defined(ENABLE_FIELD_14) && !defined(DISABLE_FIELD_14) +#define ENABLE_FIELD_INT_14 +#endif +#if defined(ENABLE_FIELD_15) && !defined(DISABLE_FIELD_15) +#define ENABLE_FIELD_INT_15 +#endif +#if defined(ENABLE_FIELD_16) && !defined(DISABLE_FIELD_16) +#define ENABLE_FIELD_INT_16 +#endif +#if defined(ENABLE_FIELD_17) && !defined(DISABLE_FIELD_17) +#define ENABLE_FIELD_INT_17 +#endif +#if defined(ENABLE_FIELD_18) && !defined(DISABLE_FIELD_18) +#define ENABLE_FIELD_INT_18 +#endif +#if defined(ENABLE_FIELD_19) && !defined(DISABLE_FIELD_19) +#define ENABLE_FIELD_INT_19 +#endif +#if defined(ENABLE_FIELD_20) && !defined(DISABLE_FIELD_20) +#define ENABLE_FIELD_INT_20 +#endif +#if defined(ENABLE_FIELD_21) && !defined(DISABLE_FIELD_21) +#define ENABLE_FIELD_INT_21 +#endif +#if defined(ENABLE_FIELD_22) && !defined(DISABLE_FIELD_22) +#define ENABLE_FIELD_INT_22 +#endif +#if defined(ENABLE_FIELD_23) && !defined(DISABLE_FIELD_23) +#define ENABLE_FIELD_INT_23 +#endif +#if defined(ENABLE_FIELD_24) && !defined(DISABLE_FIELD_24) +#define ENABLE_FIELD_INT_24 +#endif +#if defined(ENABLE_FIELD_25) && !defined(DISABLE_FIELD_25) +#define ENABLE_FIELD_INT_25 +#endif +#if defined(ENABLE_FIELD_26) && !defined(DISABLE_FIELD_26) +#define ENABLE_FIELD_INT_26 +#endif +#if defined(ENABLE_FIELD_27) && !defined(DISABLE_FIELD_27) +#define ENABLE_FIELD_INT_27 +#endif +#if defined(ENABLE_FIELD_28) && !defined(DISABLE_FIELD_28) +#define ENABLE_FIELD_INT_28 +#endif +#if defined(ENABLE_FIELD_29) && !defined(DISABLE_FIELD_29) +#define ENABLE_FIELD_INT_29 +#endif +#if defined(ENABLE_FIELD_30) && !defined(DISABLE_FIELD_30) +#define ENABLE_FIELD_INT_30 +#endif +#if defined(ENABLE_FIELD_31) && !defined(DISABLE_FIELD_31) +#define ENABLE_FIELD_INT_31 +#endif +#if defined(ENABLE_FIELD_32) && !defined(DISABLE_FIELD_32) +#define ENABLE_FIELD_INT_32 +#endif +#if defined(ENABLE_FIELD_33) && !defined(DISABLE_FIELD_33) +#define ENABLE_FIELD_INT_33 +#endif +#if defined(ENABLE_FIELD_34) && !defined(DISABLE_FIELD_34) +#define ENABLE_FIELD_INT_34 +#endif +#if defined(ENABLE_FIELD_35) && !defined(DISABLE_FIELD_35) +#define ENABLE_FIELD_INT_35 +#endif +#if defined(ENABLE_FIELD_36) && !defined(DISABLE_FIELD_36) +#define ENABLE_FIELD_INT_36 +#endif +#if defined(ENABLE_FIELD_37) && !defined(DISABLE_FIELD_37) +#define ENABLE_FIELD_INT_37 +#endif +#if defined(ENABLE_FIELD_38) && !defined(DISABLE_FIELD_38) +#define ENABLE_FIELD_INT_38 +#endif +#if defined(ENABLE_FIELD_39) && !defined(DISABLE_FIELD_39) +#define ENABLE_FIELD_INT_39 +#endif +#if defined(ENABLE_FIELD_40) && !defined(DISABLE_FIELD_40) +#define ENABLE_FIELD_INT_40 +#endif +#if defined(ENABLE_FIELD_41) && !defined(DISABLE_FIELD_41) +#define ENABLE_FIELD_INT_41 +#endif +#if defined(ENABLE_FIELD_42) && !defined(DISABLE_FIELD_42) +#define ENABLE_FIELD_INT_42 +#endif +#if defined(ENABLE_FIELD_43) && !defined(DISABLE_FIELD_43) +#define ENABLE_FIELD_INT_43 +#endif +#if defined(ENABLE_FIELD_44) && !defined(DISABLE_FIELD_44) +#define ENABLE_FIELD_INT_44 +#endif +#if defined(ENABLE_FIELD_45) && !defined(DISABLE_FIELD_45) +#define ENABLE_FIELD_INT_45 +#endif +#if defined(ENABLE_FIELD_46) && !defined(DISABLE_FIELD_46) +#define ENABLE_FIELD_INT_46 +#endif +#if defined(ENABLE_FIELD_47) && !defined(DISABLE_FIELD_47) +#define ENABLE_FIELD_INT_47 +#endif +#if defined(ENABLE_FIELD_48) && !defined(DISABLE_FIELD_48) +#define ENABLE_FIELD_INT_48 +#endif +#if defined(ENABLE_FIELD_49) && !defined(DISABLE_FIELD_49) +#define ENABLE_FIELD_INT_49 +#endif +#if defined(ENABLE_FIELD_50) && !defined(DISABLE_FIELD_50) +#define ENABLE_FIELD_INT_50 +#endif +#if defined(ENABLE_FIELD_51) && !defined(DISABLE_FIELD_51) +#define ENABLE_FIELD_INT_51 +#endif +#if defined(ENABLE_FIELD_52) && !defined(DISABLE_FIELD_52) +#define ENABLE_FIELD_INT_52 +#endif +#if defined(ENABLE_FIELD_53) && !defined(DISABLE_FIELD_53) +#define ENABLE_FIELD_INT_53 +#endif +#if defined(ENABLE_FIELD_54) && !defined(DISABLE_FIELD_54) +#define ENABLE_FIELD_INT_54 +#endif +#if defined(ENABLE_FIELD_55) && !defined(DISABLE_FIELD_55) +#define ENABLE_FIELD_INT_55 +#endif +#if defined(ENABLE_FIELD_56) && !defined(DISABLE_FIELD_56) +#define ENABLE_FIELD_INT_56 +#endif +#if defined(ENABLE_FIELD_57) && !defined(DISABLE_FIELD_57) +#define ENABLE_FIELD_INT_57 +#endif +#if defined(ENABLE_FIELD_58) && !defined(DISABLE_FIELD_58) +#define ENABLE_FIELD_INT_58 +#endif +#if defined(ENABLE_FIELD_59) && !defined(DISABLE_FIELD_59) +#define ENABLE_FIELD_INT_59 +#endif +#if defined(ENABLE_FIELD_60) && !defined(DISABLE_FIELD_60) +#define ENABLE_FIELD_INT_60 +#endif +#if defined(ENABLE_FIELD_61) && !defined(DISABLE_FIELD_61) +#define ENABLE_FIELD_INT_61 +#endif +#if defined(ENABLE_FIELD_62) && !defined(DISABLE_FIELD_62) +#define ENABLE_FIELD_INT_62 +#endif +#if defined(ENABLE_FIELD_63) && !defined(DISABLE_FIELD_63) +#define ENABLE_FIELD_INT_63 +#endif +#if defined(ENABLE_FIELD_64) && !defined(DISABLE_FIELD_64) +#define ENABLE_FIELD_INT_64 +#endif +#else +#if !defined(DISABLE_FIELD_2) +#define ENABLE_FIELD_INT_2 +#endif +#if !defined(DISABLE_FIELD_3) +#define ENABLE_FIELD_INT_3 +#endif +#if !defined(DISABLE_FIELD_4) +#define ENABLE_FIELD_INT_4 +#endif +#if !defined(DISABLE_FIELD_5) +#define ENABLE_FIELD_INT_5 +#endif +#if !defined(DISABLE_FIELD_6) +#define ENABLE_FIELD_INT_6 +#endif +#if !defined(DISABLE_FIELD_7) +#define ENABLE_FIELD_INT_7 +#endif +#if !defined(DISABLE_FIELD_8) +#define ENABLE_FIELD_INT_8 +#endif +#if !defined(DISABLE_FIELD_9) +#define ENABLE_FIELD_INT_9 +#endif +#if !defined(DISABLE_FIELD_10) +#define ENABLE_FIELD_INT_10 +#endif +#if !defined(DISABLE_FIELD_11) +#define ENABLE_FIELD_INT_11 +#endif +#if !defined(DISABLE_FIELD_12) +#define ENABLE_FIELD_INT_12 +#endif +#if !defined(DISABLE_FIELD_13) +#define ENABLE_FIELD_INT_13 +#endif +#if !defined(DISABLE_FIELD_14) +#define ENABLE_FIELD_INT_14 +#endif +#if !defined(DISABLE_FIELD_15) +#define ENABLE_FIELD_INT_15 +#endif +#if !defined(DISABLE_FIELD_16) +#define ENABLE_FIELD_INT_16 +#endif +#if !defined(DISABLE_FIELD_17) +#define ENABLE_FIELD_INT_17 +#endif +#if !defined(DISABLE_FIELD_18) +#define ENABLE_FIELD_INT_18 +#endif +#if !defined(DISABLE_FIELD_19) +#define ENABLE_FIELD_INT_19 +#endif +#if !defined(DISABLE_FIELD_20) +#define ENABLE_FIELD_INT_20 +#endif +#if !defined(DISABLE_FIELD_21) +#define ENABLE_FIELD_INT_21 +#endif +#if !defined(DISABLE_FIELD_22) +#define ENABLE_FIELD_INT_22 +#endif +#if !defined(DISABLE_FIELD_23) +#define ENABLE_FIELD_INT_23 +#endif +#if !defined(DISABLE_FIELD_24) +#define ENABLE_FIELD_INT_24 +#endif +#if !defined(DISABLE_FIELD_25) +#define ENABLE_FIELD_INT_25 +#endif +#if !defined(DISABLE_FIELD_26) +#define ENABLE_FIELD_INT_26 +#endif +#if !defined(DISABLE_FIELD_27) +#define ENABLE_FIELD_INT_27 +#endif +#if !defined(DISABLE_FIELD_28) +#define ENABLE_FIELD_INT_28 +#endif +#if !defined(DISABLE_FIELD_29) +#define ENABLE_FIELD_INT_29 +#endif +#if !defined(DISABLE_FIELD_30) +#define ENABLE_FIELD_INT_30 +#endif +#if !defined(DISABLE_FIELD_31) +#define ENABLE_FIELD_INT_31 +#endif +#if !defined(DISABLE_FIELD_32) +#define ENABLE_FIELD_INT_32 +#endif +#if !defined(DISABLE_FIELD_33) +#define ENABLE_FIELD_INT_33 +#endif +#if !defined(DISABLE_FIELD_34) +#define ENABLE_FIELD_INT_34 +#endif +#if !defined(DISABLE_FIELD_35) +#define ENABLE_FIELD_INT_35 +#endif +#if !defined(DISABLE_FIELD_36) +#define ENABLE_FIELD_INT_36 +#endif +#if !defined(DISABLE_FIELD_37) +#define ENABLE_FIELD_INT_37 +#endif +#if !defined(DISABLE_FIELD_38) +#define ENABLE_FIELD_INT_38 +#endif +#if !defined(DISABLE_FIELD_39) +#define ENABLE_FIELD_INT_39 +#endif +#if !defined(DISABLE_FIELD_40) +#define ENABLE_FIELD_INT_40 +#endif +#if !defined(DISABLE_FIELD_41) +#define ENABLE_FIELD_INT_41 +#endif +#if !defined(DISABLE_FIELD_42) +#define ENABLE_FIELD_INT_42 +#endif +#if !defined(DISABLE_FIELD_43) +#define ENABLE_FIELD_INT_43 +#endif +#if !defined(DISABLE_FIELD_44) +#define ENABLE_FIELD_INT_44 +#endif +#if !defined(DISABLE_FIELD_45) +#define ENABLE_FIELD_INT_45 +#endif +#if !defined(DISABLE_FIELD_46) +#define ENABLE_FIELD_INT_46 +#endif +#if !defined(DISABLE_FIELD_47) +#define ENABLE_FIELD_INT_47 +#endif +#if !defined(DISABLE_FIELD_48) +#define ENABLE_FIELD_INT_48 +#endif +#if !defined(DISABLE_FIELD_49) +#define ENABLE_FIELD_INT_49 +#endif +#if !defined(DISABLE_FIELD_50) +#define ENABLE_FIELD_INT_50 +#endif +#if !defined(DISABLE_FIELD_51) +#define ENABLE_FIELD_INT_51 +#endif +#if !defined(DISABLE_FIELD_52) +#define ENABLE_FIELD_INT_52 +#endif +#if !defined(DISABLE_FIELD_53) +#define ENABLE_FIELD_INT_53 +#endif +#if !defined(DISABLE_FIELD_54) +#define ENABLE_FIELD_INT_54 +#endif +#if !defined(DISABLE_FIELD_55) +#define ENABLE_FIELD_INT_55 +#endif +#if !defined(DISABLE_FIELD_56) +#define ENABLE_FIELD_INT_56 +#endif +#if !defined(DISABLE_FIELD_57) +#define ENABLE_FIELD_INT_57 +#endif +#if !defined(DISABLE_FIELD_58) +#define ENABLE_FIELD_INT_58 +#endif +#if !defined(DISABLE_FIELD_59) +#define ENABLE_FIELD_INT_59 +#endif +#if !defined(DISABLE_FIELD_60) +#define ENABLE_FIELD_INT_60 +#endif +#if !defined(DISABLE_FIELD_61) +#define ENABLE_FIELD_INT_61 +#endif +#if !defined(DISABLE_FIELD_62) +#define ENABLE_FIELD_INT_62 +#endif +#if !defined(DISABLE_FIELD_63) +#define ENABLE_FIELD_INT_63 +#endif +#if !defined(DISABLE_FIELD_64) +#define ENABLE_FIELD_INT_64 +#endif +#endif + +#if !defined(ENABLE_FIELD_INT_2) && \ + !defined(ENABLE_FIELD_INT_3) && \ + !defined(ENABLE_FIELD_INT_4) && \ + !defined(ENABLE_FIELD_INT_5) && \ + !defined(ENABLE_FIELD_INT_6) && \ + !defined(ENABLE_FIELD_INT_7) && \ + !defined(ENABLE_FIELD_INT_8) && \ + !defined(ENABLE_FIELD_INT_9) && \ + !defined(ENABLE_FIELD_INT_10) && \ + !defined(ENABLE_FIELD_INT_11) && \ + !defined(ENABLE_FIELD_INT_12) && \ + !defined(ENABLE_FIELD_INT_13) && \ + !defined(ENABLE_FIELD_INT_14) && \ + !defined(ENABLE_FIELD_INT_15) && \ + !defined(ENABLE_FIELD_INT_16) && \ + !defined(ENABLE_FIELD_INT_17) && \ + !defined(ENABLE_FIELD_INT_18) && \ + !defined(ENABLE_FIELD_INT_19) && \ + !defined(ENABLE_FIELD_INT_20) && \ + !defined(ENABLE_FIELD_INT_21) && \ + !defined(ENABLE_FIELD_INT_22) && \ + !defined(ENABLE_FIELD_INT_23) && \ + !defined(ENABLE_FIELD_INT_24) && \ + !defined(ENABLE_FIELD_INT_25) && \ + !defined(ENABLE_FIELD_INT_26) && \ + !defined(ENABLE_FIELD_INT_27) && \ + !defined(ENABLE_FIELD_INT_28) && \ + !defined(ENABLE_FIELD_INT_29) && \ + !defined(ENABLE_FIELD_INT_30) && \ + !defined(ENABLE_FIELD_INT_31) && \ + !defined(ENABLE_FIELD_INT_32) && \ + !defined(ENABLE_FIELD_INT_33) && \ + !defined(ENABLE_FIELD_INT_34) && \ + !defined(ENABLE_FIELD_INT_35) && \ + !defined(ENABLE_FIELD_INT_36) && \ + !defined(ENABLE_FIELD_INT_37) && \ + !defined(ENABLE_FIELD_INT_38) && \ + !defined(ENABLE_FIELD_INT_39) && \ + !defined(ENABLE_FIELD_INT_40) && \ + !defined(ENABLE_FIELD_INT_41) && \ + !defined(ENABLE_FIELD_INT_42) && \ + !defined(ENABLE_FIELD_INT_43) && \ + !defined(ENABLE_FIELD_INT_44) && \ + !defined(ENABLE_FIELD_INT_45) && \ + !defined(ENABLE_FIELD_INT_46) && \ + !defined(ENABLE_FIELD_INT_47) && \ + !defined(ENABLE_FIELD_INT_48) && \ + !defined(ENABLE_FIELD_INT_49) && \ + !defined(ENABLE_FIELD_INT_50) && \ + !defined(ENABLE_FIELD_INT_51) && \ + !defined(ENABLE_FIELD_INT_52) && \ + !defined(ENABLE_FIELD_INT_53) && \ + !defined(ENABLE_FIELD_INT_54) && \ + !defined(ENABLE_FIELD_INT_55) && \ + !defined(ENABLE_FIELD_INT_56) && \ + !defined(ENABLE_FIELD_INT_57) && \ + !defined(ENABLE_FIELD_INT_58) && \ + !defined(ENABLE_FIELD_INT_59) && \ + !defined(ENABLE_FIELD_INT_60) && \ + !defined(ENABLE_FIELD_INT_61) && \ + !defined(ENABLE_FIELD_INT_62) && \ + !defined(ENABLE_FIELD_INT_63) && \ + !defined(ENABLE_FIELD_INT_64) +#error No fields enabled +#endif + +#if defined(ENABLE_FIELD_INT_2) || \ + defined(ENABLE_FIELD_INT_3) || \ + defined(ENABLE_FIELD_INT_4) || \ + defined(ENABLE_FIELD_INT_5) || \ + defined(ENABLE_FIELD_INT_6) || \ + defined(ENABLE_FIELD_INT_7) || \ + defined(ENABLE_FIELD_INT_8) +#define ENABLE_FIELD_BYTES_INT_1 +#endif + +#if defined(ENABLE_FIELD_INT_9) || \ + defined(ENABLE_FIELD_INT_10) || \ + defined(ENABLE_FIELD_INT_11) || \ + defined(ENABLE_FIELD_INT_12) || \ + defined(ENABLE_FIELD_INT_13) || \ + defined(ENABLE_FIELD_INT_14) || \ + defined(ENABLE_FIELD_INT_15) || \ + defined(ENABLE_FIELD_INT_16) +#define ENABLE_FIELD_BYTES_INT_2 +#endif + +#if defined(ENABLE_FIELD_INT_17) || \ + defined(ENABLE_FIELD_INT_18) || \ + defined(ENABLE_FIELD_INT_19) || \ + defined(ENABLE_FIELD_INT_20) || \ + defined(ENABLE_FIELD_INT_21) || \ + defined(ENABLE_FIELD_INT_22) || \ + defined(ENABLE_FIELD_INT_23) || \ + defined(ENABLE_FIELD_INT_24) +#define ENABLE_FIELD_BYTES_INT_3 +#endif + +#if defined(ENABLE_FIELD_INT_25) || \ + defined(ENABLE_FIELD_INT_26) || \ + defined(ENABLE_FIELD_INT_27) || \ + defined(ENABLE_FIELD_INT_28) || \ + defined(ENABLE_FIELD_INT_29) || \ + defined(ENABLE_FIELD_INT_30) || \ + defined(ENABLE_FIELD_INT_31) || \ + defined(ENABLE_FIELD_INT_32) +#define ENABLE_FIELD_BYTES_INT_4 +#endif + +#if defined(ENABLE_FIELD_INT_33) || \ + defined(ENABLE_FIELD_INT_34) || \ + defined(ENABLE_FIELD_INT_35) || \ + defined(ENABLE_FIELD_INT_36) || \ + defined(ENABLE_FIELD_INT_37) || \ + defined(ENABLE_FIELD_INT_38) || \ + defined(ENABLE_FIELD_INT_39) || \ + defined(ENABLE_FIELD_INT_40) +#define ENABLE_FIELD_BYTES_INT_5 +#endif + +#if defined(ENABLE_FIELD_INT_41) || \ + defined(ENABLE_FIELD_INT_42) || \ + defined(ENABLE_FIELD_INT_43) || \ + defined(ENABLE_FIELD_INT_44) || \ + defined(ENABLE_FIELD_INT_45) || \ + defined(ENABLE_FIELD_INT_46) || \ + defined(ENABLE_FIELD_INT_47) || \ + defined(ENABLE_FIELD_INT_48) +#define ENABLE_FIELD_BYTES_INT_6 +#endif + +#if defined(ENABLE_FIELD_INT_49) || \ + defined(ENABLE_FIELD_INT_50) || \ + defined(ENABLE_FIELD_INT_51) || \ + defined(ENABLE_FIELD_INT_52) || \ + defined(ENABLE_FIELD_INT_53) || \ + defined(ENABLE_FIELD_INT_54) || \ + defined(ENABLE_FIELD_INT_55) || \ + defined(ENABLE_FIELD_INT_56) +#define ENABLE_FIELD_BYTES_INT_7 +#endif + +#if defined(ENABLE_FIELD_INT_57) || \ + defined(ENABLE_FIELD_INT_58) || \ + defined(ENABLE_FIELD_INT_59) || \ + defined(ENABLE_FIELD_INT_60) || \ + defined(ENABLE_FIELD_INT_61) || \ + defined(ENABLE_FIELD_INT_62) || \ + defined(ENABLE_FIELD_INT_63) || \ + defined(ENABLE_FIELD_INT_64) +#define ENABLE_FIELD_BYTES_INT_8 +#endif +#endif // _MINISKETCH_FIELDDEFINES_H_ diff --git a/src/fields/clmul_1byte.cpp b/src/fields/clmul_1byte.cpp new file mode 100644 index 0000000000..8826af9605 --- /dev/null +++ b/src/fields/clmul_1byte.cpp @@ -0,0 +1,119 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_1) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_2 +// 2 bit field +typedef RecLinTrans StatTableTRI2; +constexpr StatTableTRI2 SQR_TABLE_TRI2({0x1, 0x3}); +constexpr StatTableTRI2 QRT_TABLE_TRI2({0x2, 0}); +typedef FieldTri FieldTri2; +#endif + +#ifdef ENABLE_FIELD_INT_3 +// 3 bit field +typedef RecLinTrans StatTableTRI3; +constexpr StatTableTRI3 SQR_TABLE_TRI3({0x1, 0x4, 0x6}); +constexpr StatTableTRI3 QRT_TABLE_TRI3({0, 0x4, 0x6}); +typedef FieldTri FieldTri3; +#endif + +#ifdef ENABLE_FIELD_INT_4 +// 4 bit field +typedef RecLinTrans StatTableTRI4; +constexpr StatTableTRI4 SQR_TABLE_TRI4({0x1, 0x4, 0x3, 0xc}); +constexpr StatTableTRI4 QRT_TABLE_TRI4({0x6, 0xa, 0x8, 0}); +typedef FieldTri FieldTri4; +#endif + +#ifdef ENABLE_FIELD_INT_5 +// 5 bit field +typedef RecLinTrans StatTable5; +constexpr StatTable5 SQR_TABLE_5({0x1, 0x4, 0x10, 0xa, 0xd}); +constexpr StatTable5 SQR2_TABLE_5({0x1, 0x10, 0xd, 0xe, 0x1b}); +constexpr StatTable5 QRT_TABLE_5({0x14, 0x8, 0xa, 0, 0xe}); +typedef Field Field5; +typedef FieldTri, &SQR_TABLE_5, &SQR2_TABLE_5, &QRT_TABLE_5, &QRT_TABLE_5, &QRT_TABLE_5, &QRT_TABLE_5, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri5; +#endif + +#ifdef ENABLE_FIELD_INT_6 +// 6 bit field +typedef RecLinTrans StatTableTRI6; +constexpr StatTableTRI6 SQR_TABLE_TRI6({0x1, 0x4, 0x10, 0x3, 0xc, 0x30}); +constexpr StatTableTRI6 SQR2_TABLE_TRI6({0x1, 0x10, 0xc, 0x5, 0x13, 0x3c}); +constexpr StatTableTRI6 QRT_TABLE_TRI6({0x3a, 0x26, 0x24, 0x14, 0x20, 0}); +typedef FieldTri FieldTri6; +#endif + +#ifdef ENABLE_FIELD_INT_7 +// 7 bit field +typedef RecLinTrans StatTableTRI7; +constexpr StatTableTRI7 SQR_TABLE_TRI7({0x1, 0x4, 0x10, 0x40, 0x6, 0x18, 0x60}); +constexpr StatTableTRI7 SQR2_TABLE_TRI7({0x1, 0x10, 0x6, 0x60, 0x14, 0x46, 0x78}); +constexpr StatTableTRI7 QRT_TABLE_TRI7({0, 0x14, 0x16, 0x72, 0x12, 0x40, 0x7a}); +typedef FieldTri FieldTri7; +#endif + +#ifdef ENABLE_FIELD_INT_8 +// 8 bit field +typedef RecLinTrans StatTable8; +constexpr StatTable8 SQR_TABLE_8({0x1, 0x4, 0x10, 0x40, 0x1b, 0x6c, 0xab, 0x9a}); +constexpr StatTable8 SQR2_TABLE_8({0x1, 0x10, 0x1b, 0xab, 0x5e, 0x97, 0xb3, 0xc5}); +constexpr StatTable8 QRT_TABLE_8({0xbc, 0x2a, 0x28, 0x86, 0x2c, 0xde, 0x8e, 0}); +typedef Field Field8; +#endif +} + +Sketch* ConstructClMul1Byte(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_5 + case 5: return new SketchImpl(implementation, 5); +#endif +#ifdef ENABLE_FIELD_INT_8 + case 8: return new SketchImpl(implementation, 8); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri1Byte(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_2 + case 2: return new SketchImpl(implementation, 2); +#endif +#ifdef ENABLE_FIELD_INT_3 + case 3: return new SketchImpl(implementation, 3); +#endif +#ifdef ENABLE_FIELD_INT_4 + case 4: return new SketchImpl(implementation, 4); +#endif +#ifdef ENABLE_FIELD_INT_5 + case 5: return new SketchImpl(implementation, 5); +#endif +#ifdef ENABLE_FIELD_INT_6 + case 6: return new SketchImpl(implementation, 6); +#endif +#ifdef ENABLE_FIELD_INT_7 + case 7: return new SketchImpl(implementation, 7); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_2bytes.cpp b/src/fields/clmul_2bytes.cpp new file mode 100644 index 0000000000..43930254dd --- /dev/null +++ b/src/fields/clmul_2bytes.cpp @@ -0,0 +1,154 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_2) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_9 +// 9 bit field +typedef RecLinTrans StatTableTRI9; +constexpr StatTableTRI9 SQR_TABLE_TRI9({0x1, 0x4, 0x10, 0x40, 0x100, 0x6, 0x18, 0x60, 0x180}); +constexpr StatTableTRI9 SQR2_TABLE_TRI9({0x1, 0x10, 0x100, 0x18, 0x180, 0x14, 0x140, 0x1e, 0x1e0}); +constexpr StatTableTRI9 SQR4_TABLE_TRI9({0x1, 0x180, 0x1e0, 0x198, 0x1fe, 0x80, 0xa0, 0x88, 0xaa}); +constexpr StatTableTRI9 QRT_TABLE_TRI9({0, 0x4e, 0x4c, 0x1aa, 0x48, 0x22, 0x1a2, 0x100, 0x58}); +typedef FieldTri FieldTri9; +#endif + +#ifdef ENABLE_FIELD_INT_10 +// 10 bit field +typedef RecLinTrans StatTable10; +constexpr StatTable10 SQR_TABLE_10({0x1, 0x4, 0x10, 0x40, 0x100, 0x9, 0x24, 0x90, 0x240, 0x112}); +constexpr StatTable10 SQR2_TABLE_10({0x1, 0x10, 0x100, 0x24, 0x240, 0x41, 0x19, 0x190, 0x136, 0x344}); +constexpr StatTable10 SQR4_TABLE_10({0x1, 0x240, 0x136, 0x141, 0x35d, 0x18, 0x265, 0x2e6, 0x227, 0x36b}); +constexpr StatTable10 QRT_TABLE_10({0xec, 0x86, 0x84, 0x30e, 0x80, 0x3c2, 0x306, 0, 0x90, 0x296}); +typedef Field Field10; +typedef FieldTri, &SQR_TABLE_10, &SQR2_TABLE_10, &SQR4_TABLE_10, &QRT_TABLE_10, &QRT_TABLE_10, &QRT_TABLE_10, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri10; +#endif + +#ifdef ENABLE_FIELD_INT_11 +// 11 bit field +typedef RecLinTrans StatTable11; +constexpr StatTable11 SQR_TABLE_11({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0xa, 0x28, 0xa0, 0x280, 0x205}); +constexpr StatTable11 SQR2_TABLE_11({0x1, 0x10, 0x100, 0xa, 0xa0, 0x205, 0x44, 0x440, 0x428, 0x2a8, 0x291}); +constexpr StatTable11 SQR4_TABLE_11({0x1, 0xa0, 0x428, 0x1a, 0x645, 0x3a9, 0x144, 0x2d5, 0x9e, 0x4e7, 0x649}); +constexpr StatTable11 QRT_TABLE_11({0x734, 0x48, 0x4a, 0x1de, 0x4e, 0x35e, 0x1d6, 0x200, 0x5e, 0, 0x37e}); +typedef Field Field11; +typedef FieldTri, &SQR_TABLE_11, &SQR2_TABLE_11, &SQR4_TABLE_11, &QRT_TABLE_11, &QRT_TABLE_11, &QRT_TABLE_11, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri11; +#endif + +#ifdef ENABLE_FIELD_INT_12 +// 12 bit field +typedef RecLinTrans StatTable12; +constexpr StatTable12 SQR_TABLE_12({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x9, 0x24, 0x90, 0x240, 0x900, 0x412}); +constexpr StatTable12 SQR2_TABLE_12({0x1, 0x10, 0x100, 0x9, 0x90, 0x900, 0x41, 0x410, 0x124, 0x249, 0x482, 0x804}); +constexpr StatTable12 SQR4_TABLE_12({0x1, 0x90, 0x124, 0x8, 0x480, 0x920, 0x40, 0x412, 0x924, 0x200, 0x82, 0x904}); +constexpr StatTable12 QRT_TABLE_12({0x48, 0xc10, 0xc12, 0x208, 0xc16, 0xd82, 0x200, 0x110, 0xc06, 0, 0xda2, 0x5a4}); +typedef Field Field12; +typedef FieldTri, &SQR_TABLE_12, &SQR2_TABLE_12, &SQR4_TABLE_12, &QRT_TABLE_12, &QRT_TABLE_12, &QRT_TABLE_12, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri12; +#endif + +#ifdef ENABLE_FIELD_INT_13 +// 13 bit field +typedef RecLinTrans StatTable13; +constexpr StatTable13 SQR_TABLE_13({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x36, 0xd8, 0x360, 0xd80, 0x161b, 0x185a}); +constexpr StatTable13 SQR2_TABLE_13({0x1, 0x10, 0x100, 0x1000, 0xd8, 0xd80, 0x185a, 0x514, 0x1176, 0x17b8, 0x1b75, 0x17ff, 0x1f05}); +constexpr StatTable13 SQR4_TABLE_13({0x1, 0xd8, 0x1176, 0x1f05, 0xd96, 0x18e8, 0x68, 0xbdb, 0x1a61, 0x1af2, 0x1a37, 0x3b9, 0x1440}); +constexpr StatTable13 QRT_TABLE_13({0xcfc, 0x1500, 0x1502, 0x382, 0x1506, 0x149c, 0x38a, 0x118, 0x1516, 0, 0x14bc, 0x100e, 0x3ca}); +typedef Field Field13; +#endif + +#ifdef ENABLE_FIELD_INT_14 +// 14 bit field +typedef RecLinTrans StatTable14; +constexpr StatTable14 SQR_TABLE_14({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x21, 0x84, 0x210, 0x840, 0x2100, 0x442, 0x1108}); +constexpr StatTable14 SQR2_TABLE_14({0x1, 0x10, 0x100, 0x1000, 0x84, 0x840, 0x442, 0x401, 0x31, 0x310, 0x3100, 0x118c, 0x1844, 0x486}); +constexpr StatTable14 SQR4_TABLE_14({0x1, 0x84, 0x31, 0x1844, 0x501, 0x15ce, 0x3552, 0x3101, 0x8c5, 0x3a5, 0x1cf3, 0xd74, 0xc8a, 0x3411}); +constexpr StatTable14 QRT_TABLE_14({0x13f2, 0x206, 0x204, 0x3e06, 0x200, 0x1266, 0x3e0e, 0x114, 0x210, 0, 0x1246, 0x2848, 0x3e4e, 0x2258}); +typedef Field Field14; +typedef FieldTri, &SQR_TABLE_14, &SQR2_TABLE_14, &SQR4_TABLE_14, &QRT_TABLE_14, &QRT_TABLE_14, &QRT_TABLE_14, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri14; +#endif + +#ifdef ENABLE_FIELD_INT_15 +// 15 bit field +typedef RecLinTrans StatTableTRI15; +constexpr StatTableTRI15 SQR_TABLE_TRI15({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x6, 0x18, 0x60, 0x180, 0x600, 0x1800, 0x6000}); +constexpr StatTableTRI15 SQR2_TABLE_TRI15({0x1, 0x10, 0x100, 0x1000, 0x6, 0x60, 0x600, 0x6000, 0x14, 0x140, 0x1400, 0x4006, 0x78, 0x780, 0x7800}); +constexpr StatTableTRI15 SQR4_TABLE_TRI15({0x1, 0x6, 0x14, 0x78, 0x110, 0x660, 0x1540, 0x7f80, 0x106, 0x614, 0x1478, 0x7910, 0x1666, 0x7554, 0x3ffe}); +constexpr StatTableTRI15 QRT_TABLE_TRI15({0, 0x114, 0x116, 0x428, 0x112, 0x137a, 0x420, 0x6d62, 0x102, 0x73a, 0x135a, 0x6460, 0x460, 0x4000, 0x6de2}); +typedef FieldTri FieldTri15; +#endif + +#ifdef ENABLE_FIELD_INT_16 +// 16 bit field +typedef RecLinTrans StatTable16; +constexpr StatTable16 SQR_TABLE_16({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x2b, 0xac, 0x2b0, 0xac0, 0x2b00, 0xac00, 0xb056, 0xc10e}); +constexpr StatTable16 SQR2_TABLE_16({0x1, 0x10, 0x100, 0x1000, 0x2b, 0x2b0, 0x2b00, 0xb056, 0x445, 0x4450, 0x45ac, 0x5a6c, 0xa647, 0x657e, 0x571a, 0x7127}); +constexpr StatTable16 SQR4_TABLE_16({0x1, 0x2b, 0x445, 0xa647, 0x12a1, 0xf69d, 0x7f07, 0x9825, 0x6fad, 0x399d, 0xb515, 0xd7d1, 0x3fb4, 0x4b06, 0xe4df, 0x93c7}); +constexpr StatTable16 QRT_TABLE_16({0x732, 0x72b8, 0x72ba, 0x7e96, 0x72be, 0x78b2, 0x7e9e, 0x8cba, 0x72ae, 0xfa24, 0x7892, 0x5892, 0x7ede, 0xbec6, 0x8c3a, 0}); +typedef Field Field16; +#endif +} + +Sketch* ConstructClMul2Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_10 + case 10: return new SketchImpl(implementation, 10); +#endif +#ifdef ENABLE_FIELD_INT_11 + case 11: return new SketchImpl(implementation, 11); +#endif +#ifdef ENABLE_FIELD_INT_12 + case 12: return new SketchImpl(implementation, 12); +#endif +#ifdef ENABLE_FIELD_INT_13 + case 13: return new SketchImpl(implementation, 13); +#endif +#ifdef ENABLE_FIELD_INT_14 + case 14: return new SketchImpl(implementation, 14); +#endif +#ifdef ENABLE_FIELD_INT_16 + case 16: return new SketchImpl(implementation, 16); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri2Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_9 + case 9: return new SketchImpl(implementation, 9); +#endif +#ifdef ENABLE_FIELD_INT_10 + case 10: return new SketchImpl(implementation, 10); +#endif +#ifdef ENABLE_FIELD_INT_11 + case 11: return new SketchImpl(implementation, 11); +#endif +#ifdef ENABLE_FIELD_INT_12 + case 12: return new SketchImpl(implementation, 12); +#endif +#ifdef ENABLE_FIELD_INT_14 + case 14: return new SketchImpl(implementation, 14); +#endif +#ifdef ENABLE_FIELD_INT_15 + case 15: return new SketchImpl(implementation, 15); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_3bytes.cpp b/src/fields/clmul_3bytes.cpp new file mode 100644 index 0000000000..b473f66ba2 --- /dev/null +++ b/src/fields/clmul_3bytes.cpp @@ -0,0 +1,166 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_3) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_17 +// 17 bit field +typedef RecLinTrans StatTable17; +constexpr StatTable17 SQR_TABLE_17({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x8012}); +constexpr StatTable17 SQR2_TABLE_17({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x48, 0x480, 0x4800, 0x8012, 0x104, 0x1040, 0x10400, 0x4048, 0x492, 0x4920, 0x9212, 0x12104}); +constexpr StatTable17 SQR4_TABLE_17({0x1, 0x10000, 0x8012, 0x4048, 0x12104, 0x1480, 0x5840, 0x14d20, 0x19202, 0x8112, 0x44c8, 0x13144, 0x5da0, 0x15850, 0x1cd7a, 0x1d34e, 0x1a484}); +constexpr StatTable17 SQR8_TABLE_17({0x1, 0x1a484, 0x1f24a, 0x1d572, 0x1eec4, 0x15448, 0xf9de, 0x9af0, 0x1ab78, 0x6048, 0xdc9a, 0x1eb24, 0x2ef4, 0x7c5e, 0x170b2, 0x16c1a, 0xa660}); +constexpr StatTable17 QRT_TABLE_17({0, 0x4c3e, 0x4c3c, 0x1a248, 0x4c38, 0x428, 0x1a240, 0x1b608, 0x4c28, 0x206, 0x408, 0x4000, 0x1a200, 0x18006, 0x1b688, 0x14d2e, 0x4d28}); +typedef Field Field17; +typedef FieldTri, &SQR_TABLE_17, &SQR2_TABLE_17, &SQR4_TABLE_17, &SQR8_TABLE_17, &QRT_TABLE_17, &QRT_TABLE_17, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri17; +#endif + +#ifdef ENABLE_FIELD_INT_18 +// 18 bit field +typedef RecLinTrans StatTable18; +constexpr StatTable18 SQR_TABLE_18({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x10012}); +constexpr StatTable18 SQR2_TABLE_18({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x24, 0x240, 0x2400, 0x24000, 0x41, 0x410, 0x4100, 0x1009, 0x10090, 0x924, 0x9240, 0x12412, 0x24104}); +constexpr StatTable18 SQR4_TABLE_18({0x1, 0x10000, 0x24000, 0x1009, 0x12412, 0x124, 0x201, 0x10480, 0x24820, 0x241, 0x10410, 0x24924, 0x8, 0x12, 0x20024, 0x8048, 0x12082, 0x920}); +constexpr StatTable18 SQR8_TABLE_18({0x1, 0x12082, 0x20904, 0x1000, 0x92, 0x904, 0x240, 0x12012, 0x4104, 0x41, 0x10080, 0x4924, 0x1009, 0x2412, 0x24804, 0x9240, 0x12410, 0x20}); +constexpr StatTable18 QRT_TABLE_18({0x9208, 0x422, 0x420, 0x8048, 0x424, 0x68b0, 0x8040, 0x30086, 0x434, 0x1040, 0x6890, 0x30ca2, 0x8000, 0x32896, 0x30006, 0, 0x534, 0x20532}); +typedef Field Field18; +typedef FieldTri, &SQR_TABLE_18, &SQR2_TABLE_18, &SQR4_TABLE_18, &SQR8_TABLE_18, &QRT_TABLE_18, &QRT_TABLE_18, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri18; +#endif + +#ifdef ENABLE_FIELD_INT_19 +// 19 bit field +typedef RecLinTrans StatTable19; +constexpr StatTable19 SQR_TABLE_19({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x3804e, 0x6011f}); +constexpr StatTable19 SQR2_TABLE_19({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x4e, 0x4e0, 0x4e00, 0x4e000, 0x6011f, 0x1054, 0x10540, 0x544e, 0x544e0, 0x44f76, 0x4f658, 0x7649f, 0x6481a, 0x48004}); +constexpr StatTable19 SQR4_TABLE_19({0x1, 0x10000, 0x4e000, 0x544e, 0x7649f, 0x15f0, 0x5afa, 0x35b7d, 0x17dca, 0x7390f, 0x151ae, 0x3902b, 0x41e9c, 0x7f117, 0x23ec7, 0x62c2f, 0x5e852, 0x69238, 0x775c}); +constexpr StatTable19 SQR8_TABLE_19({0x1, 0x5e852, 0x394a3, 0x29f41, 0x618e5, 0x4210, 0x7add9, 0x31105, 0x5d098, 0x7bb13, 0x44f00, 0x966, 0x11ae6, 0x70901, 0x664bf, 0x67449, 0x3d2bf, 0x4cbf9, 0x54e0c}); +constexpr StatTable19 QRT_TABLE_19({0x5d6b0, 0x2f476, 0x2f474, 0x1d6a2, 0x2f470, 0x42a, 0x1d6aa, 0x1060, 0x2f460, 0x19e92, 0x40a, 0x1da98, 0x1d6ea, 0x28c78, 0x10e0, 0xf56a, 0x2f560, 0, 0x19c92}); +typedef Field Field19; +#endif + +#ifdef ENABLE_FIELD_INT_20 +// 20 bit field +typedef RecLinTrans StatTable20; +constexpr StatTable20 SQR_TABLE_20({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x90000, 0x40012}); +constexpr StatTable20 SQR2_TABLE_20({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x9, 0x90, 0x900, 0x9000, 0x90000, 0x41, 0x410, 0x4100, 0x41000, 0x10024, 0x249, 0x2490, 0x24900, 0x49012, 0x90104}); +constexpr StatTable20 SQR4_TABLE_20({0x1, 0x10000, 0x9000, 0x4100, 0x2490, 0x1001, 0x10900, 0x9410, 0x4349, 0x92594, 0x91, 0x10041, 0x19024, 0x4d112, 0x2599, 0x91091, 0x51941, 0x3dd34, 0x5d34b, 0x9b494}); +constexpr StatTable20 SQR8_TABLE_20({0x1, 0x51941, 0x880b5, 0x66d0, 0x46103, 0x19025, 0x45a49, 0x8a4b4, 0x80b45, 0x81f9f, 0xb081, 0x41040, 0xd19f5, 0xc11be, 0x4634b, 0xd8d70, 0x11027, 0xf8651, 0x141fa, 0xdc63}); +constexpr StatTable20 QRT_TABLE_20({0xc5dea, 0xc0110, 0xc0112, 0xe11de, 0xc0116, 0x24814, 0xe11d6, 0x20080, 0xc0106, 0xfe872, 0x24834, 0xe4106, 0xe1196, 0x1d9a4, 0x20000, 0x31190, 0xc0006, 0, 0xfea72, 0x7ea74}); +typedef Field Field20; +typedef FieldTri, &SQR_TABLE_20, &SQR2_TABLE_20, &SQR4_TABLE_20, &SQR8_TABLE_20, &QRT_TABLE_20, &QRT_TABLE_20, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri20; +#endif + +#ifdef ENABLE_FIELD_INT_21 +// 21 bit field +typedef RecLinTrans StatTable21; +constexpr StatTable21 SQR_TABLE_21({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x80005}); +constexpr StatTable21 SQR2_TABLE_21({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x28, 0x280, 0x2800, 0x28000, 0x80005, 0x44, 0x440, 0x4400, 0x44000, 0x4000a, 0xaa, 0xaa0, 0xaa00, 0xaa000, 0xa0011}); +constexpr StatTable21 SQR4_TABLE_21({0x1, 0x10000, 0x2800, 0x440, 0xaa, 0xa0011, 0x101000, 0x28280, 0x4444, 0x40aaa, 0xaa101, 0x128, 0x8002d, 0xc4005, 0x4ea00, 0xba10, 0x101290, 0x1282c4, 0x6c44e, 0xeeeaa, 0xbaaa1}); +constexpr StatTable21 SQR8_TABLE_21({0x1, 0x101290, 0xc412d, 0x1ab101, 0x986d1, 0x1c6cc5, 0x3aa8c, 0x14b0fe, 0x1e7301, 0xb491d, 0x10d23e, 0xa4015, 0x4c2fa, 0xce8e5, 0xadfd9, 0xf110, 0x5220c, 0xf225f, 0xb8bdb, 0x159467, 0xc0df9}); +constexpr StatTable21 QRT_TABLE_21({0x1bd5fc, 0xbc196, 0xbc194, 0x74b96, 0xbc190, 0x1048, 0x74b9e, 0x672c8, 0xbc180, 0x4080, 0x1068, 0xc8200, 0x74bde, 0x64280, 0x67248, 0xc4280, 0xbc080, 0x80000, 0x4280, 0, 0x1468}); +typedef Field Field21; +typedef FieldTri, &SQR_TABLE_21, &SQR2_TABLE_21, &SQR4_TABLE_21, &SQR8_TABLE_21, &QRT_TABLE_21, &QRT_TABLE_21, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri21; +#endif + +#ifdef ENABLE_FIELD_INT_22 +// 22 bit field +typedef RecLinTrans StatTableTRI22; +constexpr StatTableTRI22 SQR_TABLE_TRI22({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000}); +constexpr StatTableTRI22 SQR2_TABLE_TRI22({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0xc, 0xc0, 0xc00, 0xc000, 0xc0000, 0x5, 0x50, 0x500, 0x5000, 0x50000, 0x100003, 0x3c, 0x3c0, 0x3c00, 0x3c000, 0x3c0000}); +constexpr StatTableTRI22 SQR4_TABLE_TRI22({0x1, 0x10000, 0xc00, 0x50, 0x100003, 0x3c000, 0x1100, 0xcc, 0xc0005, 0x55000, 0x3fc0, 0x101, 0x1000c, 0xc0c00, 0x5050, 0x1003c3, 0x3c011, 0x111100, 0xcccc, 0xc0555, 0x15503f, 0x3fffc0}); +constexpr StatTableTRI22 SQR8_TABLE_TRI22({0x1, 0x3c011, 0x3ec1, 0x101103, 0x14503e, 0x28282, 0xd0009, 0x1d9c, 0xcc598, 0x25c81, 0x47304, 0xc0004, 0x3cc41, 0xcf758, 0x11415f, 0x1d11f7, 0x128280, 0x1b9027, 0x1070ce, 0x10eb5e, 0x5c0ec, 0x2097e0}); +constexpr StatTableTRI22 QRT_TABLE_TRI22({0x210d16, 0x104a, 0x1048, 0x4088, 0x104c, 0x200420, 0x4080, 0x492dc, 0x105c, 0x1a67f0, 0x200400, 0x21155c, 0x40c0, 0x20346c, 0x4925c, 0x1af7ac, 0x115c, 0x2274ac, 0x1a65f0, 0x2a65f0, 0x200000, 0}); +typedef FieldTri FieldTri22; +#endif + +#ifdef ENABLE_FIELD_INT_23 +// 23 bit field +typedef RecLinTrans StatTable23; +constexpr StatTable23 SQR_TABLE_23({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x42, 0x108, 0x420, 0x1080, 0x4200, 0x10800, 0x42000, 0x108000, 0x420000, 0x80042, 0x200108}); +constexpr StatTable23 SQR2_TABLE_23({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x42, 0x420, 0x4200, 0x42000, 0x420000, 0x200108, 0x1004, 0x10040, 0x100400, 0x4042, 0x40420, 0x404200, 0x42108, 0x421080, 0x210908, 0x109004, 0x90002}); +constexpr StatTable23 SQR4_TABLE_23({0x1, 0x10000, 0x4200, 0x1004, 0x40420, 0x210908, 0x52, 0x520000, 0x142400, 0x52148, 0x494202, 0x10c204, 0x1104, 0x40462, 0x630908, 0x100452, 0x562108, 0x1d2402, 0x57348, 0x495626, 0x34c72c, 0x21584e, 0x4614b0}); +constexpr StatTable23 SQR8_TABLE_23({0x1, 0x562108, 0x662840, 0x5304, 0x6d3842, 0x738f46, 0x50472, 0x6ff79e, 0x7cf204, 0x436274, 0x3e4bde, 0x42a93e, 0x147704, 0x6c3810, 0x28bff4, 0x78815c, 0x7ab4b0, 0x62852a, 0x255b30, 0x5653d0, 0x1afd36, 0x5f118, 0x601dd4}); +constexpr StatTable23 QRT_TABLE_23({0, 0x1040, 0x1042, 0x43056, 0x1046, 0x121d76, 0x4305e, 0x40a0, 0x1056, 0x15176, 0x121d56, 0x7ee1f6, 0x4301e, 0x40000, 0x4020, 0x4f0be, 0x1156, 0x7cf0a0, 0x15376, 0x1ee9e8, 0x121956, 0x3ac9f6, 0x7ee9f6}); +typedef Field Field23; +typedef FieldTri, &SQR_TABLE_23, &SQR2_TABLE_23, &SQR4_TABLE_23, &SQR8_TABLE_23, nullptr, &QRT_TABLE_23, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri23; +#endif + +#ifdef ENABLE_FIELD_INT_24 +// 24 bit field +typedef RecLinTrans StatTable24; +constexpr StatTable24 SQR_TABLE_24({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0xb0001b, 0xc0005a}); +constexpr StatTable24 SQR2_TABLE_24({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1b, 0x1b0, 0x1b00, 0x1b000, 0x1b0000, 0xb0001b, 0x145, 0x1450, 0x14500, 0x145000, 0x45001b, 0x5001dc, 0x1db7, 0x1db70, 0x1db700, 0xdb701b, 0xb7011f, 0x701105}); +constexpr StatTable24 SQR4_TABLE_24({0x1, 0x10000, 0x1b00, 0x145, 0x45001b, 0x1db700, 0x11011, 0x111ab0, 0xb1aa5e, 0x51450e, 0x96db7, 0xb7c60f, 0x1a1a, 0x1a015e, 0x5f5e1b, 0x1ceef2, 0xf30ca2, 0xabbdb4, 0xba1aff, 0xf0bf5e, 0x579fc9, 0xce3da9, 0xa2c07f, 0x71dd40}); +constexpr StatTable24 SQR8_TABLE_24({0x1, 0xf30ca2, 0x573345, 0xb0a14e, 0xafd77d, 0x1419b, 0xb616a2, 0xba7db, 0xbe1560, 0xe0d0a3, 0x15bf5, 0x1056dd, 0xa29845, 0xf83d32, 0x13e0e9, 0xe2d8d3, 0xa10841, 0x57ac5a, 0x1c432f, 0x57044e, 0x454fba, 0x2bb37c, 0xf50fa, 0x85d5b9}); +constexpr StatTable24 QRT_TABLE_24({0x104e, 0xaf42a8, 0xaf42aa, 0xb78186, 0xaf42ae, 0x4090, 0xb7818e, 0x4a37c, 0xaf42be, 0x3688c0, 0x40b0, 0x80080e, 0xb781ce, 0xaf2232, 0x4a3fc, 0x856a82, 0xaf43be, 0x29c970, 0x368ac0, 0x968ace, 0x44b0, 0x77d570, 0x80000e, 0}); +typedef Field Field24; +#endif +} + +Sketch* ConstructClMul3Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_17 + case 17: return new SketchImpl(implementation, 17); +#endif +#ifdef ENABLE_FIELD_INT_18 + case 18: return new SketchImpl(implementation, 18); +#endif +#ifdef ENABLE_FIELD_INT_19 + case 19: return new SketchImpl(implementation, 19); +#endif +#ifdef ENABLE_FIELD_INT_20 + case 20: return new SketchImpl(implementation, 20); +#endif +#ifdef ENABLE_FIELD_INT_21 + case 21: return new SketchImpl(implementation, 21); +#endif +#ifdef ENABLE_FIELD_INT_23 + case 23: return new SketchImpl(implementation, 23); +#endif +#ifdef ENABLE_FIELD_INT_24 + case 24: return new SketchImpl(implementation, 24); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri3Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_17 + case 17: return new SketchImpl(implementation, 17); +#endif +#ifdef ENABLE_FIELD_INT_18 + case 18: return new SketchImpl(implementation, 18); +#endif +#ifdef ENABLE_FIELD_INT_20 + case 20: return new SketchImpl(implementation, 20); +#endif +#ifdef ENABLE_FIELD_INT_21 + case 21: return new SketchImpl(implementation, 21); +#endif +#ifdef ENABLE_FIELD_INT_22 + case 22: return new SketchImpl(implementation, 22); +#endif +#ifdef ENABLE_FIELD_INT_23 + case 23: return new SketchImpl(implementation, 23); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_4bytes.cpp b/src/fields/clmul_4bytes.cpp new file mode 100644 index 0000000000..c65974394c --- /dev/null +++ b/src/fields/clmul_4bytes.cpp @@ -0,0 +1,158 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_4) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_25 +// 25 bit field +typedef RecLinTrans StatTable25; +constexpr StatTable25 SQR_TABLE_25({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x800012}); +constexpr StatTable25 SQR2_TABLE_25({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x48, 0x480, 0x4800, 0x48000, 0x480000, 0x800012, 0x104, 0x1040, 0x10400, 0x104000, 0x1040000, 0x400048, 0x492, 0x4920, 0x49200, 0x492000, 0x920012, 0x1200104}); +constexpr StatTable25 SQR4_TABLE_25({0x1, 0x10000, 0x480, 0x800012, 0x104000, 0x4920, 0x1200104, 0x1001000, 0x48048, 0x481040, 0x410448, 0x492492, 0x930002, 0x580, 0x1800012, 0x14c000, 0x5960, 0x160014c, 0x1493000, 0x58058, 0x5814c0, 0xc14c5a, 0x596596, 0x1974922, 0x1249684}); +constexpr StatTable25 SQR8_TABLE_25({0x1, 0x5960, 0x1411448, 0x1860922, 0x1d814d2, 0x1cdede8, 0x1e15e16, 0x1b79686, 0xfdf116, 0x1efe4c8, 0x1b839a8, 0x10ced66, 0xae05ce, 0x1459400, 0xa29fa6, 0x85e4d2, 0x7eecee, 0x183a96, 0x1eb2fa8, 0xede876, 0xf6e440, 0x1f7140a, 0xd07d7c, 0x10e4ea2, 0x1222a54}); +constexpr StatTable25 QRT_TABLE_25({0, 0x482110, 0x482112, 0x1b3c3e6, 0x482116, 0x4960ae, 0x1b3c3ee, 0x4088, 0x482106, 0x58a726, 0x49608e, 0x5ce52e, 0x1b3c3ae, 0x2006, 0x4008, 0x1c1a8, 0x482006, 0x1e96488, 0x58a526, 0x400000, 0x49648e, 0x1800006, 0x5ced2e, 0xb3d3a8, 0x1b3d3ae}); +typedef Field Field25; +typedef FieldTri, &SQR_TABLE_25, &SQR2_TABLE_25, &SQR4_TABLE_25, &SQR8_TABLE_25, &QRT_TABLE_25, &QRT_TABLE_25, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri25; +#endif + +#ifdef ENABLE_FIELD_INT_26 +// 26 bit field +typedef RecLinTrans StatTable26; +constexpr StatTable26 SQR_TABLE_26({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0x1b00000, 0x2c0001b, 0x300005a}); +constexpr StatTable26 SQR2_TABLE_26({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x6c, 0x6c0, 0x6c00, 0x6c000, 0x6c0000, 0x2c0001b, 0x145, 0x1450, 0x14500, 0x145000, 0x1450000, 0x500077, 0x100076b, 0x76dc, 0x76dc0, 0x76dc00, 0x36dc01b, 0x2dc011f, 0x1c01105}); +constexpr StatTable26 SQR4_TABLE_26({0x1, 0x10000, 0x6c0, 0x2c0001b, 0x145000, 0x76dc, 0x2dc011f, 0x1101100, 0x106ac6c, 0x6ad515, 0x1145127, 0x121b6dc, 0x2da1d0f, 0x10007c1, 0x3c7c01b, 0x128290, 0x29062e0, 0x2ee8d68, 0x167abcd, 0x3cabbce, 0x3c7a862, 0x6b83ce, 0x3cf5620, 0x229b787, 0x38a6b0f, 0x3071ade}); +constexpr StatTable26 SQR8_TABLE_26({0x1, 0x29062e0, 0x2b2942d, 0x34ab63, 0x3bddebb, 0x7b1823, 0x58b9ae, 0x391720e, 0x1385e18, 0x3891746, 0x13069c5, 0x2dfd089, 0x12a35ff, 0x3e534f, 0x172c6a2, 0x55338f, 0x3887137, 0x3f45b03, 0x164a695, 0x2c7e7ef, 0x29c907d, 0x636c85, 0x3db4007, 0x97e7ff, 0x3cbfe55, 0x31c0d96}); +constexpr StatTable26 QRT_TABLE_26({0x217b530, 0x2ae82a8, 0x2ae82aa, 0x2001046, 0x2ae82ae, 0x2de032e, 0x200104e, 0x70c10c, 0x2ae82be, 0x20151f2, 0x2de030e, 0xbc1400, 0x200100e, 0x178570, 0x70c18c, 0x2ae4232, 0x2ae83be, 0x211d742, 0x20153f2, 0x21f54f2, 0x2de070e, 0x5e0700, 0xbc1c00, 0x3abb97e, 0x200000e, 0}); +typedef Field Field26; +#endif + +#ifdef ENABLE_FIELD_INT_27 +// 27 bit field +typedef RecLinTrans StatTable27; +constexpr StatTable27 SQR_TABLE_27({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x138000, 0x4e0000, 0x1380000, 0x4e00000, 0x380004e, 0x600011f}); +constexpr StatTable27 SQR2_TABLE_27({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x4e, 0x4e0, 0x4e00, 0x4e000, 0x4e0000, 0x4e00000, 0x600011f, 0x1054, 0x10540, 0x105400, 0x1054000, 0x54004e, 0x54004e0, 0x4004f76, 0x4f658, 0x4f6580, 0x4f65800, 0x765811f, 0x658101a, 0x5810004}); +constexpr StatTable27 SQR4_TABLE_27({0x1, 0x10000, 0x4e0, 0x4e00000, 0x105400, 0x4004f76, 0x765811f, 0x1001110, 0x114e04e, 0x4abe54, 0x6551445, 0x45e212e, 0x13ccbdc, 0x3d805ef, 0x5e10100, 0x114b0e0, 0xe4bf22, 0x721c505, 0x51b3ba8, 0x3bf04d5, 0x4dabba0, 0x3b0aa45, 0x24a80cb, 0xc3d4b0, 0x4b34626, 0x6372e18, 0x6028c1b}); +constexpr StatTable27 SQR8_TABLE_27({0x1, 0xe4bf22, 0x430cb3c, 0x73b7225, 0x6526539, 0x3c278e3, 0x4724a6e, 0x48b39b4, 0x1dbf7de, 0x106508, 0x3564785, 0x33ae33f, 0x61d6685, 0x6adaca3, 0x2786b6f, 0x4e76784, 0x869f42, 0x466b048, 0x415e00e, 0x46c3c9a, 0x73ffd91, 0x49002e0, 0x3734fed, 0x3c04a43, 0x191d3ee, 0xe828b9, 0xfab68c}); +constexpr StatTable27 QRT_TABLE_27({0x6bf0530, 0x2be4496, 0x2be4494, 0x2bf0522, 0x2be4490, 0x1896cca, 0x2bf052a, 0x408a, 0x2be4480, 0x368ae72, 0x1896cea, 0x18d2ee0, 0x2bf056a, 0x1c76d6a, 0x400a, 0x336e9f8, 0x2be4580, 0x36baf12, 0x368ac72, 0x430360, 0x18968ea, 0x34a6b80, 0x18d26e0, 0xbf1560, 0x2bf156a, 0, 0x1c74d6a}); +typedef Field Field27; +#endif + +#ifdef ENABLE_FIELD_INT_28 +// 28 bit field +typedef RecLinTrans StatTableTRI28; +constexpr StatTableTRI28 SQR_TABLE_TRI28({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000}); +constexpr StatTableTRI28 SQR2_TABLE_TRI28({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x3, 0x30, 0x300, 0x3000, 0x30000, 0x300000, 0x3000000, 0x5, 0x50, 0x500, 0x5000, 0x50000, 0x500000, 0x5000000, 0xf, 0xf0, 0xf00, 0xf000, 0xf0000, 0xf00000, 0xf000000}); +constexpr StatTableTRI28 SQR4_TABLE_TRI28({0x1, 0x10000, 0x30, 0x300000, 0x500, 0x5000000, 0xf000, 0x11, 0x110000, 0x330, 0x3300000, 0x5500, 0x500000f, 0xff000, 0x101, 0x1010000, 0x3030, 0x300005, 0x50500, 0x50000f0, 0xf0f000, 0x1111, 0x1110003, 0x33330, 0x3300055, 0x555500, 0x5000fff, 0xffff000}); +constexpr StatTableTRI28 SQR8_TABLE_TRI28({0x1, 0x3030, 0x5000500, 0xf0e111, 0x3210000, 0x6300faa, 0x40ef10e, 0x501, 0xf0c030, 0x5110630, 0x395b444, 0x621010e, 0x6010f9b, 0x13bc4cb, 0x110001, 0x3303065, 0xff50f, 0xf0e120, 0x3243530, 0x330fabb, 0x5ec232c, 0x511050e, 0x3c1c064, 0x2ec60a, 0x3954175, 0x7c5c43d, 0x20acba, 0x943bc43}); +constexpr StatTableTRI28 QRT_TABLE_TRI28({0x121d57a, 0x40216, 0x40214, 0x8112578, 0x40210, 0x10110, 0x8112570, 0x12597ec, 0x40200, 0x6983e00, 0x10130, 0x972b99c, 0x8112530, 0x8002000, 0x125976c, 0x815a76c, 0x40300, 0x936b29c, 0x6983c00, 0x97bb8ac, 0x10530, 0x9103000, 0x972b19c, 0xf6384ac, 0x8113530, 0x4113530, 0x8000000, 0}); +typedef FieldTri FieldTri28; +#endif + +#ifdef ENABLE_FIELD_INT_29 +// 29 bit field +typedef RecLinTrans StatTable29; +constexpr StatTable29 SQR_TABLE_29({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x280000, 0xa00000, 0x2800000, 0xa000000, 0x8000005}); +constexpr StatTable29 SQR2_TABLE_29({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x28, 0x280, 0x2800, 0x28000, 0x280000, 0x2800000, 0x8000005, 0x44, 0x440, 0x4400, 0x44000, 0x440000, 0x4400000, 0x400000a, 0xaa, 0xaa0, 0xaa00, 0xaa000, 0xaa0000, 0xaa00000, 0xa000011}); +constexpr StatTable29 SQR4_TABLE_29({0x1, 0x10000, 0x28, 0x280000, 0x440, 0x4400000, 0xaa00, 0xa000011, 0x101000, 0x10000280, 0x2828000, 0x4444, 0x444000a, 0xaaaa0, 0xaa00101, 0x1000100, 0x1002800, 0x8002805, 0x8044005, 0x440aa, 0xaa00aa, 0xaa1010, 0x10101010, 0x10128280, 0x28282c4, 0x2c44444, 0x4444eaa, 0xeaaaaaa, 0xaaba001}); +constexpr StatTable29 SQR8_TABLE_29({0x1, 0x1002800, 0x4680000, 0xae50ba, 0x2822a00, 0x14545eba, 0x110aed64, 0xc6eeaaf, 0x4ee00a0, 0x10aba290, 0x1bd6efc1, 0x8222b29, 0x1c791ebf, 0x174e85da, 0x1cc66c7f, 0x29292c4, 0x2886c20, 0xea04467, 0xc0eeb87, 0xccd4115, 0x16d5fa2e, 0x1cf8fe75, 0xe45a4e1, 0x19018b3f, 0x1d64778, 0x2e0bdf8, 0xa1bd96b, 0xff5b70e, 0x14d89770}); +constexpr StatTable29 QRT_TABLE_29({0x1b8351dc, 0xb87135e, 0xb87135c, 0xda7b35e, 0xb871358, 0x621a116, 0xda7b356, 0x40200, 0xb871348, 0xc9e2620, 0x621a136, 0x478b16, 0xda7b316, 0x6762e20, 0x40280, 0x6202000, 0xb871248, 0x627a316, 0xc9e2420, 0xcd1ad36, 0x621a536, 0x760e20, 0x478316, 0xa760e20, 0xda7a316, 0x8000000, 0x6760e20, 0, 0x44280}); +typedef Field Field29; +typedef FieldTri, &SQR_TABLE_29, &SQR2_TABLE_29, &SQR4_TABLE_29, &SQR8_TABLE_29, &QRT_TABLE_29, &QRT_TABLE_29, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri29; +#endif + +#ifdef ENABLE_FIELD_INT_30 +// 30 bit field +typedef RecLinTrans StatTableTRI30; +constexpr StatTableTRI30 SQR_TABLE_TRI30({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000}); +constexpr StatTableTRI30 SQR2_TABLE_TRI30({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0xc, 0xc0, 0xc00, 0xc000, 0xc0000, 0xc00000, 0xc000000, 0x5, 0x50, 0x500, 0x5000, 0x50000, 0x500000, 0x5000000, 0x10000003, 0x3c, 0x3c0, 0x3c00, 0x3c000, 0x3c0000, 0x3c00000, 0x3c000000}); +constexpr StatTableTRI30 SQR4_TABLE_TRI30({0x1, 0x10000, 0xc, 0xc0000, 0x50, 0x500000, 0x3c0, 0x3c00000, 0x1100, 0x11000000, 0xcc00, 0xc000005, 0x55000, 0x1000003f, 0x3fc000, 0x101, 0x1010000, 0xc0c, 0xc0c0000, 0x5050, 0x10500003, 0x3c3c0, 0x3c00011, 0x111100, 0x110000cc, 0xcccc00, 0xc000555, 0x5555000, 0x10003fff, 0x3fffc000}); +constexpr StatTableTRI30 SQR8_TABLE_TRI30({0x1, 0x1010000, 0xc000c, 0xc0c5050, 0x390, 0x13900012, 0x12c012c0, 0x121ddddd, 0x54100, 0x1003f33, 0xc3f0d04, 0x9555558, 0xd379000, 0x105d3fa2, 0x1d615e9e, 0x1101, 0x100100cc, 0xc0ccc09, 0x5590505, 0x3a9390, 0x3913fec, 0x13fedfcd, 0x121ddd8c, 0x11544103, 0x2cc3cff, 0x3e24c45, 0x9558bc8, 0x3a7958b, 0x1e98b158, 0x29d629e9}); +constexpr StatTableTRI30 QRT_TABLE_TRI30({0x2159df4a, 0x109134a, 0x1091348, 0x10114, 0x109134c, 0x3a203420, 0x1011c, 0x20004080, 0x109135c, 0x2005439c, 0x3a203400, 0x100400, 0x1015c, 0x3eb21930, 0x20004000, 0x20504c00, 0x109125c, 0x3b2b276c, 0x2005419c, 0x210450c0, 0x3a203000, 0x3e93186c, 0x100c00, 0x3aa23530, 0x1115c, 0x6b3286c, 0x3eb23930, 0xeb23930, 0x20000000, 0}); +typedef FieldTri FieldTri30; +#endif + +#ifdef ENABLE_FIELD_INT_31 +// 31 bit field +typedef RecLinTrans StatTable31; +constexpr StatTable31 SQR_TABLE_31({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x4800000, 0x12000000, 0x48000000, 0x20000012}); +constexpr StatTable31 SQR2_TABLE_31({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x12, 0x120, 0x1200, 0x12000, 0x120000, 0x1200000, 0x12000000, 0x20000012, 0x104, 0x1040, 0x10400, 0x104000, 0x1040000, 0x10400000, 0x4000012, 0x40000120, 0x1248, 0x12480, 0x124800, 0x1248000, 0x12480000, 0x24800012, 0x48000104}); +constexpr StatTable31 SQR4_TABLE_31({0x1, 0x10000, 0x12, 0x120000, 0x104, 0x1040000, 0x1248, 0x12480000, 0x10010, 0x100012, 0x120120, 0x1200104, 0x1041040, 0x10401248, 0x12492480, 0x24810002, 0x112, 0x1120000, 0x1304, 0x13040000, 0x11648, 0x16480012, 0x134810, 0x48100116, 0x1121120, 0x11201304, 0x13053040, 0x3041165a, 0x16596492, 0x64934922, 0x49248016}); +constexpr StatTable31 SQR8_TABLE_31({0x1, 0x112, 0x10104, 0x1131648, 0x10002, 0x1120224, 0x106021a, 0x146e3f86, 0x16, 0x174c, 0x161658, 0x175b1130, 0x16002c, 0x174c2e98, 0x16742dfc, 0x3f877966, 0x114, 0x10768, 0x1151050, 0x66b75b2, 0x1140228, 0x76a0ec2, 0x127a33da, 0x79648102, 0x1738, 0x1665f0, 0x172f64e0, 0x73cc668c, 0x17382e70, 0x65dccaac, 0x4abf956e}); +constexpr StatTable31 QRT_TABLE_31({0, 0x10110, 0x10112, 0x15076e, 0x10116, 0x117130e, 0x150766, 0x4743fa0, 0x10106, 0x1121008, 0x117132e, 0x176b248e, 0x150726, 0x172a2c88, 0x4743f20, 0x7eb81e86, 0x10006, 0x20008, 0x1121208, 0x56b2c8e, 0x117172e, 0x133f1bae, 0x176b2c8e, 0x7f2a0c8e, 0x151726, 0x10000000, 0x172a0c88, 0x60000006, 0x4747f20, 0x3eb89e80, 0x7eb89e86}); +typedef Field Field31; +typedef FieldTri, &SQR_TABLE_31, &SQR2_TABLE_31, &SQR4_TABLE_31, &SQR8_TABLE_31, &QRT_TABLE_31, &QRT_TABLE_31, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri31; +#endif + +#ifdef ENABLE_FIELD_INT_32 +// 32 bit field +typedef RecLinTrans StatTable32; +constexpr StatTable32 SQR_TABLE_32({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x8d, 0x234, 0x8d0, 0x2340, 0x8d00, 0x23400, 0x8d000, 0x234000, 0x8d0000, 0x2340000, 0x8d00000, 0x23400000, 0x8d000000, 0x3400011a, 0xd0000468, 0x40001037}); +constexpr StatTable32 SQR2_TABLE_32({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x8d, 0x8d0, 0x8d00, 0x8d000, 0x8d0000, 0x8d00000, 0x8d000000, 0xd0000468, 0x4051, 0x40510, 0x405100, 0x4051000, 0x40510000, 0x5100234, 0x51002340, 0x100236b9, 0x236b1d, 0x236b1d0, 0x236b1d00, 0x36b1d11a, 0x6b1d1037, 0xb1d1005e, 0x1d10001f, 0xd100017d}); +constexpr StatTable32 SQR4_TABLE_32({0x1, 0x10000, 0x8d, 0x8d0000, 0x4051, 0x40510000, 0x236b1d, 0x6b1d1037, 0x10001101, 0x1109d000, 0xd00859e5, 0x59881468, 0x144737e8, 0x37e2c4e3, 0xc4f9a67a, 0xa61d8c55, 0x8c010001, 0x41dc8d, 0xdc8d23cd, 0x23a60c51, 0xc41630e, 0x63087fcd, 0x7ffe7368, 0x735580f6, 0x80cd8e29, 0x8e6fe311, 0xe350f32b, 0xf35edc90, 0xdced0bd6, 0xbbd3eb1, 0x3eb4a621, 0xa63f6bc4}); +constexpr StatTable32 SQR8_TABLE_32({0x1, 0x8c010001, 0x6b9010bb, 0x7faf6b, 0xc4da8d37, 0xc10ab646, 0x445f546c, 0xe389129e, 0xd8aa2d3e, 0x85249468, 0xd599253f, 0x458976f9, 0xc9c86411, 0xccc2f34b, 0xa79e37dc, 0x9068e3c4, 0x3a30447f, 0x674c3398, 0x94f38a7, 0x402d3532, 0x116fffc7, 0x1c6b5ba2, 0xcd6a32e4, 0x49067a77, 0xa7f6a61e, 0x3cc3746, 0xeebe962e, 0x599276e1, 0x7b5fa4d9, 0x2aa3ce1, 0x990f8767, 0x1c3b66cb}); +constexpr StatTable32 QRT_TABLE_32({0x54fd1264, 0xc26fcd64, 0xc26fcd66, 0x238a7462, 0xc26fcd62, 0x973bccaa, 0x238a746a, 0x77766712, 0xc26fcd72, 0xc1bdd556, 0x973bcc8a, 0x572a094c, 0x238a742a, 0xb693be84, 0x77766792, 0x9555c03e, 0xc26fcc72, 0x568419f8, 0xc1bdd756, 0x96c3d2ca, 0x973bc88a, 0x54861fdc, 0x572a014c, 0xb79badc4, 0x238a642a, 0xb9b99fe0, 0xb6939e84, 0xc519fa86, 0x77762792, 0, 0x9555403e, 0x377627ba}); +typedef Field Field32; +#endif +} + +Sketch* ConstructClMul4Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_25 + case 25: return new SketchImpl(implementation, 25); +#endif +#ifdef ENABLE_FIELD_INT_26 + case 26: return new SketchImpl(implementation, 26); +#endif +#ifdef ENABLE_FIELD_INT_27 + case 27: return new SketchImpl(implementation, 27); +#endif +#ifdef ENABLE_FIELD_INT_29 + case 29: return new SketchImpl(implementation, 29); +#endif +#ifdef ENABLE_FIELD_INT_31 + case 31: return new SketchImpl(implementation, 31); +#endif +#ifdef ENABLE_FIELD_INT_32 + case 32: return new SketchImpl(implementation, 32); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri4Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_25 + case 25: return new SketchImpl(implementation, 25); +#endif +#ifdef ENABLE_FIELD_INT_28 + case 28: return new SketchImpl(implementation, 28); +#endif +#ifdef ENABLE_FIELD_INT_29 + case 29: return new SketchImpl(implementation, 29); +#endif +#ifdef ENABLE_FIELD_INT_30 + case 30: return new SketchImpl(implementation, 30); +#endif +#ifdef ENABLE_FIELD_INT_31 + case 31: return new SketchImpl(implementation, 31); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_5bytes.cpp b/src/fields/clmul_5bytes.cpp new file mode 100644 index 0000000000..29c3fb10e7 --- /dev/null +++ b/src/fields/clmul_5bytes.cpp @@ -0,0 +1,174 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_5) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_33 +// 33 bit field +typedef RecLinTrans StatTable33; +constexpr StatTable33 SQR_TABLE_33({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x802, 0x2008, 0x8020, 0x20080, 0x80200, 0x200800, 0x802000, 0x2008000, 0x8020000, 0x20080000, 0x80200000, 0x800401, 0x2001004, 0x8004010, 0x20010040, 0x80040100}); +constexpr StatTable33 SQR2_TABLE_33({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x2008, 0x20080, 0x200800, 0x2008000, 0x20080000, 0x800401, 0x8004010, 0x80040100, 0x400004, 0x4000040, 0x40000400, 0x4802, 0x48020, 0x480200, 0x4802000, 0x48020000, 0x80200802, 0x2009024, 0x20090240, 0x902001, 0x9020010, 0x90200100, 0x102000004, 0x20002048}); +constexpr StatTable33 SQR4_TABLE_33({0x1, 0x10000, 0x100000000, 0x2008000, 0x80040100, 0x4802, 0x48020000, 0x902001, 0x20002048, 0x20081000, 0x10400004, 0x248820, 0x88204812, 0x49020410, 0x4822081, 0x20880641, 0x6000044, 0x480300, 0x3009024, 0x90220180, 0xa00c11, 0xc104050, 0x40482608, 0x2688b024, 0xb0690344, 0x102248834, 0x8a30c912, 0xc8062518, 0x24886803, 0x684a0244, 0x294a025, 0xa020294a, 0x280a1010}); +constexpr StatTable33 SQR8_TABLE_33({0x1, 0x6000044, 0x280a1010, 0x122ac8e75, 0x83209926, 0x4a7a8a1, 0xcada863d, 0x6f2ab824, 0x6b4a8654, 0x70484bd6, 0x164c04e0b, 0x2fbc1617, 0xe095e5a3, 0xeaf7847d, 0xe5625e26, 0xa6aaa3e5, 0xc0164126, 0xd06217c0, 0x1ae58d21, 0xa8600250, 0xbaf87951, 0x8e12c19a, 0xa9b413b9, 0xb75ef087, 0x17e9214d9, 0x85968f33, 0x1e299478f, 0x92bc9a0f, 0x1975d642, 0x11af0b3f1, 0x4e86ee77, 0xe75f4726, 0x38026cce}); +constexpr StatTable33 SQR16_TABLE_33({0x1, 0x185df5e91, 0x193fb40eb, 0xd464f9e4, 0x1ba2d73a6, 0x1d9288c5e, 0x5de03a49, 0x1869ea37b, 0x13faaf379, 0x195d1a8f5, 0x6afd5625, 0xf9d75bab, 0xaf44fe50, 0x101034b9e, 0xcc889caf, 0x5ec7455, 0x7d232a66, 0x17dcfe2c3, 0x1c66ff8d0, 0x17107e836, 0x1939cdead, 0x9852afa0, 0x1b946909a, 0x1846638c5, 0xdd5fa94c, 0x1cb2600fe, 0x19241c856, 0x15fe05ccd, 0xc9f9a425, 0x89e0f463, 0x37b01b39, 0xab0410e0, 0x1ace4ca03}); +constexpr StatTable33 QRT_TABLE_33({0xba504dd4, 0x1e2798ef2, 0x1e2798ef0, 0x6698a4ec, 0x1e2798ef4, 0x1c7f1bef0, 0x6698a4e4, 0x16da1b384, 0x1e2798ee4, 0x661ca6ec, 0x1c7f1bed0, 0x1483b87a6, 0x6698a4a4, 0x800000, 0x16da1b304, 0x1a185101c, 0x1e2798fe4, 0xaa400954, 0x661ca4ec, 0x667caeec, 0x1c7f1bad0, 0x400800, 0x1483b8fa6, 0, 0x6698b4a4, 0x1c61da4b8, 0x802000, 0x16e5dadec, 0x16da1f304, 0x62fc8eec, 0x1a185901c, 0x1661da5ec, 0x1e2788fe4}); +typedef Field Field33; +typedef FieldTri, &SQR_TABLE_33, &SQR2_TABLE_33, &SQR4_TABLE_33, &SQR8_TABLE_33, &SQR16_TABLE_33, &QRT_TABLE_33, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri33; +#endif + +#ifdef ENABLE_FIELD_INT_34 +// 34 bit field +typedef RecLinTrans StatTable34; +constexpr StatTable34 SQR_TABLE_34({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x81, 0x204, 0x810, 0x2040, 0x8100, 0x20400, 0x81000, 0x204000, 0x810000, 0x2040000, 0x8100000, 0x20400000, 0x81000000, 0x204000000, 0x10000102, 0x40000408, 0x100001020}); +constexpr StatTable34 SQR2_TABLE_34({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x204, 0x2040, 0x20400, 0x204000, 0x2040000, 0x20400000, 0x204000000, 0x40000408, 0x4001, 0x40010, 0x400100, 0x4001000, 0x40010000, 0x100081, 0x1000810, 0x10008100, 0x100081000, 0x810204, 0x8102040, 0x81020400, 0x10204102, 0x102041020, 0x20410004, 0x204100040, 0x41000008}); +constexpr StatTable34 SQR4_TABLE_34({0x1, 0x10000, 0x100000000, 0x204000, 0x40000408, 0x4001000, 0x10008100, 0x81020400, 0x204100040, 0x304, 0x3040000, 0x6041, 0x60410000, 0x1000c1010, 0x10304183, 0x4181020c, 0x102042060, 0x20400001, 0x50010, 0x100100081, 0xa14204, 0x142041428, 0x14001001, 0x10038500, 0x385020400, 0x204704140, 0x41000f1c, 0xf143040, 0x3041e145, 0x1e1430410, 0x3042c5050, 0x5030448b, 0x4481120c, 0x112048120}); +constexpr StatTable34 SQR8_TABLE_34({0x1, 0x102042060, 0x4481120c, 0x1523455ab, 0x307081050, 0x21410f1c, 0x275d0e309, 0x3f676408a, 0x143a54d38, 0x304100344, 0x181774550, 0x1003cd092, 0x3f36b6421, 0x164d51695, 0x3e7c7f2ab, 0x9309b234, 0x354f8d24c, 0x1f5431410, 0x142012478, 0xc5225409, 0x14033f3cf, 0x123bd530c, 0x1100ee58, 0x35490c368, 0x2e1f3dcba, 0x2018108d2, 0x3c61a735d, 0xbf8fa918, 0x282ab07ea, 0x19c32af, 0x175e54c02, 0x2e4dfe2bb, 0x3374ab928, 0x3124a055}); +constexpr StatTable34 SQR16_TABLE_34({0x1, 0x3448e6f02, 0x352590eb9, 0xb173da17, 0x264977d39, 0x172d45e48, 0x1e026e5d6, 0x357b54017, 0x2925d27a4, 0x1f6a32696, 0x2f49f220c, 0x3a7383d9e, 0x28111d79b, 0x5580fcf1, 0x276ede679, 0x175b379f8, 0x34d67b66, 0xc7019416, 0x3f3d9d59f, 0x2a7c2c032, 0x2b3482ba7, 0x177cd0128, 0x1d6f4bd2e, 0x31647a632, 0x41353027, 0x56292eea, 0x2733c0501, 0x6d7ed066, 0x2f3db9a75, 0x3225bc5cc, 0x3f22da089, 0xd0a7588e, 0xb60b22d1, 0xc2fddb7e}); +constexpr StatTable34 QRT_TABLE_34({0x2f973a1f6, 0x40202, 0x40200, 0x348102060, 0x40204, 0x8000420, 0x348102068, 0x1092195c8, 0x40214, 0x3f6881b6e, 0x8000400, 0x3f810383e, 0x348102028, 0x340002068, 0x109219548, 0x24015a774, 0x40314, 0x3f050343e, 0x3f688196e, 0x3f81c3a3a, 0x8000000, 0x24031a560, 0x3f810303e, 0xb08c1a12, 0x348103028, 0xb2881906, 0x340000068, 0, 0x10921d548, 0x2e131e576, 0x240152774, 0x18921d55e, 0x50314, 0x14015271c}); +typedef Field Field34; +typedef FieldTri, &SQR_TABLE_34, &SQR2_TABLE_34, &SQR4_TABLE_34, &SQR8_TABLE_34, &SQR16_TABLE_34, &QRT_TABLE_34, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri34; +#endif + +#ifdef ENABLE_FIELD_INT_35 +// 35 bit field +typedef RecLinTrans StatTable35; +constexpr StatTable35 SQR_TABLE_35({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x280000, 0xa00000, 0x2800000, 0xa000000, 0x28000000, 0xa0000000, 0x280000000, 0x200000005}); +constexpr StatTable35 SQR2_TABLE_35({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0xa, 0xa0, 0xa00, 0xa000, 0xa0000, 0xa00000, 0xa000000, 0xa0000000, 0x200000005, 0x44, 0x440, 0x4400, 0x44000, 0x440000, 0x4400000, 0x44000000, 0x440000000, 0x400000028, 0x2a8, 0x2a80, 0x2a800, 0x2a8000, 0x2a80000, 0x2a800000, 0x2a8000000, 0x280000011}); +constexpr StatTable35 SQR4_TABLE_35({0x1, 0x10000, 0x100000000, 0xa000, 0xa0000000, 0x4400, 0x44000000, 0x2a80, 0x2a800000, 0x1010, 0x10100000, 0xa0a, 0xa0a0000, 0x200000445, 0x4444000, 0x4400002a8, 0x2aaa800, 0x2a8000101, 0x1000100, 0x10000a0, 0xa000a0, 0xa00044, 0x440044, 0x400440028, 0x4002a8028, 0x2802a8011, 0x280101011, 0x1010100a, 0x100a0a0a, 0x20a0a0a05, 0x20a044445, 0x444444440, 0x44442aaa8, 0x2aaaaaaa8, 0x2aaa90001}); +constexpr StatTable35 SQR8_TABLE_35({0x1, 0x2aaa800, 0x44442aaa8, 0x6400006ed, 0x64e4e4e45, 0x14544000, 0x8a145454, 0x2000034df, 0x49a749a36, 0xaa0a0000, 0x10aa0aaa, 0x1ba1a, 0x393a91ba, 0x3febaaaa9, 0x285105155, 0xa0ad9ad4, 0x269ce8d3b, 0x4de74f4e6, 0x42aaa8028, 0x4002aeea8, 0x400e46eec, 0x544e4006c, 0x145440144, 0x2abede545, 0x44309e74c, 0xa74eeda4, 0x64444ee49, 0x1aa1aaaa, 0x2b90bb1b1, 0x393902109, 0x16bc47bb2, 0x271ad1511, 0x6c8f98767, 0x69d3aa74c, 0x27790dc3b}); +constexpr StatTable35 SQR16_TABLE_35({0x1, 0x4c80f98a4, 0x763684437, 0x5a1cc86a0, 0x38922db8, 0x71755e12d, 0x2ca94c627, 0x388a2bc7f, 0x406596de0, 0x1818c6958, 0x174a92efe, 0x1a80c764e, 0x2f23eacbf, 0xd611ea8, 0x64d783fd5, 0x4fdfe0798, 0x31459de8d, 0x62c889d99, 0x9c419962, 0x2d8d865b3, 0x1ac7e7ffc, 0x38a0c12f3, 0x9fbc1076, 0x6f76d3b89, 0x6e472c757, 0x5f240de42, 0x10176ecc0, 0x20c1cef8, 0x8f77f91c, 0x3f6e533b9, 0x62017c147, 0x5ce81e2fa, 0x371fe4ad9, 0x2552b5046, 0xc3f3696c}); +constexpr StatTable35 QRT_TABLE_35({0x5c2038114, 0x2bf547ee8, 0x2bf547eea, 0x2bf1074e8, 0x2bf547eee, 0x1883d0736, 0x2bf1074e0, 0x100420, 0x2bf547efe, 0x400800, 0x1883d0716, 0x5e90e4a0, 0x2bf1074a0, 0x4e70ac20, 0x1004a0, 0x2f060c880, 0x2bf547ffe, 0x37d55fffe, 0x400a00, 0x3372573de, 0x1883d0316, 0x700c20, 0x5e90eca0, 0x10604880, 0x2bf1064a0, 0x18f35377e, 0x4e708c20, 0x33f557ffe, 0x1044a0, 0x1bf557ffe, 0x2f0604880, 0x200000000, 0x2bf557ffe, 0, 0x37d57fffe}); +typedef Field Field35; +typedef FieldTri, &SQR_TABLE_35, &SQR2_TABLE_35, &SQR4_TABLE_35, &SQR8_TABLE_35, &SQR16_TABLE_35, &QRT_TABLE_35, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri35; +#endif + +#ifdef ENABLE_FIELD_INT_36 +// 36 bit field +typedef RecLinTrans StatTable36; +constexpr StatTable36 SQR_TABLE_36({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x201, 0x804, 0x2010, 0x8040, 0x20100, 0x80400, 0x201000, 0x804000, 0x2010000, 0x8040000, 0x20100000, 0x80400000, 0x201000000, 0x804000000, 0x10000402, 0x40001008, 0x100004020, 0x400010080}); +constexpr StatTable36 SQR2_TABLE_36({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x201, 0x2010, 0x20100, 0x201000, 0x2010000, 0x20100000, 0x201000000, 0x10000402, 0x100004020, 0x40001, 0x400010, 0x4000100, 0x40001000, 0x400010000, 0x100804, 0x1008040, 0x10080400, 0x100804000, 0x8040201, 0x80402010, 0x804020100, 0x40200008, 0x402000080, 0x20000004, 0x200000040, 0x2, 0x20}); +constexpr StatTable36 SQR4_TABLE_36({0x1, 0x10000, 0x100000000, 0x201000, 0x10000402, 0x4000100, 0x1008040, 0x80402010, 0x20000004, 0x200, 0x2000000, 0x4020, 0x40200000, 0x80002, 0x800020000, 0x201008000, 0x80400010, 0x4, 0x40000, 0x400000000, 0x804000, 0x40001008, 0x10000400, 0x4020100, 0x201008040, 0x80000010, 0x800, 0x8000000, 0x10080, 0x100800000, 0x200008, 0x80402, 0x804020000, 0x201000040, 0x10, 0x100000}); +constexpr StatTable36 SQR8_TABLE_36({0x1, 0x80400010, 0x804020000, 0x201008, 0x2000080, 0x20000804, 0x1008000, 0x402, 0x800000, 0x200, 0x80000010, 0x804020100, 0x40201000, 0x400010000, 0x100004, 0x201000000, 0x80400, 0x100000000, 0x40000, 0x10, 0x804000100, 0x40201008, 0x2010080, 0x20000800, 0x200008040, 0x10080000, 0x4020, 0x8000000, 0x2000, 0x800000100, 0x40200008, 0x402010000, 0x100804, 0x1000040, 0x10000402, 0x804000}); +constexpr StatTable36 SQR16_TABLE_36({0x1, 0x402000000, 0x100800020, 0x201000, 0x10080402, 0x800000000, 0x1008040, 0x400000, 0x20000800, 0x200, 0x400010080, 0x100000020, 0x40200000, 0x10080002, 0x20100, 0x201008000, 0x80000000, 0x100804, 0x40000, 0x2000080, 0x20, 0x40001008, 0x10000002, 0x4020000, 0x201008040, 0x2010, 0x20100800, 0x8000000, 0x400010000, 0x4000, 0x200008, 0x2, 0x804000000, 0x201000040, 0x402000, 0x20100804}); +constexpr StatTable36 QRT_TABLE_36({0x40200, 0x8b0526186, 0x8b0526184, 0x240001000, 0x8b0526180, 0xcb6894d94, 0x240001008, 0xdb6880c22, 0x8b0526190, 0x8000200, 0xcb6894db4, 0x500424836, 0x240001048, 0x406cb2834, 0xdb6880ca2, 0x241200008, 0x8b0526090, 0xdb05021a6, 0x8000000, 0xdb01829b2, 0xcb68949b4, 0x1001000, 0x500424036, 0x106116406, 0x240000048, 0xcb29968a4, 0x406cb0834, 0, 0xdb6884ca2, 0x110010516, 0x241208008, 0x430434520, 0x8b0536090, 0x41208040, 0xdb05221a6, 0xb6884d14}); +typedef Field Field36; +typedef FieldTri, &SQR_TABLE_36, &SQR2_TABLE_36, &SQR4_TABLE_36, &SQR8_TABLE_36, &SQR16_TABLE_36, &QRT_TABLE_36, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri36; +#endif + +#ifdef ENABLE_FIELD_INT_37 +// 37 bit field +typedef RecLinTrans StatTable37; +constexpr StatTable37 SQR_TABLE_37({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0xa6, 0x298, 0xa60, 0x2980, 0xa600, 0x29800, 0xa6000, 0x298000, 0xa60000, 0x2980000, 0xa600000, 0x29800000, 0xa6000000, 0x298000000, 0xa60000000, 0x980000053, 0x60000011f, 0x180000047c}); +constexpr StatTable37 SQR2_TABLE_37({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x298, 0x2980, 0x29800, 0x298000, 0x2980000, 0x29800000, 0x298000000, 0x980000053, 0x180000047c, 0x4414, 0x44140, 0x441400, 0x4414000, 0x44140000, 0x441400000, 0x4140000a6, 0x140000ac6, 0x140000ac60, 0xac43e, 0xac43e0, 0xac43e00, 0xac43e000, 0xac43e0000, 0xc43e0011f, 0x43e00101a, 0x3e0010106, 0x1e00101033}); +constexpr StatTable37 SQR4_TABLE_37({0x1, 0x10000, 0x100000000, 0x29800, 0x298000000, 0x44140, 0x441400000, 0xac43e, 0xac43e0000, 0x1e00101033, 0x1010011000, 0x11029a980, 0x9a982b1d3, 0x2b1c45014, 0x4501005f2, 0x1005f8ef80, 0x18efa98941, 0x9897de117, 0x1de10002ad, 0x2990398, 0x190398047c, 0x180443dee4, 0x3ded94ac6, 0x194ac071fa, 0x71c56e1a, 0x56e1adff2, 0x1adffa1690, 0x1a16a9ab31, 0x9ab0957cf, 0x957d85468, 0x18547edba2, 0x1edb9fc515, 0x1fc526c1a4, 0x6c1956aab, 0x156aa5b9d4, 0x5b9f59def, 0x159de6d961}); +constexpr StatTable37 SQR8_TABLE_37({0x1, 0x18efa98941, 0x1fc526c1a4, 0x11352e16c4, 0xba7aa5340, 0x17346e075f, 0xe91c746aa, 0xe560ac1bd, 0xa4544c5d9, 0x11bd3c631f, 0xd70c4b63c, 0xfe77d107c, 0x10548e5288, 0x1183954fb3, 0x19b3aa4bb, 0x782a2943c, 0x1c19ba61de, 0x6ad01fe38, 0xa22701577, 0xb96546ca0, 0x1d7c6c8b9c, 0xffef807e2, 0x16fcc14dc2, 0x110cc4e83c, 0xc3a35629a, 0x1062330476, 0xb2e5d1de1, 0x1ca4e3d229, 0x67826b51b, 0xe7e4c36e7, 0x59f1ac963, 0x12777f22c6, 0x13963d623a, 0x9e305ac92, 0x219b91d13, 0x175bebeb0d, 0xc6b7b5572}); +constexpr StatTable37 SQR16_TABLE_37({0x1, 0xcb88f2f8b, 0x1a2a0be7af, 0xb93048ada, 0x113ed92190, 0xc95a18e2b, 0x1e1cd4a85b, 0x19584a1a66, 0x1b947c28c2, 0x1b52b48e27, 0xe64e7b169, 0x14a256d011, 0xda657196d, 0x1947c1dcb4, 0x18b2fa3851, 0xae3d4171a, 0x658f1f4b9, 0x91852c314, 0x69346cf8e, 0x8224bf36c, 0x1086c810ed, 0x10419bc782, 0x57d6a4e36, 0xfbb31a43e, 0x18b502de05, 0x786795174, 0x1de0f1b7f3, 0x1d456b87dc, 0x1aabb2f3bc, 0xc5b80ef0c, 0x1ce4fd7543, 0x7ca740ca1, 0x29eaec26a, 0x1eb0b42043, 0xca3b2b17, 0x3453101c1, 0x1714c59187}); +constexpr StatTable37 QRT_TABLE_37({0xa3c62e7ba, 0xdc7a0c16a, 0xdc7a0c168, 0x12f7484546, 0xdc7a0c16c, 0xa9803a20, 0x12f748454e, 0xda07064a4, 0xdc7a0c17c, 0x123908de8e, 0xa9803a00, 0x122a888a8e, 0x12f748450e, 0x6790add8, 0xda0706424, 0x12e0a0384c, 0xdc7a0c07c, 0xcb28a2c2, 0x123908dc8e, 0xd09f85e86, 0xa9803e00, 0x124d682b6e, 0x122a88828e, 0x1738711a, 0x12f748550e, 0x73035b8, 0x67908dd8, 0xa0702438, 0xda0702424, 0xe0a0b860, 0x12e0a0b84c, 0x1c7a1c060, 0xdc7a1c07c, 0, 0xcb2aa2c2, 0x100000002c, 0x12390cdc8e}); +typedef Field Field37; +#endif + +#ifdef ENABLE_FIELD_INT_38 +// 38 bit field +typedef RecLinTrans StatTable38; +constexpr StatTable38 SQR_TABLE_38({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x63, 0x18c, 0x630, 0x18c0, 0x6300, 0x18c00, 0x63000, 0x18c000, 0x630000, 0x18c0000, 0x6300000, 0x18c00000, 0x63000000, 0x18c000000, 0x630000000, 0x18c0000000, 0x2300000063, 0xc0000014a, 0x3000000528}); +constexpr StatTable38 SQR2_TABLE_38({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x18c, 0x18c0, 0x18c00, 0x18c000, 0x18c0000, 0x18c00000, 0x18c000000, 0x18c0000000, 0xc0000014a, 0x1405, 0x14050, 0x140500, 0x1405000, 0x14050000, 0x140500000, 0x1405000000, 0x500001ef, 0x500001ef0, 0x100001ef63, 0x1ef7bc, 0x1ef7bc0, 0x1ef7bc00, 0x1ef7bc000, 0x1ef7bc0000, 0x2f7bc00129, 0x37bc00112d, 0x3bc0011027, 0x3c00110022}); +constexpr StatTable38 SQR4_TABLE_38({0x1, 0x10000, 0x100000000, 0x18c00, 0x18c000000, 0x14050, 0x140500000, 0x100001ef63, 0x1ef7bc000, 0x3bc0011027, 0x110001100, 0x110194c0, 0x194c0194c, 0x194d5455, 0xd5455154f, 0x151544a193, 0x4a18c631f, 0xc6319c6ca, 0x19c6c00014, 0x18c8d, 0x18c8d0000, 0xd00014096, 0x1409ddc00, 0x1ddc01efc6, 0x1efd5ab90, 0x15ab9110e1, 0x1110fe85b2, 0x3e85ab5465, 0x2b5445c97a, 0x5c9450993, 0x50994148f, 0x141488b12a, 0x8b134ee36, 0x34ee3a8ecc, 0x3a8ee3edc8, 0x23edeef7ed, 0x2ef7de8bf9, 0x1e8bc14041}); +constexpr StatTable38 SQR8_TABLE_38({0x1, 0x4a18c631f, 0x8b134ee36, 0x10b5c9474c, 0x3330e98ecb, 0x939897650, 0xd74b026b9, 0x860251dd9, 0x3afbe829b4, 0x3ae6afc308, 0x239ecafe00, 0x2acbc94749, 0x3a5770e19e, 0x4052e180b, 0x321fa15712, 0x3a8a4869ef, 0x1948598082, 0x3b1bd98542, 0xc1deb9112, 0x1b5c9242e, 0x338ba58e8b, 0x8abe06d20, 0x145bb1d2a9, 0x1d6e10fbf0, 0x197d522629, 0x2ff1bbe50d, 0xcc1594a16, 0xc94db1b03, 0x3b20e51c56, 0x101d1e5d07, 0x19472478f7, 0x269635a968, 0x2fd4a35802, 0x1b63e116b6, 0x19fdf9d22a, 0x2ef0e4d419, 0x3e80f730f4, 0x29869b04b9}); +constexpr StatTable38 SQR16_TABLE_38({0x1, 0x3f5fe2afaa, 0x4216541b5, 0x33b362f56a, 0x9d630d7e1, 0x11127694c1, 0x3f8daab2d6, 0x153ca20edc, 0x22a747a3de, 0xc6ab16040, 0x19cc9a7e37, 0x449d96001, 0x45a7e7c46, 0x36d11561ce, 0x114b93f52a, 0x42a87f1b3, 0x23112a30bc, 0x400df9212, 0x3aca9544df, 0x140c4b0bcf, 0x2ae2efa6d3, 0x2f7051159c, 0x19cca2f62e, 0x102023d8c0, 0xccc793f0b, 0x2ff4789b55, 0x339e4cd9ba, 0x2b02ab5052, 0x8c1b5db82, 0x2e461e4e32, 0xd93541605, 0x1acf12087, 0x33b88dca2b, 0x1e91723c8b, 0xd81047b2b, 0x2e5e54b97c, 0x85bb507d8, 0x2145b1864b}); +constexpr StatTable38 QRT_TABLE_38({0x34b0ac6430, 0x2223262fa, 0x2223262f8, 0x35554405fe, 0x2223262fc, 0x355514098a, 0x35554405f6, 0x400840, 0x2223262ec, 0x1777726532, 0x35551409aa, 0x15c06fc0, 0x35554405b6, 0x1f5303fec, 0x4008c0, 0x236a21030, 0x2223263ec, 0x1a9008c00, 0x1777726732, 0x3692c60ab6, 0x3555140daa, 0x15556007ee, 0x15c067c0, 0x14a0b030f2, 0x35554415b6, 0x227c06d168, 0x1f5301fec, 0x16c3928fc2, 0x4048c0, 0x3a942c4c0, 0x236a29030, 0x1636a2902e, 0x2223363ec, 0x3a6e898276, 0x1a9028c00, 0x6de74eb2c, 0x1777766732, 0}); +typedef Field Field38; +#endif + +#ifdef ENABLE_FIELD_INT_39 +// 39 bit field +typedef RecLinTrans StatTable39; +constexpr StatTable39 SQR_TABLE_39({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x22, 0x88, 0x220, 0x880, 0x2200, 0x8800, 0x22000, 0x88000, 0x220000, 0x880000, 0x2200000, 0x8800000, 0x22000000, 0x88000000, 0x220000000, 0x880000000, 0x2200000000, 0x800000011, 0x2000000044}); +constexpr StatTable39 SQR2_TABLE_39({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x22, 0x220, 0x2200, 0x22000, 0x220000, 0x2200000, 0x22000000, 0x220000000, 0x2200000000, 0x2000000044, 0x404, 0x4040, 0x40400, 0x404000, 0x4040000, 0x40400000, 0x404000000, 0x4040000000, 0x400000088, 0x4000000880, 0x8888, 0x88880, 0x888800, 0x8888000, 0x88880000, 0x888800000, 0x888000011, 0x880000101, 0x800001001}); +constexpr StatTable39 SQR4_TABLE_39({0x1, 0x10000, 0x100000000, 0x2200, 0x22000000, 0x404, 0x4040000, 0x400000088, 0x888800, 0x888000011, 0x100010, 0x1000100000, 0x1000022000, 0x220022000, 0x220004040, 0x40404040, 0x4040400880, 0x4008888880, 0x888888101, 0x881000001, 0x122, 0x1220000, 0x2200000022, 0x260400, 0x2604000000, 0x48c88, 0x48c880000, 0x800009889, 0x98881000, 0x810001221, 0x12201220, 0x2012200264, 0x2002604264, 0x6042604044, 0x604048c8c4, 0x48c8c8c880, 0x48c8898881, 0x988888881, 0x888802201}); +constexpr StatTable39 SQR8_TABLE_39({0x1, 0x4040400880, 0x2002604264, 0xaa8022011, 0x810049ea9, 0x100100010, 0xc04008101, 0x644048ea4c, 0x18c1764441, 0x60f8e8526c, 0x22000122, 0x48c88989a3, 0xae0032001, 0x2a7aeafae5, 0x6a76641225, 0x2036245242, 0x3e9ab0308b, 0x1c49f6fe41, 0x681b069e2d, 0x4edee8cae5, 0x898c04, 0x660daa8880, 0x69cae9ccc1, 0x4881320991, 0xd06280001, 0x1cc8c8e3d9, 0x445fc65628, 0x4c889a8a49, 0x300b8caeec, 0x50d842fc94, 0x1811acb89d, 0x9d22101c, 0x2025aa407e, 0x20370a744a, 0x3cf77cb80b, 0x54a13e66e7, 0x34c17e2e04, 0x5c19fe54c1, 0x6a72cc767d}); +constexpr StatTable39 SQR16_TABLE_39({0x1, 0x37214861ce, 0x689e897065, 0x5678d6ee60, 0x619da834c4, 0x28352752d3, 0x14fed69ec6, 0x5b3d4aa637, 0x682fb8da4d, 0x2ce48c5615, 0x1591ac539c, 0x72d4fbcd0, 0x346b547296, 0x1e7065d419, 0x4e6eb48571, 0x26615d4c2c, 0x60d1c6122e, 0x78d0e2a2eb, 0x52bb3e2980, 0x3c2592d0ab, 0x701ba76b58, 0x5fdf53b685, 0x57cfd2d120, 0x75559e4344, 0x3837a46907, 0x15f961a4ce, 0x397b9a03e9, 0x5a8dd4ab69, 0x3a6ab3356f, 0x215d39c25e, 0x5bbaf82443, 0x6759e3c88c, 0x3c0b862ca1, 0x37eec7e79e, 0x6ce865e38, 0x4a56a338c0, 0x5684636aee, 0x325a019126, 0x24f18a4ef6}); +constexpr StatTable39 QRT_TABLE_39({0x66b02a408c, 0x100420, 0x100422, 0x14206080, 0x100426, 0x5dccefab1c, 0x14206088, 0x9fc11e5b6, 0x100436, 0x5466bea62a, 0x5dccefab3c, 0x9aa110536, 0x142060c8, 0x54739ed6e2, 0x9fc11e536, 0xe7a82c080, 0x100536, 0x4002000, 0x5466bea42a, 0x6a4022000, 0x5dccefaf3c, 0x9e8118536, 0x9aa110d36, 0x5680e080, 0x142070c8, 0x7d293c5b6, 0x54739ef6e2, 0x8d680e080, 0x9fc11a536, 0x6d282c080, 0xe7a824080, 0x800000000, 0x110536, 0x2d680e080, 0x4022000, 0, 0x5466baa42a, 0x46b03a44aa, 0x6a40a2000}); +typedef Field Field39; +typedef FieldTri, &SQR_TABLE_39, &SQR2_TABLE_39, &SQR4_TABLE_39, &SQR8_TABLE_39, &SQR16_TABLE_39, &QRT_TABLE_39, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri39; +#endif + +#ifdef ENABLE_FIELD_INT_40 +// 40 bit field +typedef RecLinTrans StatTable40; +constexpr StatTable40 SQR_TABLE_40({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x39, 0xe4, 0x390, 0xe40, 0x3900, 0xe400, 0x39000, 0xe4000, 0x390000, 0xe40000, 0x3900000, 0xe400000, 0x39000000, 0xe4000000, 0x390000000, 0xe40000000, 0x3900000000, 0xe400000000, 0x900000004b, 0x400000015e}); +constexpr StatTable40 SQR2_TABLE_40({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x39, 0x390, 0x3900, 0x39000, 0x390000, 0x3900000, 0x39000000, 0x390000000, 0x3900000000, 0x900000004b, 0x541, 0x5410, 0x54100, 0x541000, 0x5410000, 0x54100000, 0x541000000, 0x5410000000, 0x41000000dd, 0x1000000d34, 0xd379, 0xd3790, 0xd37900, 0xd379000, 0xd3790000, 0xd37900000, 0xd379000000, 0x3790000115, 0x790000111b, 0x900001111f}); +constexpr StatTable40 SQR4_TABLE_40({0x1, 0x10000, 0x100000000, 0x3900, 0x39000000, 0x541, 0x5410000, 0x41000000dd, 0xd37900, 0xd379000000, 0x111001, 0x1110010000, 0x10003aa90, 0x3aa903900, 0x903900511a, 0x51051541, 0x515410de9, 0x410de9de4d, 0xe9de437815, 0x437801010e, 0x101000038, 0x383939, 0x3839390000, 0x3900057d41, 0x57d444100, 0x444100d6e5, 0xd6ebaa79, 0xebaa7911c6, 0x7911d2791a, 0xd2791102a9, 0x1102b82901, 0xb82902a972, 0x2a96bfed1, 0x6bfed16851, 0xd16859f42e, 0x59f43f61a8, 0x3f61a43794, 0xa43791de59, 0x91de42401f, 0x424000390e}); +constexpr StatTable40 SQR8_TABLE_40({0x1, 0x515410de9, 0x2a96bfed1, 0x13ba41ea90, 0x45bffe2b75, 0x5836900, 0x3887d7e690, 0xd34b688712, 0xc7a3d51557, 0xd1151ada71, 0x51442a740, 0x41cc5cbdb6, 0xc61a5701e9, 0x8757946d91, 0xa99e8b9e65, 0x80a0aca777, 0xc242b5c0e9, 0x6826eccb25, 0xad687ebd2d, 0xad5c69d802, 0x7ed2f8390, 0x51fa78eedf, 0xc0718c96f6, 0xaf4672a8c2, 0xc67436f2fd, 0x56ddb12767, 0x535afb0326, 0xbce1edda33, 0xef36202f0f, 0x45d13015ec, 0x104ab11aef, 0xef96c86d49, 0xc1b790bfc9, 0x2fa610e77f, 0x2a10a27d6e, 0xca5bb10773, 0xfdaf2b4642, 0xb3b4b7e20d, 0xe8bbe4d22e, 0xf9986bd2df}); +constexpr StatTable40 SQR16_TABLE_40({0x1, 0xe88450a7de, 0x3a0a56c3e8, 0x1684757d36, 0xc7f40bf3e9, 0x38aa7009c0, 0x2b6f129659, 0xd1e0fc42e5, 0x96150bc554, 0x9774ef4cc1, 0xd34eebf74d, 0x2d183441ec, 0xeedf6d1c78, 0x3f93c5d217, 0xb924305809, 0xc383bb7c14, 0x3f242bb94e, 0x9313556f6b, 0x2f5e1ecc6b, 0x2e7f9df195, 0xac8b882870, 0xd14f457f55, 0xf9f936148d, 0x719190770, 0x6838b41a21, 0xb95ff30106, 0xc1527dd1c5, 0xe858b5f9b6, 0x9368a791c2, 0x7de23878af, 0x95c610d398, 0xed0edcb032, 0x9548a680b0, 0xc133469e7b, 0x68c96ccbb2, 0x7773231ebb, 0xbd5ef4207c, 0xdf8bd59374, 0xb862414268, 0xfa62b39e42}); +constexpr StatTable40 QRT_TABLE_40({0x624b3cecc, 0xbc5c3f4c6, 0xbc5c3f4c4, 0xde1603e2c, 0xbc5c3f4c0, 0xaabec06cea, 0xde1603e24, 0x6cd9f724c2, 0xbc5c3f4d0, 0xcde1743818, 0xaabec06cca, 0xa138c314ca, 0xde1603e64, 0xaafc00f01a, 0x6cd9f72442, 0xcdca11bb4, 0xbc5c3f5d0, 0xa00002001a, 0xcde1743a18, 0xdf1407b90, 0xaabec068ca, 0xc043b482c8, 0xa138c31cca, 0xcb86977e3c, 0xde1602e64, 0x604596a326, 0xaafc00d01a, 0xcc1c165d0, 0x6cd9f76442, 0x673c94da26, 0xcdca19bb4, 0x67c0940a26, 0xbc5c2f5d0, 0xa4dca19bae, 0xa00000001a, 0x1bc5c2f5d0, 0xcde1703a18, 0, 0xdf1487b90, 0x8df1487b8a}); +typedef Field Field40; +#endif +} + +Sketch* ConstructClMul5Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_33 + case 33: return new SketchImpl(implementation, 33); +#endif +#ifdef ENABLE_FIELD_INT_34 + case 34: return new SketchImpl(implementation, 34); +#endif +#ifdef ENABLE_FIELD_INT_35 + case 35: return new SketchImpl(implementation, 35); +#endif +#ifdef ENABLE_FIELD_INT_36 + case 36: return new SketchImpl(implementation, 36); +#endif +#ifdef ENABLE_FIELD_INT_37 + case 37: return new SketchImpl(implementation, 37); +#endif +#ifdef ENABLE_FIELD_INT_38 + case 38: return new SketchImpl(implementation, 38); +#endif +#ifdef ENABLE_FIELD_INT_39 + case 39: return new SketchImpl(implementation, 39); +#endif +#ifdef ENABLE_FIELD_INT_40 + case 40: return new SketchImpl(implementation, 40); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri5Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_33 + case 33: return new SketchImpl(implementation, 33); +#endif +#ifdef ENABLE_FIELD_INT_34 + case 34: return new SketchImpl(implementation, 34); +#endif +#ifdef ENABLE_FIELD_INT_35 + case 35: return new SketchImpl(implementation, 35); +#endif +#ifdef ENABLE_FIELD_INT_36 + case 36: return new SketchImpl(implementation, 36); +#endif +#ifdef ENABLE_FIELD_INT_39 + case 39: return new SketchImpl(implementation, 39); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_6bytes.cpp b/src/fields/clmul_6bytes.cpp new file mode 100644 index 0000000000..d0e712400a --- /dev/null +++ b/src/fields/clmul_6bytes.cpp @@ -0,0 +1,170 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_6) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_41 +// 41 bit field +typedef RecLinTrans StatTable41; +constexpr StatTable41 SQR_TABLE_41({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x4800000, 0x12000000, 0x48000000, 0x120000000, 0x480000000, 0x1200000000, 0x4800000000, 0x12000000000, 0x8000000012}); +constexpr StatTable41 SQR2_TABLE_41({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x48, 0x480, 0x4800, 0x48000, 0x480000, 0x4800000, 0x48000000, 0x480000000, 0x4800000000, 0x8000000012, 0x104, 0x1040, 0x10400, 0x104000, 0x1040000, 0x10400000, 0x104000000, 0x1040000000, 0x10400000000, 0x4000000048, 0x492, 0x4920, 0x49200, 0x492000, 0x4920000, 0x49200000, 0x492000000, 0x4920000000, 0x9200000012, 0x12000000104}); +constexpr StatTable41 SQR4_TABLE_41({0x1, 0x10000, 0x100000000, 0x480, 0x4800000, 0x8000000012, 0x104000, 0x1040000000, 0x4920, 0x49200000, 0x12000000104, 0x1001000, 0x10010000000, 0x48048, 0x480480000, 0x4800001040, 0x10410400, 0x4104000048, 0x492492, 0x4924920000, 0x9200010002, 0x100000100, 0x1000480, 0x10004800000, 0x8000048012, 0x480104000, 0x1040001040, 0x10404920, 0x4049200048, 0x12000492104, 0x4921001000, 0x10010010010, 0x100148048, 0x1480480480, 0x4804805840, 0x8058410412, 0x410410414c, 0x10414d2492, 0x14d24924920, 0x9249259202, 0x12592000004}); +constexpr StatTable41 SQR8_TABLE_41({0x1, 0x10410400, 0x100148048, 0x13040000104, 0x11044801040, 0x924da49202, 0x9680490002, 0x82510514c, 0x8481485932, 0xc83144d832, 0x134d34d6db6, 0x18010048012, 0x165db20004c, 0x4800597052, 0x10131135cd0, 0xcd6cc30d32, 0x160586101cc, 0x15c64969da8, 0x179715681cc, 0x12f3c0ffc74, 0xc7dd3dd3ce, 0x10014c968, 0x1b040048116, 0x35d6801044, 0xda4deda6d0, 0x1de94c85852, 0x1083500114c, 0xc4c9685dfa, 0x18515c6d592, 0x17de69aed7e, 0x16c6c8a6c6c, 0x165cfe1044c, 0xdb004cf018, 0x7075031c98, 0x1d9a90b0d72, 0x1bb2485caee, 0x1cbe4dfd48a, 0x1f1540b7400, 0xc62bc7fd02, 0x147b5103f2e, 0x390ee8bcc6}); +constexpr StatTable41 SQR16_TABLE_41({0x1, 0x61deee38fe, 0xe00adae2e, 0x1ea53eaa95a, 0x503e540566, 0xabc8e7f89a, 0x1bf760d86ac, 0x94cce9c722, 0x15c8006ee5c, 0x7aba20c1da, 0x12662a9603e, 0x5fe76acec4, 0x1e6beca9e42, 0x1efc8f7a000, 0x165997c6d7e, 0xee947a07ee, 0xd9bd741142, 0xaa304566c0, 0x5fe336e356, 0x11f1021b80c, 0xd34e5a1674, 0x99ed56b9dc, 0x9afae0eca, 0x1a5830b390e, 0x1be1a63eb7e, 0x141e77e141c, 0xee3be92168, 0xa93823d65c, 0x18a59f4b19c, 0xce69942af6, 0x3f7b319c0e, 0xba83a4a7b4, 0x7da4b6fcde, 0x17f79268f10, 0x1222602d048, 0x1b4b2f326b8, 0x159abff0786, 0xb35534a7a2, 0x84bbc48050, 0x173d5cbf330, 0x2897dd6f58}); +constexpr StatTable41 QRT_TABLE_41({0, 0x1599a5e0b0, 0x1599a5e0b2, 0x105c119e0, 0x1599a5e0b6, 0x1a2030452a6, 0x105c119e8, 0x1a307c55b2e, 0x1599a5e0a6, 0x1ee3f47bc8e, 0x1a203045286, 0x400808, 0x105c119a8, 0x1a3038573a6, 0x1a307c55bae, 0x4d2882a520, 0x1599a5e1a6, 0x1ffbaa0b720, 0x1ee3f47be8e, 0x4d68c22528, 0x1a203045686, 0x200006, 0x400008, 0x1b79a21b200, 0x105c109a8, 0x1ef3886a526, 0x1a3038553a6, 0x1b692209200, 0x1a307c51bae, 0x5d99a4e1a6, 0x4d28822520, 0x185e109ae, 0x1599a4e1a6, 0x4e3f43be88, 0x1ffbaa2b720, 0x4000000000, 0x1ee3f43be8e, 0x18000000006, 0x4d68ca2528, 0xa203145680, 0x1a203145686}); +typedef Field Field41; +typedef FieldTri, &SQR_TABLE_41, &SQR2_TABLE_41, &SQR4_TABLE_41, &SQR8_TABLE_41, &SQR16_TABLE_41, &QRT_TABLE_41, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri41; +#endif + +#ifdef ENABLE_FIELD_INT_42 +// 42 bit field +typedef RecLinTrans StatTable42; +constexpr StatTable42 SQR_TABLE_42({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x81, 0x204, 0x810, 0x2040, 0x8100, 0x20400, 0x81000, 0x204000, 0x810000, 0x2040000, 0x8100000, 0x20400000, 0x81000000, 0x204000000, 0x810000000, 0x2040000000, 0x8100000000, 0x20400000000, 0x1000000102, 0x4000000408, 0x10000001020}); +constexpr StatTable42 SQR2_TABLE_42({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x204, 0x2040, 0x20400, 0x204000, 0x2040000, 0x20400000, 0x204000000, 0x2040000000, 0x20400000000, 0x4000000408, 0x4001, 0x40010, 0x400100, 0x4001000, 0x40010000, 0x400100000, 0x4001000000, 0x10000081, 0x100000810, 0x1000008100, 0x10000081000, 0x810204, 0x8102040, 0x81020400, 0x810204000, 0x8102040000, 0x1020400102, 0x10204001020, 0x2040010004, 0x20400100040, 0x4001000008}); +constexpr StatTable42 SQR4_TABLE_42({0x1, 0x10000, 0x100000000, 0x2040, 0x20400000, 0x4000000408, 0x4001000, 0x10000081, 0x810204, 0x8102040000, 0x20400100040, 0x1000000100, 0x1020400, 0x10204000000, 0x200001, 0x2000010000, 0x100040800, 0x408002040, 0x20408002, 0x4080020408, 0x204000020, 0x204001, 0x2040010000, 0x100040010, 0x400102040, 0x1020408100, 0x4081020008, 0x10200000020, 0x80, 0x800000, 0x8000000000, 0x102000, 0x1020000000, 0x20008, 0x200080000, 0x800004080, 0x40810200, 0x8102000810, 0x20008000040, 0x8102, 0x81020000, 0x10200001020}); +constexpr StatTable42 SQR8_TABLE_42({0x1, 0x100040800, 0x1020000000, 0x10200001000, 0x2040810000, 0x408102040, 0x4080000408, 0x10000000, 0x8002040810, 0x20008000, 0x10200080000, 0x40000004, 0x20408000040, 0x4000020000, 0x204000, 0x8002040010, 0x408102, 0x200081020, 0x40810000, 0x2000, 0x81000408, 0x4001, 0x2040010, 0x1020008002, 0x10204081020, 0x800004, 0x20000000000, 0x4081000400, 0x10000081, 0x100040010, 0x8102, 0x10000000020, 0x40010200, 0x408000000, 0x4080000400, 0x810204000, 0x102040810, 0x1020000102, 0x4000000, 0x2000810204, 0x8002000, 0x4080020000}); +constexpr StatTable42 SQR16_TABLE_42({0x1, 0x40800204, 0x2000800, 0x20008002040, 0x408100, 0x20000, 0x10004081020, 0x10000081, 0x40010204, 0x8102000000, 0x400000000, 0x1020008102, 0x1020408, 0x204081020, 0x200001, 0x10200, 0x102000010, 0x20408100000, 0x1020408002, 0x4000020000, 0x204000000, 0x204001, 0x2000800004, 0x8100000010, 0x400102000, 0x8002, 0x4080020000, 0x10000001000, 0x80, 0x2040010200, 0x100040000, 0x400100040, 0x20408000, 0x1000000, 0x204080020, 0x800004080, 0x2000810200, 0x8100000810, 0x20000000000, 0x1000408002, 0x81020400, 0x10204081000}); +constexpr StatTable42 QRT_TABLE_42({0x810200080, 0x120810806, 0x120810804, 0x1068c1a1000, 0x120810800, 0x34005023008, 0x1068c1a1008, 0x800004080, 0x120810810, 0x162818a10, 0x34005023028, 0x42408a14, 0x1068c1a1048, 0x1001040, 0x800004000, 0xb120808906, 0x120810910, 0x34000020068, 0x162818810, 0x68c021400, 0x34005023428, 0x10004000, 0x42408214, 0x162418214, 0x1068c1a0048, 0xb002018116, 0x1003040, 0x10008180448, 0x800000000, 0x62c08b04, 0xb120800906, 0x2408d1a3060, 0x120800910, 0x34401003028, 0x34000000068, 0, 0x162858810, 0xa042058116, 0x68c0a1400, 0x8162858806, 0x34005123428, 0x3068c0a1468}); +typedef Field Field42; +typedef FieldTri, &SQR_TABLE_42, &SQR2_TABLE_42, &SQR4_TABLE_42, &SQR8_TABLE_42, &SQR16_TABLE_42, &QRT_TABLE_42, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri42; +#endif + +#ifdef ENABLE_FIELD_INT_43 +// 43 bit field +typedef RecLinTrans StatTable43; +constexpr StatTable43 SQR_TABLE_43({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0xb2, 0x2c8, 0xb20, 0x2c80, 0xb200, 0x2c800, 0xb2000, 0x2c8000, 0xb20000, 0x2c80000, 0xb200000, 0x2c800000, 0xb2000000, 0x2c8000000, 0xb20000000, 0x2c80000000, 0xb200000000, 0x2c800000000, 0x32000000059, 0x4800000013d, 0x20000000446}); +constexpr StatTable43 SQR2_TABLE_43({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0xb2, 0xb20, 0xb200, 0xb2000, 0xb20000, 0xb200000, 0xb2000000, 0xb20000000, 0xb200000000, 0x32000000059, 0x20000000446, 0x4504, 0x45040, 0x450400, 0x4504000, 0x45040000, 0x450400000, 0x4504000000, 0x45040000000, 0x504000002c8, 0x4000002efa, 0x4000002efa0, 0x2ef8c8, 0x2ef8c80, 0x2ef8c800, 0x2ef8c8000, 0x2ef8c80000, 0x2ef8c800000, 0x6f8c800013d, 0x78c80001025, 0xc800010117, 0x48000101129}); +constexpr StatTable43 SQR4_TABLE_43({0x1, 0x10000, 0x100000000, 0xb20, 0xb200000, 0x32000000059, 0x450400, 0x4504000000, 0x4000002efa0, 0x2ef8c8000, 0x78c80001025, 0x10110010, 0x11001000b2, 0x1000b2b920, 0xb2b920b200, 0x120b204545f, 0x20454554046, 0x45540506efa, 0x506ed4df68, 0x6d4df6a79f5, 0x76a79c80133, 0x1c801010007, 0x101000b2100, 0xb210b2b20, 0x10b2b204504, 0x320450f655d, 0x50f654106c8, 0x54106efcb4c, 0x6efcb696320, 0x369631c93e1, 0x31c93ff9d8c, 0x3ff9d91a2a2, 0x591a2b9839b, 0x2b983b98dd4, 0x3b98dc651b0, 0x5c651a971e9, 0x1a971c9c0ba, 0x1c9c0b5853e, 0xb585322d78, 0x5322d7c6430, 0x57c6416617d, 0x4166159c82c, 0x159c8000b6c}); +constexpr StatTable43 SQR8_TABLE_43({0x1, 0x20454554046, 0x591a2b9839b, 0x722ff9f6fe9, 0x6a269c1eb12, 0xa3ce9f234e, 0x4d9ba8aae0b, 0x392cf0cf99b, 0x465f8594525, 0x4f9c1fb1524, 0x3b1a1dd441c, 0x381edd42255, 0x37a599424b, 0x554caee8670, 0x5335bb91d81, 0x69288c8a1a3, 0x3df2b6e68e5, 0x75330d31d56, 0x51a604b090c, 0x32e0d5a7ca3, 0x41eb9d4896e, 0x633e2855c9f, 0x4780d70e32f, 0x73b0cd728c3, 0x16627402bad, 0x4418f2a818a, 0x5cdd06cf7e5, 0x2a8da97a3ae, 0x446864a8976, 0x5a7bcbd45ea, 0x4034a4b8b04, 0x6bdaac9c5fa, 0x18769ce3a67, 0x560278257c, 0x41c06d6b64c, 0x69f6f61bd4b, 0x16cc45f84fd, 0x53f6b42f0f0, 0x6cac3d234f3, 0x1f94e8f24d5, 0x319342c7148, 0x8685bca86d, 0x6b694a6ea66}); +constexpr StatTable43 SQR16_TABLE_43({0x1, 0x1ce77599049, 0x191715250a, 0xc1573d8dff, 0x118e73ab5e4, 0x4b6a83225fe, 0x72b4bc8e0f5, 0x4a4b2b6bb02, 0x66daf4741e9, 0x50baba19898, 0x5eb38771912, 0x6fb458aad3c, 0x5ce3b10bde9, 0x5575f3498f0, 0x5f075aa8a0a, 0x41d0aa8ee20, 0x609e3c78c28, 0xe2e45a8018, 0x523ac062837, 0x738388a569d, 0x6616ec46da9, 0x1a75cc16d96, 0x49b0b43bbc3, 0x400416b3c9a, 0x25813f41ffe, 0x309fdb9d0bc, 0x489f45b2cbf, 0xa141f4f88e, 0x739e0d11fb3, 0x44971f51cc0, 0x6490576e60e, 0x6c6674c5355, 0x6978126a4e1, 0x3d04eae5a5, 0x312eed633f2, 0x1de4b98d6b9, 0x118a106fb0a, 0x26dae025f4, 0x5c179312ebb, 0x75870ef1921, 0x60e9fed95c0, 0x209ab92427a, 0x1c5014a1937}); +constexpr StatTable43 QRT_TABLE_43({0x2bccc2d6f6c, 0x4bccc2d6f54, 0x4bccc2d6f56, 0x7cc7bc61df0, 0x4bccc2d6f52, 0x7d13b404b10, 0x7cc7bc61df8, 0x37456e9ac5a, 0x4bccc2d6f42, 0x4e042c6a6, 0x7d13b404b30, 0x4a56de9ef4c, 0x7cc7bc61db8, 0x14bc18d8e, 0x37456e9acda, 0x7c89f84fb1e, 0x4bccc2d6e42, 0x7ffae40d210, 0x4e042c4a6, 0x366f45dd06, 0x7d13b404f30, 0x496fcaf8cca, 0x4a56de9e74c, 0x370b62b6af4, 0x7cc7bc60db8, 0x1498185a8, 0x14bc1ad8e, 0x7e602c46a98, 0x37456e9ecda, 0x36ccc2c6e74, 0x7c89f847b1e, 0x7e27d06d516, 0x4bccc2c6e42, 0x7f93302c396, 0x7ffae42d210, 0x3dd3440706, 0x4e046c4a6, 0x78bbc09da36, 0x366f4ddd06, 0, 0x7d13b504f30, 0x8bbc09da00, 0x496fc8f8cca}); +typedef Field Field43; +#endif + +#ifdef ENABLE_FIELD_INT_44 +// 44 bit field +typedef RecLinTrans StatTable44; +constexpr StatTable44 SQR_TABLE_44({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x21, 0x84, 0x210, 0x840, 0x2100, 0x8400, 0x21000, 0x84000, 0x210000, 0x840000, 0x2100000, 0x8400000, 0x21000000, 0x84000000, 0x210000000, 0x840000000, 0x2100000000, 0x8400000000, 0x21000000000, 0x84000000000, 0x10000000042, 0x40000000108}); +constexpr StatTable44 SQR2_TABLE_44({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x21, 0x210, 0x2100, 0x21000, 0x210000, 0x2100000, 0x21000000, 0x210000000, 0x2100000000, 0x21000000000, 0x10000000042, 0x401, 0x4010, 0x40100, 0x401000, 0x4010000, 0x40100000, 0x401000000, 0x4010000000, 0x40100000000, 0x1000000084, 0x10000000840, 0x8421, 0x84210, 0x842100, 0x8421000, 0x84210000, 0x842100000, 0x8421000000, 0x84210000000, 0x42100000108, 0x21000001004, 0x10000010002}); +constexpr StatTable44 SQR4_TABLE_44({0x1, 0x10000, 0x100000000, 0x210, 0x2100000, 0x21000000000, 0x40100, 0x401000000, 0x10000000840, 0x8421000, 0x84210000000, 0x100001, 0x1000010000, 0x100002100, 0x21000210, 0x10002100042, 0x21000401000, 0x4010040100, 0x401008421, 0x10084210840, 0x42108421108, 0x84211000010, 0x10000000001, 0x31000, 0x310000000, 0x611, 0x6110000, 0x61100000000, 0xc4310, 0xc43100000, 0x31000001844, 0x18421100, 0x84211000021, 0x10000310001, 0x3100031000, 0x310006110, 0x61100611, 0x110061100c6, 0x61100c43100, 0xc4310c4310, 0x10c43118423, 0x31184210844, 0x42108421218, 0x84212100010}); +constexpr StatTable44 SQR8_TABLE_44({0x1, 0x21000401000, 0x84211000021, 0xa521000, 0x42108421719, 0x311e5310e55, 0x10401008c61, 0x3210031000, 0x10c43058522, 0x74110050101, 0xf5312e97201, 0x42108421109, 0x21061501611, 0x94311002961, 0xf59421000, 0x52d4b56923a, 0x201e5300e54, 0x71501c8fe71, 0xb6002131043, 0xb5e4c168432, 0xb320f5619ae, 0xf5000f97201, 0x10c43118422, 0x24010451100, 0x942113d4330, 0x8421a521001, 0x6310e130719, 0xf72fc731c6c, 0x4ae739631, 0x70008417e4d, 0x20ca6358b77, 0x64110094a51, 0xd4002e97200, 0xf7f5a13853a, 0xb12664417f6, 0x843322d6860, 0xf7c4100194d, 0x7382d17842b, 0xf67fd71a10c, 0x6efcca4b731, 0xf4e5901ea2d, 0xcb8278a46, 0xa32050401ee, 0xb7218bb6518}); +constexpr StatTable44 SQR16_TABLE_44({0x1, 0xc6cdb660138, 0x13de5a69a7b, 0x80bcafe7981, 0x60eb6f976d1, 0x677fbef6cce, 0x1549bb4cdec, 0x3b1ddf6859, 0xc01b8da28a6, 0xf3e11efbf8c, 0xd3e6faf8ee3, 0xa3dbc5712c8, 0x72361d7ca84, 0xe59e509337d, 0x15fca12a6f4, 0x33ce445498c, 0x44406de91fb, 0x9784b690571, 0xb0fb81753af, 0xb53a7c2c977, 0x34fbd3dba9b, 0xc758c22e647, 0xd5ff69aa469, 0x41e6d42b47d, 0xa4d1a3d02e7, 0x365db54ae9f, 0xd2293b8770b, 0xf1bf95c7746, 0x337fbe1d950, 0x726879e26a7, 0xa4be5ec2171, 0x7080da9df82, 0x7560017ce2, 0xd03997e34ae, 0x27ad4309a78, 0xb7b0ead892b, 0xf45bedb915d, 0xc4f0e25a52c, 0xe774a9d7fe8, 0xece6c1d7a26, 0xf20ea9ab655, 0x159bb624dc2, 0x12f2780b45f, 0x840cc52f19d}); +constexpr StatTable44 QRT_TABLE_44({0xf05334f4f6e, 0x4002016, 0x4002014, 0xf04350e6246, 0x4002010, 0x4935b379a26, 0xf04350e624e, 0xf84250c228e, 0x4002000, 0xf04300e521e, 0x4935b379a06, 0xb966838dd48, 0xf04350e620e, 0xf7b8b80feda, 0xf84250c220e, 0xf972e097d5e, 0x4002100, 0x8000020000, 0xf04300e501e, 0x430025000, 0x4935b379e06, 0xf976a09dc5e, 0xb966838d548, 0xf84218c029a, 0xf04350e720e, 0x4925f36bf06, 0xf7b8b80deda, 0xb047d3ee758, 0xf84250c620e, 0xf80350e720e, 0xf972e09fd5e, 0x8091825284, 0x4012100, 0x9015063210, 0x8000000000, 0xff31a028c5e, 0xf04300a501e, 0x44340b7100, 0x4300a5000, 0, 0x4935b279e06, 0xa976b2dce18, 0xf976a29dc5e, 0x8935b279e18}); +typedef Field Field44; +typedef FieldTri, &SQR_TABLE_44, &SQR2_TABLE_44, &SQR4_TABLE_44, &SQR8_TABLE_44, &SQR16_TABLE_44, &QRT_TABLE_44, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri44; +#endif + +#ifdef ENABLE_FIELD_INT_45 +// 45 bit field +typedef RecLinTrans StatTable45; +constexpr StatTable45 SQR_TABLE_45({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x36, 0xd8, 0x360, 0xd80, 0x3600, 0xd800, 0x36000, 0xd8000, 0x360000, 0xd80000, 0x3600000, 0xd800000, 0x36000000, 0xd8000000, 0x360000000, 0xd80000000, 0x3600000000, 0xd800000000, 0x36000000000, 0xd8000000000, 0x16000000001b, 0x18000000005a}); +constexpr StatTable45 SQR2_TABLE_45({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0xd8, 0xd80, 0xd800, 0xd8000, 0xd80000, 0xd800000, 0xd8000000, 0xd80000000, 0xd800000000, 0xd8000000000, 0x18000000005a, 0x514, 0x5140, 0x51400, 0x514000, 0x5140000, 0x51400000, 0x514000000, 0x5140000000, 0x51400000000, 0x114000000036, 0x1400000003b8, 0x3b6e, 0x3b6e0, 0x3b6e00, 0x3b6e000, 0x3b6e0000, 0x3b6e00000, 0x3b6e000000, 0x3b6e0000000, 0x1b6e0000001b, 0x16e00000011f, 0xe0000001105}); +constexpr StatTable45 SQR4_TABLE_45({0x1, 0x10000, 0x100000000, 0xd8, 0xd80000, 0xd800000000, 0x5140, 0x51400000, 0x114000000036, 0x3b6e00, 0x3b6e000000, 0xe0000001105, 0x11011000, 0x110110000000, 0x1000000d58d8, 0xd58d58000, 0x18d58000054e, 0x5451454, 0x54514540000, 0x145400038db8, 0x38db6d8e0, 0xdb6d8e00104, 0x18e00101000a, 0x10100010100, 0x10100d8d8, 0x100d8d800d8, 0x18d800d8d85a, 0xd8d8511140, 0x18511140511a, 0x114051117b58, 0x11117b556e36, 0x1b556e3b5575, 0xe3b557f1015, 0x157f1011011e, 0x101101101c48, 0x1101c458d58, 0x1c458d58d580, 0xd58d58815d4, 0x158815d1451a, 0x15d1451452c0, 0x51452ce6f6e, 0x12ce6f6db6d6, 0xf6db6da6e3d, 0x16da6e39e00f, 0xe39e00000dd}); +constexpr StatTable45 SQR8_TABLE_45({0x1, 0x18d58000054e, 0xe3b557f1015, 0x51985198, 0xdb68cb55452, 0x1d0cc1d84f02, 0x140110c19ae, 0x11a16a14e7fe, 0x1e7ca7c62aa9, 0xe0eae26629b, 0x12182bee80ab, 0x1a38a68f28d3, 0x8581419c8c, 0x1d47f6f12ebc, 0x19fd34c3806e, 0x12ddba59f3cd, 0x10fa07f12a0e, 0x1d93eb544486, 0x1cf42cd119be, 0x1ff32d4b62c3, 0xf34ae031191, 0xada837715bf, 0xd368a753f92, 0x2ba87b17a03, 0x10374c3e4088, 0x1a6f539c11bd, 0x16548a5473c7, 0x1eb70011a8c9, 0x1ee5435ba1a3, 0x1173c0537680, 0xa1a3668dd6b, 0x119faad25e8, 0xd3909240e00, 0x1b560d018881, 0x127ecb9095ed, 0x306b507e701, 0x12b934c21ea3, 0x1a9d258c5b8b, 0x10452fbf0b1c, 0xae92fee120d, 0x183eb4b419fa, 0xc24d2391313, 0x4e6c4746f6, 0x2815fe7c395, 0xe4ab383747f}); +constexpr StatTable45 SQR16_TABLE_45({0x1, 0x14af92df932, 0x484e0190bdc, 0xda69889e16e, 0xcf70dfdb150, 0x18c6743571a8, 0x1b2c3ad7fa79, 0x5f0cbe204f6, 0xee973392a75, 0x3e86ef79673, 0xb2a9bef7181, 0x19b5347ff116, 0x1cae0ec79856, 0x69093f18f81, 0x1964382be09a, 0x92c894b073e, 0x1d99d2922eb2, 0x647905ad0eb, 0x1695971acdd3, 0x8f3292bc8c4, 0x1ee4057ad94, 0x17f02dc60e01, 0x1bb8e05ab4d5, 0x14de5d2a05d6, 0x13a019a02983, 0xcd7097c3616, 0x1bd798639b8f, 0x1cf0ca5ac7b2, 0xa93b983cf05, 0x159a955a2aa8, 0x69e5ba33397, 0x3a6b3392237, 0x26aeab71e13, 0x26fe04d38b9, 0x1fa9df0e8c45, 0x104e85c234b0, 0x1792853f8767, 0x81573b15f20, 0x127d6bfb06d3, 0x8110e6957e8, 0x11f59cbcc110, 0xad68264cad8, 0x61438575b35, 0x56e4446dc, 0x1cc9cb28b150}); +constexpr StatTable45 QRT_TABLE_45({0xede34e3e0fc, 0x1554148191aa, 0x1554148191a8, 0x1767be1dc4a6, 0x1554148191ac, 0x26bd4931492, 0x1767be1dc4ae, 0x233ab9c454a, 0x1554148191bc, 0x16939e8bb3dc, 0x26bd49314b2, 0x3c6ca8bac52, 0x1767be1dc4ee, 0x16caa5054c16, 0x233ab9c45ca, 0x14a1649628bc, 0x1554148190bc, 0x3c382881252, 0x16939e8bb1dc, 0x3c7ca0aa160, 0x26bd49310b2, 0x27f40158000, 0x3c6ca8ba452, 0x173fc092853c, 0x1767be1dd4ee, 0x16cbe284f25c, 0x16caa5056c16, 0x155559002f96, 0x233ab9c05ca, 0x26eb8908b32, 0x14a16496a8bc, 0x15440885333c, 0x1554148090bc, 0x17d60702e0, 0x3c3828a1252, 0x54548d10b2, 0x16939e8fb1dc, 0x3ac1e81b1d2, 0x3c7ca02a160, 0x166bd48310bc, 0x26bd48310b2, 0, 0x27f40358000, 0x10000000000e, 0x3c6cacba452}); +typedef Field Field45; +#endif + +#ifdef ENABLE_FIELD_INT_46 +// 46 bit field +typedef RecLinTrans StatTableTRI46; +constexpr StatTableTRI46 SQR_TABLE_TRI46({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000, 0xc0000000, 0x300000000, 0xc00000000, 0x3000000000, 0xc000000000, 0x30000000000, 0xc0000000000, 0x300000000000}); +constexpr StatTableTRI46 SQR2_TABLE_TRI46({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0xc, 0xc0, 0xc00, 0xc000, 0xc0000, 0xc00000, 0xc000000, 0xc0000000, 0xc00000000, 0xc000000000, 0xc0000000000, 0x5, 0x50, 0x500, 0x5000, 0x50000, 0x500000, 0x5000000, 0x50000000, 0x500000000, 0x5000000000, 0x50000000000, 0x100000000003, 0x3c, 0x3c0, 0x3c00, 0x3c000, 0x3c0000, 0x3c00000, 0x3c000000, 0x3c0000000, 0x3c00000000, 0x3c000000000, 0x3c0000000000}); +constexpr StatTableTRI46 SQR4_TABLE_TRI46({0x1, 0x10000, 0x100000000, 0xc, 0xc0000, 0xc00000000, 0x50, 0x500000, 0x5000000000, 0x3c0, 0x3c00000, 0x3c000000000, 0x1100, 0x11000000, 0x110000000000, 0xcc00, 0xcc000000, 0xc0000000005, 0x55000, 0x550000000, 0x10000000003f, 0x3fc000, 0x3fc0000000, 0x101, 0x1010000, 0x10100000000, 0xc0c, 0xc0c0000, 0xc0c00000000, 0x5050, 0x50500000, 0x105000000003, 0x3c3c0, 0x3c3c00000, 0x3c000000011, 0x111100, 0x1111000000, 0x1100000000cc, 0xcccc00, 0xcccc000000, 0xc0000000555, 0x5555000, 0x55550000000, 0x100000003fff, 0x3fffc000, 0x3fffc0000000}); +constexpr StatTableTRI46 SQR8_TABLE_TRI46({0x1, 0xcc000000, 0x3c3c0, 0x10000000c, 0x550055000, 0x3c000111111, 0xc0050, 0x103fc000003f, 0x1111cccc00, 0x5000500390, 0x13ec1010101, 0xcccc9999955, 0x5000001100, 0xc0c01010000, 0xc003fffc555, 0x12c003c0cc00, 0x10505c5c0c0f, 0x155450003ffe, 0x1100cc054100, 0xfcc5053c3d1, 0xd3ff2c00d, 0xd059c3a5c3a, 0x2828282d21e, 0x5000000001, 0xcd010000, 0xc000003c695, 0x3c103c0000c, 0x55c095c0c, 0x169550112eee, 0x1100000c1150, 0x1c339050003f, 0x112e320c01, 0x1d50cc50cf95, 0x116d5292c2c2, 0xcccc9959954, 0x1050cc00113f, 0xc1d1002c3c0, 0xc013fafc509, 0x12fa93c59d01, 0x135c9081d11e, 0x150453cc3fae, 0x13f0d044d33, 0x688119f0a84, 0x39d2d62d29d, 0x3751370167, 0x24e4e4e4b4b4}); +constexpr StatTableTRI46 SQR16_TABLE_TRI46({0x1, 0x1fe6e1ab503e, 0xbbae1f3f55b, 0x1d51cc530c59, 0x163a6a22e14a, 0x5847feb7f81, 0x1ec9cc5fd281, 0xf6cc7b80c70, 0x8f46b31e374, 0xc13bf2ed37d, 0x148a1595bffe, 0x581ad245849, 0x1ea6920b83c1, 0x9d9a8355c7d, 0x6bcf393d5ff, 0x1d4e245085c0, 0x602a8c5e62c, 0x1922dd69197f, 0x7945d3a2aad, 0xf82a823f768, 0xdd24665599b, 0x13b43f6a29d, 0x4df114f238d, 0x1ee783c75ec0, 0xfb670f65c31, 0xf855dc973d2, 0x61ede5f2651, 0x6c1a1266403, 0x1f66ed2a96a, 0xbbbdf683148, 0x1ecc83e160c0, 0x1a2778c4bc0c, 0x10e154273753, 0x1704f8873c23, 0x1b4d3172da99, 0x2b3be805044, 0x5bb08848b9d, 0x1967d2b99be5, 0x7fa55262740, 0xe761a27cc28, 0x17dedd7181b5, 0x155b0344714a, 0x15187b38816e, 0xc5a679b5300, 0x1096cbf94c5d, 0x3f6b3cc122da}); +constexpr StatTableTRI46 QRT_TABLE_TRI46({0x211c4fd486ba, 0x100104a, 0x1001048, 0x104d0492d4, 0x100104c, 0x20005040c820, 0x104d0492dc, 0x40008080, 0x100105c, 0x24835068ce00, 0x20005040c800, 0x200000400800, 0x104d04929c, 0x100904325c, 0x40008000, 0x25da9e77daf0, 0x100115c, 0x1184e1696f0, 0x24835068cc00, 0x24825169dd5c, 0x20005040cc00, 0x3ea3241c60c0, 0x200000400000, 0x211c4e5496f0, 0x104d04829c, 0x20005340d86c, 0x100904125c, 0x24835968de5c, 0x4000c000, 0x6400a0c0, 0x25da9e775af0, 0x118cf1687ac, 0x101115c, 0x1ea1745cacc0, 0x1184e1496f0, 0x20181e445af0, 0x2483506ccc00, 0x20240060c0, 0x24825161dd5c, 0x1e21755dbd9c, 0x20005050cc00, 0x26a3746cacc0, 0x3ea3243c60c0, 0xea3243c60c0, 0x200000000000, 0}); +typedef FieldTri FieldTri46; +#endif + +#ifdef ENABLE_FIELD_INT_47 +// 47 bit field +typedef RecLinTrans StatTable47; +constexpr StatTable47 SQR_TABLE_47({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x42, 0x108, 0x420, 0x1080, 0x4200, 0x10800, 0x42000, 0x108000, 0x420000, 0x1080000, 0x4200000, 0x10800000, 0x42000000, 0x108000000, 0x420000000, 0x1080000000, 0x4200000000, 0x10800000000, 0x42000000000, 0x108000000000, 0x420000000000, 0x80000000042, 0x200000000108}); +constexpr StatTable47 SQR2_TABLE_47({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x42, 0x420, 0x4200, 0x42000, 0x420000, 0x4200000, 0x42000000, 0x420000000, 0x4200000000, 0x42000000000, 0x420000000000, 0x200000000108, 0x1004, 0x10040, 0x100400, 0x1004000, 0x10040000, 0x100400000, 0x1004000000, 0x10040000000, 0x100400000000, 0x4000000042, 0x40000000420, 0x400000004200, 0x42108, 0x421080, 0x4210800, 0x42108000, 0x421080000, 0x4210800000, 0x42108000000, 0x421080000000, 0x210800000108, 0x108000001004, 0x80000010002}); +constexpr StatTable47 SQR4_TABLE_47({0x1, 0x10000, 0x100000000, 0x42, 0x420000, 0x4200000000, 0x1004, 0x10040000, 0x100400000000, 0x42108, 0x421080000, 0x210800000108, 0x1000010, 0x10000100000, 0x1000004200, 0x42000420, 0x420004200000, 0x42000100400, 0x1004010040, 0x40100400420, 0x4004210842, 0x42108421080, 0x84210810002, 0x108100000004, 0x142, 0x1420000, 0x14200000000, 0x5204, 0x52040000, 0x520400000000, 0x142508, 0x1425080000, 0x250800000528, 0x5210810, 0x52108100000, 0x81000014202, 0x142001420, 0x420014200042, 0x142000520400, 0x5204052040, 0x40520401424, 0x20401425094a, 0x142509425080, 0x9425085210a, 0x508521084204, 0x21084210804a, 0x421080420010}); +constexpr StatTable47 SQR8_TABLE_47({0x1, 0x420004200000, 0x250800000528, 0x11004, 0xc6210910402, 0x142005730c10, 0x101000010, 0x1056050040, 0x55a429184204, 0x111404110002, 0x532408500562, 0x3d196251d62e, 0x420142, 0x44524611c66, 0x1142047728, 0x46205e2508, 0x67339c2519da, 0x5661384b5880, 0x434346200424, 0x392938cb55aa, 0x724b0c31058c, 0x7f4f1cf703fc, 0x4fe303d32d1e, 0x75de250d676c, 0x100400011004, 0xc6210910540, 0x102525331834, 0x1101046318, 0x1057532548, 0x37f4bd7f4b5e, 0x115021380840, 0x52674a501142, 0x297a4a1b86ae, 0x666b0c2010ca, 0x776839031b88, 0x4b5316a05622, 0x254e215e2030, 0x6733ce2009de, 0xa8609d21e86, 0x567347420874, 0x6e0d31db55ba, 0x4357182485c4, 0x2afb35ef02ba, 0x5af227961c30, 0x64faad1b0116, 0x2d5d2527ef40, 0x6e27e3bc1978}); +constexpr StatTable47 SQR16_TABLE_47({0x1, 0x421ac25c8774, 0x30581389b510, 0x423b9c671db0, 0xa4537914208, 0x38f9be0dbf38, 0x351c5a8b92a8, 0xc38b9920da2, 0x508d34674f2a, 0x1f8c359a6b76, 0x5ac4bf86daaa, 0x51d6a6616df2, 0xe2717a0378a, 0x13353e783e6e, 0x55a55ac09ec6, 0x3f17cde43402, 0x760584b64b6c, 0x6acbecc99a02, 0x16be80e45b76, 0x2d5069e0005a, 0x3388f5759aa6, 0x2f98f891f4e2, 0x7657f368d924, 0x48f81e34f5b0, 0x51a9087f072e, 0x1de01ba9001c, 0x560b4b374bfc, 0x13f576988ff0, 0x3673cd322294, 0x595959f7c5fe, 0xbfa426eb4a4, 0x2b68fd7c02c2, 0x2a3c1437913a, 0x6e4b179fcf9e, 0x69ddf09bbdee, 0x7b91973d5e52, 0x1329cefd9514, 0x6a5f380b7ab0, 0x48e6620529c4, 0x60589a4b95b6, 0x5e4bd1d1aa34, 0x4b1ec7645cc2, 0x5cfb8785aec6, 0x34e47cf10c3a, 0x7b6c363eee10, 0x1dc52d768b32, 0x3585af9113a0}); +constexpr StatTable47 QRT_TABLE_47({0, 0x1001040, 0x1001042, 0x1047043076, 0x1001046, 0x112471c241e, 0x104704307e, 0x4304e052168, 0x1001056, 0x10004000, 0x112471c243e, 0x172a09c949d6, 0x104704303e, 0x4002020, 0x4304e0521e8, 0x5400e220, 0x1001156, 0x172b08c85080, 0x10004200, 0x41200b0800, 0x112471c203e, 0x172f0cca50a0, 0x172a09c941d6, 0x7eb88a11c1d6, 0x104704203e, 0x1044042020, 0x4000020, 0x42001011156, 0x4304e0561e8, 0x172a28c95880, 0x54006220, 0x112931cc21e, 0x1011156, 0x53670f283e, 0x172b08ca5080, 0x7a80c414a03e, 0x10044200, 0x40000000000, 0x4120030800, 0x1928318801e, 0x112470c203e, 0x799283188000, 0x172f0cea50a0, 0x1eb88a91c1c8, 0x172a098941d6, 0x3ea8cc95e1f6, 0x7eb88a91c1d6}); +typedef Field Field47; +typedef FieldTri, &SQR_TABLE_47, &SQR2_TABLE_47, &SQR4_TABLE_47, &SQR8_TABLE_47, &SQR16_TABLE_47, &QRT_TABLE_47, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri47; +#endif + +#ifdef ENABLE_FIELD_INT_48 +// 48 bit field +typedef RecLinTrans StatTable48; +constexpr StatTable48 SQR_TABLE_48({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x2d, 0xb4, 0x2d0, 0xb40, 0x2d00, 0xb400, 0x2d000, 0xb4000, 0x2d0000, 0xb40000, 0x2d00000, 0xb400000, 0x2d000000, 0xb4000000, 0x2d0000000, 0xb40000000, 0x2d00000000, 0xb400000000, 0x2d000000000, 0xb4000000000, 0x2d0000000000, 0xb40000000000, 0xd0000000005a, 0x40000000011f}); +constexpr StatTable48 SQR2_TABLE_48({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x2d, 0x2d0, 0x2d00, 0x2d000, 0x2d0000, 0x2d00000, 0x2d000000, 0x2d0000000, 0x2d00000000, 0x2d000000000, 0x2d0000000000, 0xd0000000005a, 0x451, 0x4510, 0x45100, 0x451000, 0x4510000, 0x45100000, 0x451000000, 0x4510000000, 0x45100000000, 0x451000000000, 0x5100000000b4, 0x100000000bd9, 0xbdbd, 0xbdbd0, 0xbdbd00, 0xbdbd000, 0xbdbd0000, 0xbdbd00000, 0xbdbd000000, 0xbdbd0000000, 0xbdbd00000000, 0xdbd00000011f, 0xbd0000001001, 0xd0000001010f}); +constexpr StatTable48 SQR4_TABLE_48({0x1, 0x10000, 0x100000000, 0x2d, 0x2d0000, 0x2d00000000, 0x451, 0x4510000, 0x45100000000, 0xbdbd, 0xbdbd0000, 0xbdbd00000000, 0x101101, 0x1011010000, 0x1101000002d0, 0x2d2fd2d, 0x2d2fd2d0000, 0xfd2d0000454a, 0x45514551, 0x455145510000, 0x4551000bd0bd, 0xbd0b6d0bd, 0xd0b6d0bd011f, 0xd0bd0100011e, 0x10001010001, 0x10100012d00, 0x12d002d2d, 0x2d002d2d002d, 0x2d2d00295100, 0x2951045551, 0x5104555104e5, 0x555104ecbdb4, 0x4ecbdbd00bd, 0xbdbd00bdadbc, 0xbdadac1101, 0xadac11011001, 0x11011013c3fc, 0x1013c3fefd2d, 0xc3fefd2fd2a7, 0xfd2fd2baac36, 0xd2baac2d450b, 0xac2d45145ac2, 0x45145ad0f851, 0x5ad0f85adb64, 0xf85adb6cbd10, 0xdb6cbd0bd0a2, 0xbd0bd0bc003c, 0xd0bc002c001f}); +constexpr StatTable48 SQR8_TABLE_48({0x1, 0x2d2fd2d0000, 0x4ecbdbd00bd, 0x2c0000002c, 0x47882d9b95ec, 0xb9eec2eefc90, 0xbd900450, 0x9734009bfc8f, 0x93070a51e9b7, 0xb9d0aa02ec00, 0x8630787dd0ab, 0x6b3468ab98c9, 0x4100001bc1bd, 0x3ffeec1692fd, 0x2e0cce80414a, 0x11a77fc95626, 0x7c9856084ffe, 0x2f41c702f193, 0xd260e95bfeeb, 0x3b86220b54eb, 0x5735c802071a, 0x44626fc7ba84, 0x2b9cda923f5b, 0xc57d0a962e45, 0xd1685001450b, 0x6d78400145b6, 0x9114412978c1, 0x7d9ece1f5b0c, 0xfd2419988960, 0xd12ac3eaeaa5, 0x7d67e75f441d, 0xf86c2ba5457c, 0x40db7617aa6a, 0x80ee292186, 0xbd0f8525d34c, 0xa87ce27ca699, 0xacf3315d7a6d, 0x1a289bca977, 0x92975374e0f1, 0x3fcf113826ab, 0xff4d9be19a5e, 0x1412e5091900, 0x82c721f22d43, 0x1d773380ff32, 0xfed661cca7b1, 0x308072e06846, 0xd3eb44e91aa0, 0x819a669cbb14}); +constexpr StatTable48 SQR16_TABLE_48({0x1, 0x50c24311dfa9, 0xfc08c1e39482, 0xa9ff91b620a3, 0x54954a59d16c, 0xec45c0a9fb02, 0xba7004022837, 0xc1ea19828166, 0xee9a3efecffe, 0x57ed421a20bb, 0x69d387b19141, 0x9105d02d728f, 0xd2f24d4006da, 0x39195005f508, 0xd0206ff5333c, 0x8592e734a441, 0x787b36a1c435, 0x1151e6f03f85, 0xfe0429bf95ab, 0xab2b20b47651, 0x2a65fc935212, 0x7f73ae670e2e, 0x697c17f0fc4a, 0x55dc5681f013, 0xadbd09a289bc, 0x418414f64940, 0x927c737efd40, 0x38535d08fc98, 0xe811b107691c, 0x856c3bbf4cf6, 0x47e629ad3757, 0xf9c82b4b2c09, 0x64312c99e2f, 0xd4936c978dfd, 0x782ff8716675, 0xaf853e867dd7, 0x457143c1fa6d, 0x84c4dda48e91, 0xbac9aacda41e, 0x6a6e0ffb2dc1, 0xfef377f00194, 0x3a129790acc1, 0x541e49c6f92a, 0x73e821aca96d, 0x3a6f15c03f57, 0x1bf377c66f3b, 0xbff5e192fe3b, 0x346360ee74a}); +constexpr StatTable48 QRT_TABLE_48({0xc00442c284f0, 0xc16b7fda410a, 0xc16b7fda4108, 0xada3b5c79fbe, 0xc16b7fda410c, 0x16f3c18d5b0, 0xada3b5c79fb6, 0x7090a381f64, 0xc16b7fda411c, 0xcafc15d179f8, 0x16f3c18d590, 0x6630880e534e, 0xada3b5c79ff6, 0xa13dd1f49826, 0x7090a381fe4, 0xb87560f6a74, 0xc16b7fda401c, 0xaaaaffff0012, 0xcafc15d17bf8, 0xaafd15f07bf6, 0x16f3c18d190, 0x60000020000e, 0x6630880e5b4e, 0xcb977fcb401c, 0xada3b5c78ff6, 0x6663420cad0, 0xa13dd1f4b826, 0xc0045fc2f41c, 0x7090a385fe4, 0x6762e24b834, 0xb87560fea74, 0xc6351fed241c, 0xc16b7fdb401c, 0x60065622ea7a, 0xaaaafffd0012, 0xdf9562bea74, 0xcafc15d57bf8, 0x6657ea057bea, 0xaafd15f87bf6, 0xa79329ddaa66, 0x16f3c08d190, 0xa39229f0aa66, 0x60000000000e, 0x175fb4468ad0, 0x6630884e5b4e, 0, 0xcb977f4b401c, 0x2630884e5b40}); +typedef Field Field48; +#endif +} + +Sketch* ConstructClMul6Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_41 + case 41: return new SketchImpl(implementation, 41); +#endif +#ifdef ENABLE_FIELD_INT_42 + case 42: return new SketchImpl(implementation, 42); +#endif +#ifdef ENABLE_FIELD_INT_43 + case 43: return new SketchImpl(implementation, 43); +#endif +#ifdef ENABLE_FIELD_INT_44 + case 44: return new SketchImpl(implementation, 44); +#endif +#ifdef ENABLE_FIELD_INT_45 + case 45: return new SketchImpl(implementation, 45); +#endif +#ifdef ENABLE_FIELD_INT_47 + case 47: return new SketchImpl(implementation, 47); +#endif +#ifdef ENABLE_FIELD_INT_48 + case 48: return new SketchImpl(implementation, 48); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri6Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_41 + case 41: return new SketchImpl(implementation, 41); +#endif +#ifdef ENABLE_FIELD_INT_42 + case 42: return new SketchImpl(implementation, 42); +#endif +#ifdef ENABLE_FIELD_INT_44 + case 44: return new SketchImpl(implementation, 44); +#endif +#ifdef ENABLE_FIELD_INT_46 + case 46: return new SketchImpl(implementation, 46); +#endif +#ifdef ENABLE_FIELD_INT_47 + case 47: return new SketchImpl(implementation, 47); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_7bytes.cpp b/src/fields/clmul_7bytes.cpp new file mode 100644 index 0000000000..2050dc32dd --- /dev/null +++ b/src/fields/clmul_7bytes.cpp @@ -0,0 +1,170 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_7) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_49 +// 49 bit field +typedef RecLinTrans StatTable49; +constexpr StatTable49 SQR_TABLE_49({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x402, 0x1008, 0x4020, 0x10080, 0x40200, 0x100800, 0x402000, 0x1008000, 0x4020000, 0x10080000, 0x40200000, 0x100800000, 0x402000000, 0x1008000000, 0x4020000000, 0x10080000000, 0x40200000000, 0x100800000000, 0x402000000000, 0x1008000000000, 0x20000000402, 0x80000001008, 0x200000004020, 0x800000010080}); +constexpr StatTable49 SQR2_TABLE_49({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x1008, 0x10080, 0x100800, 0x1008000, 0x10080000, 0x100800000, 0x1008000000, 0x10080000000, 0x100800000000, 0x1008000000000, 0x80000001008, 0x800000010080, 0x100004, 0x1000040, 0x10000400, 0x100004000, 0x1000040000, 0x10000400000, 0x100004000000, 0x1000040000000, 0x400001008, 0x4000010080, 0x40000100800, 0x400001008000, 0x10080402, 0x100804020, 0x1008040200, 0x10080402000, 0x100804020000, 0x1008040200000, 0x80402001008, 0x804020010080, 0x40200100004, 0x402001000040, 0x20010000002, 0x200100000020}); +constexpr StatTable49 SQR4_TABLE_49({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x1008000, 0x10080000000, 0x800000010080, 0x100004000, 0x1000040000000, 0x400001008000, 0x10080402000, 0x804020010080, 0x200100000020, 0x1000000001000, 0x11008000, 0x110080000000, 0x800000110880, 0x1108004000, 0x1080040001008, 0x400011008400, 0x110084402000, 0x844020110880, 0x201108040220, 0x1080402000008, 0x20001008002, 0x10080000100, 0x800001010080, 0x10100004000, 0x1000040010080, 0x400101808000, 0x1018080402000, 0x8040210100c0, 0x210100400020, 0x1004000011080, 0x11180c020, 0x11180c0200000, 0xc020011108c0, 0x11108004010, 0x1080040111088, 0x401111808400, 0x1118084403008, 0x8440311908c0, 0x311908440220, 0x108440211008c, 0x2110184c022, 0x10184c0201108, 0xc020100904c2, 0x100904024010, 0x1040240000004}); +constexpr StatTable49 SQR8_TABLE_49({0x1, 0x800000110880, 0x210100400020, 0x1040240000004, 0x130081008002, 0x191c0a54130c8, 0x900804130890, 0x210111408020, 0x582c0402004, 0xd320910984c0, 0xb1d1ad4522e8, 0x1d80945829818, 0x1218540601128, 0x1240340000024, 0x11300c1018082, 0x193d1a4c5f0ea, 0x148a0522810, 0xe0201051c8e0, 0x2dc7c25120a8, 0x1d2205148a440, 0x33c0adc0e24a, 0x17850e987bab8, 0xa8a40071c9e0, 0x10d47d391c1a8, 0x9740b01888c2, 0x91c0e54130c8, 0x920805138892, 0x130819500b028, 0x8583c0516884, 0x1fa25934984e8, 0x1f5c2fcc5a6ec, 0x15a094483189a, 0x1014cc242300, 0xac020000582c, 0x3b05524100aa, 0x1520255145c6e, 0x4279b95aec40, 0x1f1a0951178e8, 0xbcc646004828, 0x3b055219aca8, 0x57d2fc40666e, 0xba50b987beba, 0x8aa44d913bea, 0x944717d1b9a0, 0x776562491022, 0x1701305105c4c, 0x19039cd5be842, 0x1b281d8e58082, 0x134dac80532a4}); +constexpr StatTable49 SQR16_TABLE_49({0x1, 0x790181b552e0, 0xeb19044e00a, 0xc6bf7911f7ae, 0x447f77c1a0c4, 0x19d2a0d21c480, 0x13d4e22aadedc, 0x18fa344c8f0a6, 0x1481c1bbfde92, 0x41547e22f6e0, 0xf5ad96335088, 0xd7e4db3adaa0, 0x197fc8d7b53d0, 0x37781564b82a, 0xa52ef2139cbc, 0x153c6a0949498, 0x18d7401fc152e, 0xc4b5d8597752, 0xd15cd891aa2, 0x217903427da8, 0x13ec9e269a0e0, 0xc01720774514, 0x389aeb1d788a, 0x64a914a860a4, 0xa09aebec6188, 0x15c3239e150c8, 0x38f8fe110ce, 0xc1ea415c5006, 0x3209972f2ff0, 0x41bfc6b2ad88, 0x1ccc2fd5f73c8, 0x7bed1f863c00, 0x1a46d9b9844f4, 0x12e3ca6573ff6, 0x290c26cca98c, 0x514cb03b3b2e, 0x11168909cbc2c, 0x8e6dc910afda, 0x11311def1c440, 0x3e42d62664d8, 0x1c2bb2d75fe80, 0x2db5d58b45ca, 0x3d14059fd338, 0x109e8f457ebf8, 0x43b071b62a64, 0x185242247c010, 0x5e0c7721c092, 0x1c94950e46b82, 0x1761170f76a40}); +constexpr StatTable49 QRT_TABLE_49({0, 0x10004196, 0x10004194, 0x5099461f080, 0x10004190, 0x40840600c20, 0x5099461f088, 0x58a56349cfde, 0x10004180, 0x48641a0c03fe, 0x40840600c00, 0x10084002848, 0x5099461f0c8, 0x4002048, 0x58a56349cf5e, 0x5088460a048, 0x10004080, 0x4c2852624dde, 0x48641a0c01fe, 0x14893129c280, 0x40840600800, 0x1eb23c323ace8, 0x10084002048, 0x48740a09417e, 0x5099461e0c8, 0x40852604d96, 0x4000048, 0x5cad2b29c37e, 0x58a563498f5e, 0x20000200, 0x50884602048, 0x10000000000, 0x10014080, 0x4c2a56624d96, 0x4c2852604dde, 0x1ee2347438ca0, 0x48641a0801fe, 0x480000000048, 0x14893121c280, 0x14091121c080, 0x40840700800, 0x1a5099561e17e, 0x1eb23c303ace8, 0x8740a894136, 0x10084402048, 0x18101c501ace8, 0x48740a89417e, 0x15dace6286f96, 0x5099561e0c8}); +typedef Field Field49; +typedef FieldTri, &SQR_TABLE_49, &SQR2_TABLE_49, &SQR4_TABLE_49, &SQR8_TABLE_49, &SQR16_TABLE_49, &QRT_TABLE_49, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri49; +#endif + +#ifdef ENABLE_FIELD_INT_50 +// 50 bit field +typedef RecLinTrans StatTable50; +constexpr StatTable50 SQR_TABLE_50({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x1d, 0x74, 0x1d0, 0x740, 0x1d00, 0x7400, 0x1d000, 0x74000, 0x1d0000, 0x740000, 0x1d00000, 0x7400000, 0x1d000000, 0x74000000, 0x1d0000000, 0x740000000, 0x1d00000000, 0x7400000000, 0x1d000000000, 0x74000000000, 0x1d0000000000, 0x740000000000, 0x1d00000000000, 0x340000000001d, 0x1000000000053}); +constexpr StatTable50 SQR2_TABLE_50({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x74, 0x740, 0x7400, 0x74000, 0x740000, 0x7400000, 0x74000000, 0x740000000, 0x7400000000, 0x74000000000, 0x740000000000, 0x340000000001d, 0x151, 0x1510, 0x15100, 0x151000, 0x1510000, 0x15100000, 0x151000000, 0x1510000000, 0x15100000000, 0x151000000000, 0x1510000000000, 0x1100000000069, 0x10000000006e4, 0x6e34, 0x6e340, 0x6e3400, 0x6e34000, 0x6e340000, 0x6e3400000, 0x6e34000000, 0x6e340000000, 0x6e3400000000, 0x2e3400000001d, 0x234000000011f, 0x3400000001118}); +constexpr StatTable50 SQR4_TABLE_50({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x74000, 0x740000000, 0x340000000001d, 0x151000, 0x1510000000, 0x1100000000069, 0x6e3400, 0x6e34000000, 0x234000000011f, 0x1110100, 0x11101000000, 0x1010000000734, 0x7334740, 0x73347400000, 0x347400000145c, 0x14540510, 0x145405100000, 0x510000068b9, 0x68b91a34, 0x68b91a340000, 0x11a3400010106, 0x101010001, 0x1010100010000, 0x1000100074740, 0x1000747474000, 0x347474007401d, 0x340074015050d, 0x340150505101d, 0x1050510151069, 0x11015106e5a5d, 0x1106e5a5a3469, 0x25a5a346e351f, 0x2346e3510111e, 0x235101110001f, 0x111000110634, 0x1106347334, 0x1063473340074, 0x733400735301, 0x7353004541, 0x353004541014c, 0x454101446dc0, 0x101446dc1cb90, 0x6dc1cb97468d, 0x1cb97468c1a30, 0x3468c1a350009, 0x1a3500010007}); +constexpr StatTable50 SQR8_TABLE_50({0x1, 0x7334740, 0x1050510151069, 0x3468c1a350009, 0x341624173531c, 0x245791a347b50, 0x23179d1a40682, 0x1671402235203, 0x321023818751e, 0x143ca5b716dd5, 0x171633ad257de, 0x33860681a5d1d, 0x5e572f82a317, 0x10e7512224646, 0x32d6b56300005, 0x350ab39687414, 0x25c47550c1a8a, 0x23a2e2d91533f, 0x2211af19c2381, 0x352073a863a68, 0x37f43380f0ac4, 0x233516127052a, 0x25ad4785169cf, 0x237b6a609b0b6, 0x132fd372b5dac, 0x1f311727562e, 0x345bd7e275754, 0x352fe5b3d7708, 0x259a328ca3376, 0x25101aab53ece, 0x32701d9da5ace, 0x17809a9c86099, 0x72b4752a7323, 0x202d22dc33a7c, 0x5a8c0dbc19a2, 0x14a86b37416ad, 0x5c574289fe12, 0x3627f3bf0f37b, 0x27349052a4f83, 0x2436d71033de5, 0x22fab345e0bce, 0x27ea796d5a27a, 0x1e4f33562d17, 0x31a1f9c3f2154, 0x1638db7753f96, 0x2256163f33b5f, 0x11a6ecf28882e, 0x1bd4cf35f47cc, 0x25e19aeb21e64, 0x371612d0b4dcd}); +constexpr StatTable50 SQR16_TABLE_50({0x1, 0x14db3a1b1531f, 0x270a39b5e8c48, 0x26536a58442bd, 0x7f158d4b869e, 0x12663760f7d, 0x29634a2c8876, 0x15271f7ec5d31, 0x17fbb0726d0f0, 0x7f0f7bf826bb, 0x115135d3c7c4c, 0x348ffaaa125e5, 0x1887695a20d9, 0x25e41181c0de, 0x2670d7f17fb35, 0x356079737f513, 0x22bebda8a1574, 0x315f9649d2b50, 0x13abe45aa6ac8, 0x723d536b5242, 0x24263520a22a9, 0x15860c0156a69, 0x271d0bbeed892, 0x146920f281d19, 0x117d5d46e7991, 0x278d8273551fc, 0x15d73a9745614, 0x7e5e966bbfe0, 0x687b14e62abb, 0x178acea79fa5c, 0x3363c557e9662, 0x3153c79bf06ef, 0x15c8ff9daf7ce, 0x243b030f4617a, 0x20663fbd2383a, 0x25c5dbd448872, 0x21fc8dfbd2429, 0x229f9fb8f01b0, 0x17a180ae72359, 0x1c8e2f554ad9, 0x174596d1e774f, 0x3264c5da47f53, 0x333817d45b05c, 0x321907ec10dfd, 0x3a12b2018ada, 0x23ab0599cd08, 0x23028d60c00e5, 0x8ca05e2a1eab, 0x3537bf673a228, 0x32f8cf8611080}); +constexpr StatTable50 QRT_TABLE_50({0xfbdfa3ae9d4c, 0x38143245a4878, 0x38143245a487a, 0x38527487e7492, 0x38143245a487e, 0x3124c61f56d2a, 0x38527487e749a, 0xfa8c91b087c0, 0x38143245a486e, 0x3eca48c6196be, 0x3124c61f56d0a, 0x380000040080a, 0x38527487e74da, 0x976b2d8b39b4, 0xfa8c91b08740, 0xfa8cd5b02724, 0x38143245a496e, 0x316291dd013fe, 0x3eca48c6194be, 0x10344122064, 0x3124c61f5690a, 0x68c5f006ee40, 0x380000040000a, 0x852749fe64d0, 0x38527487e64da, 0x37ef8e9d0e9da, 0x976b2d8b19b4, 0x37fabd1cef34a, 0xfa8c91b0c740, 0x96282d9159b4, 0xfa8cd5b0a724, 0x464a8249dd0, 0x38143245b496e, 0x37eaa8ddc94be, 0x316291dd213fe, 0x392446035690a, 0x3eca48c6594be, 0x974b258b4964, 0x103441a2064, 0x385a7c87fb4da, 0x3124c61e5690a, 0xeb8ad5d9a724, 0x68c5f026ee40, 0x3724c61e5690a, 0x380000000000a, 0x3a8c5f026ee4a, 0x8527497e64d0, 0, 0x38527497e64da, 0x2fbdfa2ae8d0a}); +typedef Field Field50; +#endif + +#ifdef ENABLE_FIELD_INT_51 +// 51 bit field +typedef RecLinTrans StatTable51; +constexpr StatTable51 SQR_TABLE_51({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x96, 0x258, 0x960, 0x2580, 0x9600, 0x25800, 0x96000, 0x258000, 0x960000, 0x2580000, 0x9600000, 0x25800000, 0x96000000, 0x258000000, 0x960000000, 0x2580000000, 0x9600000000, 0x25800000000, 0x96000000000, 0x258000000000, 0x960000000000, 0x2580000000000, 0x160000000004b, 0x580000000012c, 0x6000000000426}); +constexpr StatTable51 SQR2_TABLE_51({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x96, 0x960, 0x9600, 0x96000, 0x960000, 0x9600000, 0x96000000, 0x960000000, 0x9600000000, 0x96000000000, 0x960000000000, 0x160000000004b, 0x6000000000426, 0x4114, 0x41140, 0x411400, 0x4114000, 0x41140000, 0x411400000, 0x4114000000, 0x41140000000, 0x411400000000, 0x4114000000000, 0x1140000000258, 0x1400000002516, 0x40000000251f6, 0x251d38, 0x251d380, 0x251d3800, 0x251d38000, 0x251d380000, 0x251d3800000, 0x251d38000000, 0x251d380000000, 0x51d380000012c, 0x1d3800000100e, 0x538000001003d, 0x380000010011e}); +constexpr StatTable51 SQR4_TABLE_51({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x96000, 0x960000000, 0x160000000004b, 0x411400, 0x4114000000, 0x1140000000258, 0x251d380, 0x251d3800000, 0x1d3800000100e, 0x10010110, 0x100101100000, 0x1011000009600, 0x960969f6, 0x960969f60000, 0x169f60004110b, 0x6000411015132, 0x4110151054000, 0x1510540251f60, 0x540251f6ba760, 0x51f6ba74eb92c, 0x3a74eb900001f, 0x6b90000010033, 0x100010860, 0x1000108600000, 0x1086000096000, 0x960092874, 0x160092874004b, 0x128740041144b, 0x40041144304e2, 0x1144304c78258, 0x304c78251d1d8, 0x78251d1c38368, 0x1d1c38352800e, 0x3835280011188, 0x280011197096e, 0x1119709787000, 0x709787009fb46, 0x7009fb7861c9, 0x1fb7861cae24b, 0x61cae2456109, 0x2e245610a7bc3, 0x5610a7bd61498, 0x27bd614b79d2b, 0x614b79d3a74de, 0x79d3a74e9f68a, 0x274e9f6b06011, 0x1f6b06000000f}); +constexpr StatTable51 SQR8_TABLE_51({0x1, 0x960969f6, 0x40041144304e2, 0x79d3a74e9f68a, 0x61005961939d4, 0x2e2108dfdafb5, 0xe7e61b897f73, 0x493a58b330d18, 0x7882105dc65ec, 0x5f00774200d11, 0x63ef4cd371ef3, 0x660b24b8d214b, 0x7ab791e669e3d, 0x10821820969f6, 0x1544b9d4c3f3e, 0x831185e3da14, 0x1eb0831983187, 0x1d8699ae87312, 0x586e000eb5f1a, 0x3ea794ef9c821, 0x2ab1c63209cc1, 0x7f434bcc29855, 0x673d370c40117, 0x6a668249ddd8b, 0x48be019e56bbe, 0x57d1a751be823, 0x5621931ca6d5f, 0x68c5a37844a68, 0xefa69123b6b1, 0x5804da97df62a, 0x30c29b82f3986, 0x5b808f6ddc779, 0x2c8b4e7596cbe, 0x2c5a432ec7a14, 0x7f178a4d63277, 0x77112a07b99b7, 0x56cf47ad50529, 0x73a2180190a41, 0x25cbc68f1f1a8, 0x1c27dc22e6950, 0x2fbf4aafee2ad, 0x554b728a595ca, 0x52726d34627e, 0x6dcc716c9e860, 0x36ade274d5eff, 0x1fa23a55b359a, 0x1bc6260896059, 0x53a74c5798bc1, 0x50e671fc54a4a, 0x251a72b3c4c3c, 0x6d9623f5d3a1e}); +constexpr StatTable51 SQR16_TABLE_51({0x1, 0x27b32044e9663, 0x528c08dd195bf, 0x5d461228d5764, 0x616db8f131bf6, 0x9d910988ca4, 0x1e7a7a29c55fd, 0x512a2e6297818, 0x688d44453ead0, 0x70e0b6e1b3be2, 0x4313e5612d70, 0x132241d43589d, 0x7ca688c29c89a, 0x1d6b8caeb8958, 0x36d06e8e76e3b, 0x18ebafc89388e, 0x1cb5f93b2c29c, 0x5137bd7b7b6ec, 0x6e3ae8731000b, 0x359203e5e12fe, 0x1822ded1f1e16, 0x3ee9c50cbcb89, 0x5cc0b4564ab4, 0x695b235bd9236, 0x283c619a1ecb, 0x6f37f1f6ef70d, 0x7f394b6fbdd53, 0x3f482b36793f, 0x4055274e56dfa, 0x1a85d9d434f33, 0x37aa8f3df2031, 0x5f4e77b2bb063, 0x6e9702d84f07b, 0x25f16f8ffd4c2, 0x22c591d8277cb, 0x59435d9bae242, 0x46eaf9f69ddd9, 0x3098c1e26bd6e, 0x6c6544847a1d, 0x254946c0c33ce, 0x23970a6118811, 0x67f6c55082b49, 0x6592c83ebde46, 0x716418f089ed8, 0x8cb8de463166, 0x37cb1794fac42, 0x94ac55c1ac68, 0x3ab0d33bb4fdf, 0x1669c2f7ae3c5, 0x4d4e4f61d1f04, 0x476980d17eef5}); +constexpr StatTable51 QRT_TABLE_51({0x778bf2703d152, 0x2aaaafbff2092, 0x2aaaafbff2090, 0x4d2119c7e7780, 0x2aaaafbff2094, 0x65de1df8ae194, 0x4d2119c7e7788, 0x67d63d7ba262c, 0x2aaaafbff2084, 0x28ff003f4167c, 0x65de1df8ae1b4, 0x658397fb1d034, 0x4d2119c7e77c8, 0x4d7c9284526ba, 0x67d63d7ba26ac, 0x6666333fc0cbe, 0x2aaaafbff2184, 0x295b807ab55ee, 0x28ff003f4147c, 0x2aaabfffe0016, 0x65de1df8ae5b4, 0x209210349d60, 0x658397fb1d834, 0x4d215dc7cf1c8, 0x4d2119c7e67c8, 0x662b2b3d7b4be, 0x4d7c9284506ba, 0x255af00b36e0, 0x67d63d7ba66ac, 0x65de1fb8ac1a6, 0x6666333fc8cbe, 0x662f3b3ded4be, 0x2aaaafbfe2184, 0x663a9dbc3a426, 0x295b807a955ee, 0x4cdc9ec128928, 0x28ff003f0147c, 0x28a0c93cd511c, 0x2aaabfff60016, 0x65d73cf8e78d4, 0x65de1df9ae5b4, 0x4d5eddc44f1c8, 0x209210149d60, 0x357fcc506c8a, 0x658397ff1d834, 0, 0x4d215dcfcf1c8, 0x63f536f5d4554, 0x4d2119d7e67c8, 0x4000000000022, 0x662b2b1d7b4be}); +typedef Field Field51; +#endif + +#ifdef ENABLE_FIELD_INT_52 +// 52 bit field +typedef RecLinTrans StatTable52; +constexpr StatTable52 SQR_TABLE_52({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x90000, 0x240000, 0x900000, 0x2400000, 0x9000000, 0x24000000, 0x90000000, 0x240000000, 0x900000000, 0x2400000000, 0x9000000000, 0x24000000000, 0x90000000000, 0x240000000000, 0x900000000000, 0x2400000000000, 0x9000000000000, 0x4000000000012}); +constexpr StatTable52 SQR2_TABLE_52({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x9, 0x90, 0x900, 0x9000, 0x90000, 0x900000, 0x9000000, 0x90000000, 0x900000000, 0x9000000000, 0x90000000000, 0x900000000000, 0x9000000000000, 0x41, 0x410, 0x4100, 0x41000, 0x410000, 0x4100000, 0x41000000, 0x410000000, 0x4100000000, 0x41000000000, 0x410000000000, 0x4100000000000, 0x1000000000024, 0x249, 0x2490, 0x24900, 0x249000, 0x2490000, 0x24900000, 0x249000000, 0x2490000000, 0x24900000000, 0x249000000000, 0x2490000000000, 0x4900000000012, 0x9000000000104}); +constexpr StatTable52 SQR4_TABLE_52({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x9000, 0x90000000, 0x900000000000, 0x4100, 0x41000000, 0x410000000000, 0x2490, 0x24900000, 0x249000000000, 0x1001, 0x10010000, 0x100100000000, 0x1000000000900, 0x9009000, 0x90090000000, 0x900000000410, 0x4104100, 0x41041000000, 0x410000000249, 0x2492490, 0x24924900000, 0x9249000000104, 0x1000001, 0x10000010000, 0x100000090, 0x1000000900000, 0x9000009000, 0x90000041, 0x900000410000, 0x4100004100, 0x1000041000024, 0x410000249000, 0x2490002490, 0x4900024900012, 0x249000100100, 0x1001001001, 0x10010010009, 0x100100090090, 0x1000900900900, 0x9009009009000, 0x90090041041, 0x900410410410, 0x4104104104100, 0x1041041024924, 0x410249249249, 0x2492492492490, 0x4924924910002, 0x9249100000004}); +constexpr StatTable52 SQR8_TABLE_52({0x1, 0x1000000000900, 0x900000410000, 0x410249249249, 0x349100000000, 0x1900000d1, 0x10d10d1065965, 0x51010000, 0x2d924909000, 0x4114114114109, 0xe591, 0x1000007c96591, 0x1903981d13981, 0x249000000001, 0x1000100000990, 0x990090451041, 0x41022cb49249, 0x37d824910000, 0x90191890190d8, 0x10d10d106edf5, 0x54114101, 0x102f4b400bd90, 0x5c04114114108, 0x8f5900000ebcc, 0x1e59107b5f401, 0x8f5ab89f5abcc, 0x24924900001, 0x1010010010909, 0x90000041d100, 0x41024f7df7d9, 0x34a591003491, 0x9001900000d0, 0x4c10d106522c, 0xb49051500100, 0x43dafdb40249, 0x428c0c5114109, 0x59001f590e576, 0x49f59f25facf6, 0x19039c4c17881, 0x26fdb4902491, 0x10110010998, 0x109009045cc51, 0x90022a8867d9, 0x9527ffcb5a6dc, 0x9bc01190190d9, 0x14c119006e608, 0x42fd9e0d55042, 0x143f711b5bfd9, 0x5fa58e1809008, 0x23d647ac81eb2, 0x57ac8f223a466, 0x865abc8b4abcc}); +constexpr StatTable52 SQR16_TABLE_52({0x1, 0x4f881c2d96599, 0xd7eb53011fc41, 0x81d7387961fef, 0xd9afe338982c3, 0x17590c140da98, 0x141a99a87a04e, 0x10036ba4083d9, 0x8f4f72ffb12c7, 0xc8b70df241e1b, 0x18bd9e5d46c40, 0x18331d76266bd, 0x4d915f264a4e0, 0x46aeffb8e4037, 0x4800042de37b5, 0xdb172953272e8, 0x17a9c2edf826a, 0x191cf7053e3f2, 0x82036da842cea, 0x5891da126c1e, 0x1e536e9e4af49, 0x451b5638f5449, 0x5a006c6c4f8c8, 0x5ac71a535fb44, 0xd39a4d489ebd0, 0x4704e31bc006d, 0xc4b327f6ffae1, 0x46980b709bd00, 0xd755405154c11, 0x5741be2d0b797, 0xcb3d48ed630cb, 0x98a66c9f4f599, 0x4caa324b91629, 0x816b5015eeeaf, 0xa595e92a8ed4, 0xc93c6d9f5a073, 0x4250068b39e2, 0x105add98997b5, 0x408b030c0bce0, 0xced5e4a4a2028, 0x1eb59d68e7f25, 0x189756a5b6db0, 0xc49c5a7c98b01, 0x18c9a496767cb, 0xde650554b3d49, 0x11077035fd81c, 0x8b37c5e95a659, 0x45b9226c2c25e, 0xdd2b5e20c7c8b, 0x6de972f0e7025, 0x84e80092f5681, 0x8dfcf97183cbc}); +constexpr StatTable52 QRT_TABLE_52({0xc108165459b0e, 0x10004086, 0x10004084, 0xc00000100104e, 0x10004080, 0x2041810a545b0, 0xc000001001046, 0x1181e055efc0, 0x10004090, 0x40810214390, 0x2041810a54590, 0xc000141019106, 0xc000001001006, 0x10816045ab40, 0x1181e055ef40, 0xc000111015196, 0x10004190, 0xe045c19af44a2, 0x40810214190, 0xe045809ad0532, 0x2041810a54190, 0xdb387a03fe646, 0xc000141019906, 0x2000000800000, 0xc000001000006, 0x2486548199c34, 0x108160458b40, 0x2041808a50534, 0x1181e055af40, 0xc0408312153d6, 0xc00011101d196, 0x21499f0e0eed0, 0x10014190, 0xe15dff9faabe2, 0xe045c19ad44a2, 0xdb787b01ea7d6, 0x40810254190, 0xe484409180532, 0xe045809a50532, 0xc14095164d896, 0x2041810b54190, 0x217dee8fb7a74, 0xdb387a01fe646, 0x441810b54190, 0xc000141419906, 0xc3386e15e7f46, 0x2000000000000, 0x1000141419900, 0xc000000000006, 0, 0x248654a199c34, 0xa48654a199c32}); +typedef Field Field52; +typedef FieldTri, &SQR_TABLE_52, &SQR2_TABLE_52, &SQR4_TABLE_52, &SQR8_TABLE_52, &SQR16_TABLE_52, &QRT_TABLE_52, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri52; +#endif + +#ifdef ENABLE_FIELD_INT_53 +// 53 bit field +typedef RecLinTrans StatTable53; +constexpr StatTable53 SQR_TABLE_53({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x8e, 0x238, 0x8e0, 0x2380, 0x8e00, 0x23800, 0x8e000, 0x238000, 0x8e0000, 0x2380000, 0x8e00000, 0x23800000, 0x8e000000, 0x238000000, 0x8e0000000, 0x2380000000, 0x8e00000000, 0x23800000000, 0x8e000000000, 0x238000000000, 0x8e0000000000, 0x2380000000000, 0x8e00000000000, 0x3800000000047, 0xe00000000011c, 0x18000000000437}); +constexpr StatTable53 SQR2_TABLE_53({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x238, 0x2380, 0x23800, 0x238000, 0x2380000, 0x23800000, 0x238000000, 0x2380000000, 0x23800000000, 0x238000000000, 0x2380000000000, 0x3800000000047, 0x18000000000437, 0x4054, 0x40540, 0x405400, 0x4054000, 0x40540000, 0x405400000, 0x4054000000, 0x40540000000, 0x405400000000, 0x4054000000000, 0x54000000008e, 0x54000000008e0, 0x14000000008e8e, 0x8ea56, 0x8ea560, 0x8ea5600, 0x8ea56000, 0x8ea560000, 0x8ea5600000, 0x8ea56000000, 0x8ea560000000, 0x8ea5600000000, 0xea5600000011c, 0xa560000001015, 0x560000001000b, 0x1600000010003e}); +constexpr StatTable53 SQR4_TABLE_53({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x23800, 0x238000000, 0x2380000000000, 0x40540, 0x405400000, 0x4054000000000, 0x8ea56, 0x8ea560000, 0x8ea5600000000, 0x1600000010003e, 0x1000111000, 0x10001110000000, 0x11100000238000, 0x2380219b80, 0x380219b800047, 0x19b8000405447, 0x4054441114, 0x54441114008e, 0x41114008ea5ee, 0x14008ea5e6c1b8, 0xea5e6c193611c, 0x6c1936100000d, 0x13610000000124, 0x1010338, 0x10103380000, 0x1033800000238, 0x180000023a3e0f, 0x23a3e3d4000, 0x1a3e3d40000437, 0x1d400004014997, 0x40149af1600, 0x149af160008e0, 0xf160008e2a4a3, 0x8e2a4bc4710, 0x2a4bc47101015, 0x1c4710101022bb, 0x101010228120a8, 0x102281208ba380, 0x1208ba3a3c26c, 0xba3a3c26e7e1c, 0x3c26e7e0ad413, 0xe7e0ad414deb9, 0xad414dea4a7d0, 0x14dea4a7c40960, 0x4a7c4094acd8b, 0x4094acd82b43a, 0xacd82b432f376, 0x2b432f3623804, 0x12f36238010027}); +constexpr StatTable53 SQR8_TABLE_53({0x1, 0x11100000238000, 0x1a3e3d40000437, 0x4a7c4094acd8b, 0x1eea7d6a679bbe, 0x1c423906384897, 0x2168da5f2c08c, 0x1b8259ec8ea11, 0x19d1f388b2d3d6, 0x1959a5720001a7, 0x1a6c8fa147c79, 0x191868056d58df, 0x19b717beaf7eb0, 0x1d37e92df66e7f, 0x16ec165c8535b7, 0x7da5e73dba0b3, 0x14d2bece5702b1, 0xadfaa30a5cf7e, 0x101934bec0d066, 0xaae7f006690ce, 0x6bfdbd85eb297, 0x6b2cb00d8c2b5, 0x52ee73aae547e, 0x67461baa976c2, 0xf8a44f459c7d, 0x2579abd0b5fc6, 0x6e7e5e9b82057, 0x1ab0a0fcf2d91c, 0x385dc87020053, 0xfc75a891c9df0, 0xca67b851d0c1a, 0x4c8d3234fd4f7, 0xf3ea564798c7f, 0x16881f479c0d6b, 0x60b0e8e33fe90, 0x18259a2869066d, 0xc52b463fb1475, 0x8229075c3475d, 0x6725108ff0948, 0xd5edf67d5a509, 0xbf52bb2383664, 0xd5b84ac7ed2ab, 0xbb5901d009d56, 0xcb380bfcebc5, 0x5f411d4594745, 0x18bdcb9f9d25da, 0xf0d3abe76ec15, 0x8b1a6404ca3b5, 0x15b7c7c793b65f, 0x11ff16f08569ff, 0x19c1d4c5eb3442, 0x5deb92ff5fc40, 0xa8009f9410cbc}); +constexpr StatTable53 SQR16_TABLE_53({0x1, 0x5a65e677a526f, 0x142b8f50195f72, 0x12b0ca8e8b1225, 0x1b892547f268cc, 0x1239ca3a4824b6, 0x4249dac026ea8, 0x38080cba150e5, 0x903882481cefb, 0x1ad11e5cf99bf0, 0x14fa149116ab75, 0x6cbd888de21e5, 0x1388c718c37a69, 0x89d1eb38e9978, 0xf12019f00f91f, 0xb377986c7da1f, 0x1c780b06da5cb9, 0x1e10c7eee3249d, 0xe1afb7bd8111d, 0xc821f2a6fa090, 0x1a26caa65e1d59, 0x4280741c8cc4c, 0xb9c507337dad8, 0x65bffa0a097b6, 0x12068bb8ed4ac0, 0x6d751e7056355, 0xbccc3fbdcf084, 0x17ed82d58ea927, 0x125a30b543b4b8, 0xaf1ce3f5f84ce, 0x1082e42090b649, 0xf8d6a6212c41a, 0x1f89211d4982d, 0x1910bdfe092d07, 0x9363da9b9b9d3, 0x8a7196ef7b84e, 0x33fe46ddf1dc, 0x1f3f3291cf719d, 0x91a5da69f1035, 0x5a8dc6eb62cfb, 0xaf99fcc57728a, 0x15e73f1aa49f47, 0x2d82e50796b97, 0x1072fcbb074200, 0x15180f0fc7904, 0xa3a194b750f79, 0xb053c3eea9bb3, 0x1e58da5ae123de, 0x10b47afec00861, 0x17cd9ea910639d, 0x1ecf806dbf8c36, 0xf93d00fe6145b, 0x1247d788a3eda}); +constexpr StatTable53 QRT_TABLE_53({0xf940b90844076, 0x1f940b90844052, 0x1f940b90844050, 0x9d2a063b43e64, 0x1f940b90844054, 0x936f69323ec14, 0x9d2a063b43e6c, 0xe12270a88898, 0x1f940b90844044, 0x1f917f00bb5a3c, 0x936f69323ec34, 0x1f622df85b46ee, 0x9d2a063b43e2c, 0x9bc65ab040b66, 0xe12270a88818, 0x958330b931986, 0x1f940b90844144, 0x98e2a06e32e0, 0x1f917f00bb583c, 0x1f877970dc1024, 0x936f69323e834, 0x16cc3c9b1558c2, 0x1f622df85b4eee, 0x16de1c3351dae8, 0x9d2a063b42e2c, 0x1fecdc7855f8ee, 0x9bc65ab042b66, 0x933821b1cb6fe, 0xe12270a8c818, 0x1f675958641c0e, 0x958330b939986, 0x9d97e050e960, 0x1f940b90854144, 0x1f820fa0e38adc, 0x98e2a06c32e0, 0x1650f0e358a010, 0x1f917f00bf583c, 0x1643af4b037a3a, 0x1f877970d41024, 0x1ffe2c281d8c16, 0x936f69333e834, 0xf00d50ffccf8, 0x16cc3c9b3558c2, 0x16bc31cbca943a, 0x1f622df81b4eee, 0xa6cbd8007232, 0x16de1c33d1dae8, 0x15d2a062b42e10, 0x9d2a062b42e2c, 0x1aa77896586ca, 0x1fecdc7a55f8ee, 0, 0x9bc65af042b66}); +typedef Field Field53; +#endif + +#ifdef ENABLE_FIELD_INT_54 +// 54 bit field +typedef RecLinTrans StatTable54; +constexpr StatTable54 SQR_TABLE_54({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x201, 0x804, 0x2010, 0x8040, 0x20100, 0x80400, 0x201000, 0x804000, 0x2010000, 0x8040000, 0x20100000, 0x80400000, 0x201000000, 0x804000000, 0x2010000000, 0x8040000000, 0x20100000000, 0x80400000000, 0x201000000000, 0x804000000000, 0x2010000000000, 0x8040000000000, 0x20100000000000, 0x400000000402, 0x1000000001008, 0x4000000004020, 0x10000000010080}); +constexpr StatTable54 SQR2_TABLE_54({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x804, 0x8040, 0x80400, 0x804000, 0x8040000, 0x80400000, 0x804000000, 0x8040000000, 0x80400000000, 0x804000000000, 0x8040000000000, 0x400000000402, 0x4000000004020, 0x40001, 0x400010, 0x4000100, 0x40001000, 0x400010000, 0x4000100000, 0x40001000000, 0x400010000000, 0x4000100000000, 0x1000000201, 0x10000002010, 0x100000020100, 0x1000000201000, 0x10000002010000, 0x20100804, 0x201008040, 0x2010080400, 0x20100804000, 0x201008040000, 0x2010080400000, 0x20100804000000, 0x1008040001008, 0x10080400010080, 0x804000100004, 0x8040001000040, 0x400010000002, 0x4000100000020}); +constexpr StatTable54 SQR4_TABLE_54({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x80400, 0x804000000, 0x8040000000000, 0x400010, 0x4000100000, 0x1000000201, 0x10000002010000, 0x20100804000, 0x1008040001008, 0x400010000002, 0x100000000100, 0x1008040, 0x10080400000, 0x804000000804, 0x8000001, 0x80000010000, 0x100004020, 0x1000040200000, 0x402000080400, 0x20000804020100, 0x8040200008000, 0x2000080400010, 0x804000000800, 0x8040001, 0x80400010000, 0x4000100004020, 0x1000040001000, 0x400010080400, 0x100804020100, 0x8040201008040, 0x2010080000010, 0x800000000004, 0x200, 0x2000000, 0x20000000000, 0x1008, 0x10080000, 0x100800000000, 0x8000000008040, 0x80002000, 0x800020000000, 0x200000040200, 0x402010080, 0x4020100800000, 0x1008000200008, 0x2000000002, 0x20000000020000, 0x201008000, 0x2010080000000, 0x800000100004}); +constexpr StatTable54 SQR8_TABLE_54({0x1, 0x10080400000, 0x100804020100, 0x1008000200008, 0x10080002000000, 0x4000000804, 0x8000201000040, 0x402000000000, 0x20100004020, 0x1000000000, 0x80000010, 0x20100800000100, 0x201008, 0x80002010080, 0x804020100000, 0x201000040, 0x2000080000, 0x4020100004000, 0x8040000, 0x10000402000, 0x20000, 0x1008000001008, 0x10080402010080, 0x4000000004, 0x40001000040, 0x10080402, 0x4020000004000, 0x40001, 0x2000080402010, 0x20000000000000, 0x1000000200000, 0x10000000000080, 0x4020100000, 0x40201008040, 0x402000080002, 0x4020000800000, 0x1000000201, 0x2000080400010, 0x100800000000, 0x8040001008, 0x400000000, 0x20000004, 0x8040200000040, 0x80402, 0x20000804020, 0x201008040000, 0x80400010, 0x800020000, 0x1008040001000, 0x2010000, 0x4000100800, 0x8000, 0x402000000402, 0x4020100804020}); +constexpr StatTable54 SQR16_TABLE_54({0x1, 0x80002010000, 0x4020000000000, 0x1000000201008, 0x402010000402, 0x20100804020000, 0x8000001000040, 0x2000000000000, 0x804020000800, 0x1000000201, 0x80002000080, 0x804020, 0x8, 0x400010080000, 0x20100000000000, 0x8000001008040, 0x2010080002010, 0x804020100804, 0x8000001, 0x10000000000000, 0x4020100004000, 0x8000001008, 0x400010000400, 0x4020100, 0x40, 0x2000080400000, 0x800000000804, 0x8040001, 0x10080400010080, 0x4020100804020, 0x40000008, 0x402, 0x20100800020000, 0x40000008040, 0x2000080002000, 0x20100800, 0x200, 0x10000402000000, 0x4000000004020, 0x40200008, 0x402000080002, 0x20100804020100, 0x200000040, 0x2010, 0x804000100804, 0x200000040200, 0x10000400010000, 0x100804000, 0x1000, 0x2010000402, 0x20000000020100, 0x201000040, 0x2010000400010, 0x804020100004}); +constexpr StatTable54 QRT_TABLE_54({0x201008000200, 0x26c10916494994, 0x26c10916494996, 0x40008008, 0x26c10916494992, 0x141a2434c12d12, 0x40008000, 0x36c00110594c22, 0x26c10916494982, 0x200000040200, 0x141a2434c12d32, 0x10010816104534, 0x40008040, 0x36da60b01308b2, 0x36c00110594ca2, 0x48200209000, 0x26c10916494882, 0x41b6da2d86106, 0x200000040000, 0x32db2c228965b0, 0x141a2434c12932, 0x9000000200048, 0x10010816104d34, 0x32db68b2832da4, 0x40009040, 0x40045928b4902, 0x36da60b01328b2, 0x1000040000, 0x36c00110590ca2, 0x101b69865a4120, 0x48200201000, 0x22da6434912884, 0x26c10916484882, 0x9000240208008, 0x41b6da2da6106, 0x22c14484c20180, 0x200000000000, 0x4016db29b6812, 0x32db2c228165b0, 0x9008200201048, 0x141a2434d12932, 0x32c36ca2c264b0, 0x9000000000048, 0x140a65b48a2c32, 0x10010816504d34, 0, 0x32db68b2032da4, 0x404490824814, 0x41009040, 0x14da60a4536126, 0x40045908b4902, 0x8000041009008, 0x36da60b41328b2, 0x6db68b2032c12}); +typedef Field Field54; +typedef FieldTri, &SQR_TABLE_54, &SQR2_TABLE_54, &SQR4_TABLE_54, &SQR8_TABLE_54, &SQR16_TABLE_54, &QRT_TABLE_54, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri54; +#endif + +#ifdef ENABLE_FIELD_INT_55 +// 55 bit field +typedef RecLinTrans StatTable55; +constexpr StatTable55 SQR_TABLE_55({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x102, 0x408, 0x1020, 0x4080, 0x10200, 0x40800, 0x102000, 0x408000, 0x1020000, 0x4080000, 0x10200000, 0x40800000, 0x102000000, 0x408000000, 0x1020000000, 0x4080000000, 0x10200000000, 0x40800000000, 0x102000000000, 0x408000000000, 0x1020000000000, 0x4080000000000, 0x10200000000000, 0x40800000000000, 0x2000000000102, 0x8000000000408, 0x20000000001020}); +constexpr StatTable55 SQR2_TABLE_55({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x102, 0x1020, 0x10200, 0x102000, 0x1020000, 0x10200000, 0x102000000, 0x1020000000, 0x10200000000, 0x102000000000, 0x1020000000000, 0x10200000000000, 0x2000000000102, 0x20000000001020, 0x10004, 0x100040, 0x1000400, 0x10004000, 0x100040000, 0x1000400000, 0x10004000000, 0x100040000000, 0x1000400000000, 0x10004000000000, 0x40000000102, 0x400000001020, 0x4000000010200, 0x40000000102000, 0x1020408, 0x10204080, 0x102040800, 0x1020408000, 0x10204080000, 0x102040800000, 0x1020408000000, 0x10204080000000, 0x2040800000102, 0x20408000001020, 0x4080000010004, 0x40800000100040, 0x8000001000008}); +constexpr StatTable55 SQR4_TABLE_55({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x10200, 0x102000000, 0x1020000000000, 0x10004, 0x100040000, 0x1000400000000, 0x4000000010200, 0x102040800, 0x1020408000000, 0x4080000010004, 0x100000010, 0x1000000100000, 0x1000010200, 0x10000102000000, 0x1020000102000, 0x1020010004, 0x10200100040000, 0x1000400100040, 0x4001000410200, 0x10004102040800, 0x41020408102000, 0x4081020418004, 0x10204180000010, 0x41800000000040, 0x10300, 0x103000000, 0x1030000000000, 0x10106, 0x101060000, 0x1010600000000, 0x6000000010302, 0x103040c00, 0x103040c000000, 0x40c0000010106, 0x101020418, 0x1010204180000, 0x2041800010302, 0x18000103000008, 0x1030000103000, 0x1030010106, 0x10300101060000, 0x1010600101060, 0x6001010610302, 0x10106103040c00, 0x6103040c103020, 0x40c103041c106, 0x103041c1020418, 0x41c10204081060, 0x2040810214282, 0x8102142800008, 0x21428000000020}); +constexpr StatTable55 SQR8_TABLE_55({0x1, 0x1000010200, 0x101060000, 0x6103040c103020, 0x1001400010200, 0x4081121478004, 0x7903050f103028, 0x1100010200, 0x1020101162000, 0x6703040c113322, 0x113055c1030618, 0x50a102353a804, 0x3e83050f11336a, 0x103040f102071e, 0x101070200, 0x7123050c143020, 0x3100c010200, 0x60c193166c286, 0x6d2b040f15302c, 0x103140f010200, 0x2070f112772e2, 0x7621050c153322, 0x143341cd420418, 0x70e193270ee9e, 0xbe9840f14334e, 0x143355fe53051e, 0x3050e103555fc, 0x5153e50f143c00, 0x1001500050200, 0x450a152957a004, 0x7b071d0f11332a, 0x1000040200, 0x5000040b060000, 0x64061a0c103020, 0x153c44f147c71e, 0x4008142b428a04, 0x2ca75c8f103078, 0x113341f117371e, 0x5402040b162000, 0x62064aac143336, 0x50c003f51ff06, 0x7cf1f3d7ef2e6, 0x6e2d180714332e, 0x103150f050100, 0x43350b1a3152e2, 0x74261e06143020, 0x113f54fd17c65e, 0x56301c3b66cd98, 0x10dff9c7953054, 0x153055fe47360e, 0x46340a1b2277fc, 0x4574bfb5753c14, 0x50f003345fc52, 0x41f653264c9962, 0x7612375be93322}); +constexpr StatTable55 SQR16_TABLE_55({0x1, 0x4ba7488f00015a, 0x30ce9d3a61c1e4, 0x4a2e76980aff84, 0x4e44f5b9d2f610, 0x7b479e4450115c, 0x248c18e86b39b2, 0x1ba74c8406015a, 0x35e93420af76aa, 0x7f282c7c68ad54, 0x7f8b356ad7bc5a, 0x527272878d3b24, 0x587495a40395a4, 0x43c4d0fd51f96c, 0x57ce893a71f0c6, 0x62c68a94803da, 0x1b32bc920e6546, 0x5073c39b469c78, 0x2fba08c009b110, 0x10bd0559ba45c, 0x3bbbd0ca4b3246, 0x243ad4b7c193b8, 0x335d7f186b5db2, 0x5590f3a0fd73f0, 0x72953f208233ba, 0x5210b9a31e6c62, 0x744bb124e351da, 0x4929f00a730244, 0x736ff5bdc1c63c, 0x4c1da1fb246e2e, 0x553c18b46d95cc, 0x268f5c8c143376, 0x438f5a59cbf094, 0x6a718b25fd3946, 0x67053b1bf54fe0, 0x441c5323cb0288, 0x5def8fcd41d5a8, 0x40446cdfcdb062, 0x1043009fb20072, 0xef08d6ed9e9c6, 0xbdf8adea645be, 0x76b092b499c072, 0xd754f98b724c2, 0x5a21d55c8f8752, 0x4f0f36a62eeb0c, 0x262f651fb93b18, 0x3336d340aa0aaa, 0x69375d0e9970fa, 0x2e0997225afe66, 0x6692008b83364e, 0x230856519bc3ae, 0x2c0a54962f8378, 0x2a6460de8a4266, 0x2f14d8fa237452, 0x25934cd7ae0030}); +constexpr StatTable55 QRT_TABLE_55({0, 0x121d57b6623fde, 0x121d57b6623fdc, 0x68908340d10e00, 0x121d57b6623fd8, 0x100300510e20, 0x68908340d10e08, 0x10004096, 0x121d57b6623fc8, 0x100010000, 0x100300510e00, 0x7ea8c890a088e8, 0x68908340d10e48, 0x68809540871648, 0x10004016, 0x68808000808068, 0x121d57b6623ec8, 0x68909240d41c48, 0x100010200, 0x6884c170ad0216, 0x100300510a00, 0x68848160a50200, 0x7ea8c890a080e8, 0x7eecbca04ab4b6, 0x68908340d11e48, 0x120c54b62234c8, 0x68809540873648, 0x69929240d61c48, 0x10000016, 0x68808060800000, 0x68808000800068, 0x80000080, 0x121d57b6633ec8, 0x7ea8cb90a18ae8, 0x68909240d61c48, 0x16284090200080, 0x100050200, 0x474302a345e, 0x6884c170a50216, 0x166cbca0cab4de, 0x100300410a00, 0x1000000000000, 0x68848160850200, 0x688cc1f0a50296, 0x7ea8c890e080e8, 0x7e8cc1f0a50280, 0x7eecbca0cab4b6, 0x68000000000068, 0x68908341d11e48, 0x7880954487365e, 0x120c54b42234c8, 0x9929248d61c20, 0x68809544873648, 0x41121208561c20, 0x69929248d61c48}); +typedef Field Field55; +typedef FieldTri, &SQR_TABLE_55, &SQR2_TABLE_55, &SQR4_TABLE_55, &SQR8_TABLE_55, &SQR16_TABLE_55, &QRT_TABLE_55, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri55; +#endif + +#ifdef ENABLE_FIELD_INT_56 +// 56 bit field +typedef RecLinTrans StatTable56; +constexpr StatTable56 SQR_TABLE_56({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x95, 0x254, 0x950, 0x2540, 0x9500, 0x25400, 0x95000, 0x254000, 0x950000, 0x2540000, 0x9500000, 0x25400000, 0x95000000, 0x254000000, 0x950000000, 0x2540000000, 0x9500000000, 0x25400000000, 0x95000000000, 0x254000000000, 0x950000000000, 0x2540000000000, 0x9500000000000, 0x25400000000000, 0x95000000000000, 0x5400000000012a, 0x5000000000043d, 0x40000000001061}); +constexpr StatTable56 SQR2_TABLE_56({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x95, 0x950, 0x9500, 0x95000, 0x950000, 0x9500000, 0x95000000, 0x950000000, 0x9500000000, 0x95000000000, 0x950000000000, 0x9500000000000, 0x95000000000000, 0x5000000000043d, 0x4111, 0x41110, 0x411100, 0x4111000, 0x41110000, 0x411100000, 0x4111000000, 0x41110000000, 0x411100000000, 0x4111000000000, 0x41110000000000, 0x11100000000254, 0x110000000025d5, 0x10000000025dc5, 0x25dcc5, 0x25dcc50, 0x25dcc500, 0x25dcc5000, 0x25dcc50000, 0x25dcc500000, 0x25dcc5000000, 0x25dcc50000000, 0x25dcc500000000, 0x5dcc500000012a, 0xdcc50000001061, 0xcc500000010079, 0xc500000010016c, 0x5000000100103c}); +constexpr StatTable56 SQR4_TABLE_56({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x9500, 0x95000000, 0x950000000000, 0x4111, 0x41110000, 0x411100000000, 0x110000000025d5, 0x25dcc500, 0x25dcc5000000, 0xdcc50000001061, 0x10010101, 0x100101010000, 0x1010100000950, 0x1000009509595, 0x95095959500, 0x5095959500043d, 0x95950004115111, 0x41151505011, 0x11515050110254, 0x505011025de985, 0x11025de9a93c10, 0x5de9a93c19c42a, 0xa93c19c400005d, 0x19c4000001000c, 0x100010094, 0x1000100940000, 0x1009400009500, 0x94000095009500, 0x950095418400, 0x954184004111, 0x41840041114111, 0x411141349dd4, 0x1141349dd425d5, 0x349dd425dce0d5, 0xd425dce0cce1b9, 0xdce0cce1ddd461, 0xcce1ddd4011160, 0xddd401110941f5, 0x1110941959dc4, 0x941959dc49cc5, 0x959dc49cc118d5, 0xc49cc1189454b9, 0xc1189454d4d12c, 0x9454d4d14358f8, 0xd4d14358b9aa44, 0x4358b9aa20a205, 0xb9aa20a221d7b8, 0x20a221d7ed10a2, 0x21d7ed10b0f90a, 0xed10b0f918507b, 0xb0f91850000050, 0x1850000001000d}); +constexpr StatTable56 SQR8_TABLE_56({0x1, 0x1010100000950, 0x950095418400, 0xd4d14358b9aa44, 0x1135dd851025d5, 0x2c3e45b8a8a9d9, 0xcc39c4d816cc89, 0x51109400, 0x8496c8edb8f151, 0x1c2d7d88406199, 0x3856af0918b2ea, 0x2c26c02be43364, 0x7c13f0a9492898, 0x887abc757e3b3c, 0x10100411009c5, 0x850b98e029a995, 0x18309e7d346f24, 0x49e692600134d8, 0xf902789abce101, 0xed998d1d57187b, 0xa5488e663e846e, 0x84267921a952d0, 0x3d464f2d15176e, 0x801aac9d710b04, 0xfc00d6eb916343, 0x5c7fb78f391c1, 0x745ee236e80ea0, 0x81f8c65be65eac, 0x1940095415941, 0x1188025e2103d0, 0x49c166e0b13f34, 0xbd26558f28a2b3, 0xe147d131ae4b81, 0x25b501ad8ba86d, 0x75fb4e24c70a79, 0x88172901f1684d, 0xb090520bb570a2, 0x963c9b97aad59, 0x39b1e5f12c85a6, 0xd90de4c2bf3055, 0x9c921257e4a1b, 0x45f1f318fef834, 0x48161e1eb09635, 0x10685b397538ce, 0xbc8d4a0c6bc62a, 0xdce738247bfad9, 0x1115b3337d25a4, 0x195bf5a6f0999b, 0xb85101388b2f37, 0x8ee1b2833544cf, 0x49bc1efef7da90, 0x346e404662e355, 0x8c0dab6a1217d6, 0x3cb782ec54c968, 0x5efe07d4f59f4, 0x55f19c0f482900}); +constexpr StatTable56 SQR16_TABLE_56({0x1, 0x2563e0b70105c4, 0x48ce07ef5576bb, 0xb94064d844f117, 0x207d2f511ffe3c, 0xf8f6dd1e2a3e6b, 0xe4cc405e0c6cdb, 0xd053f9b827b2bf, 0x550ae8d22edcbf, 0x29f7570f88728b, 0xa06a9e2dfd84a6, 0x55567b9483b3ff, 0x197c6c0d004df6, 0xe106c03f218a16, 0xc50dd2aaf0a388, 0x39473f6702a06c, 0xc8c1736b312ded, 0x992dc692bb707d, 0x24bb9a8dcad06f, 0x9cc45f9e3c2075, 0x455e7271eb130b, 0x847157a5326f59, 0xdc8ccb4ab3f5bd, 0x9463c02c46910d, 0xe1debd0b794514, 0x4c5128db660cde, 0x11910a685416a3, 0x11dfa5b9552a3d, 0x5d902ced822708, 0x794ff735e94601, 0xf1dc5fd7efcf7e, 0x19fb7ff8d06993, 0x7069119ac28a09, 0x98ba5a77d83e7f, 0xf4923dbc1b24e5, 0x7c2dcc84668312, 0xc27e2f5f2243f, 0x78c6d8ebe4bede, 0xad39495debf1a5, 0xa1564b894b50f0, 0x5898ae4e965be9, 0x28aa991e046567, 0x585e95889bb734, 0xc59e73661cf916, 0xed70696926d95d, 0xcca6630954309a, 0x8c4b12ac111264, 0xe8413ad0493e05, 0x1acea73bc9166, 0x9a7f11cd38d12d, 0x390dd1972139ec, 0x9146bc1a4fbff0, 0xd5a1c594335b01, 0x2566272e74ef1a, 0xd4a8baf259e7d0, 0x71e7efd8f20703}); +constexpr StatTable56 QRT_TABLE_56({0x10004084, 0xd058f12fd5925e, 0xd058f12fd5925c, 0x41a60b5566d9f0, 0xd058f12fd59258, 0xbda60a142740ba, 0x41a60b5566d9f8, 0xd059f1afc5e688, 0xd058f12fd59248, 0xfc040841615a22, 0xbda60a1427409a, 0xbda60b5426c1ca, 0x41a60b5566d9b8, 0x1a60b4166b950, 0xd059f1afc5e608, 0xfc000041409822, 0xd058f12fd59348, 0xd1ee7a4ef4185c, 0xfc040841615822, 0x9049759b80b4a4, 0xbda60a1427449a, 0xd258e06f301e18, 0xbda60b5426c9ca, 0x6dfeeb3bf6d7d2, 0x41a60b5566c9b8, 0xbdef3ed4ae398a, 0x1a60b41669950, 0xd1ef3f8eeff04c, 0xd059f1afc5a608, 0xbda203340783de, 0xfc000041401822, 0x2dfefbaff2b27a, 0xd058f12fd49348, 0xfdb788a0706776, 0xd1ee7a4ef6185c, 0x2e5de0ae41337a, 0xfc040841655822, 0x41eb17d5ceecf8, 0x9049759b88b4a4, 0x40048874211afc, 0xbda60a1437449a, 0xd04a720f93400c, 0xd258e06f101e18, 0xbc559cf5ac7fce, 0xbda60b5466c9ca, 0x6dc9759b88b4d6, 0x6dfeeb3b76d7d2, 0x92feea7b275af0, 0x41a60b5466c9b8, 0, 0xbdef3ed6ae398a, 0x2811d5edd8ee2a, 0x1a60b45669950, 0xb1a60b5466c9ca, 0xd1ef3f86eff04c, 0xec493582c8f032}); +typedef Field Field56; +#endif +} + +Sketch* ConstructClMul7Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_49 + case 49: return new SketchImpl(implementation, 49); +#endif +#ifdef ENABLE_FIELD_INT_50 + case 50: return new SketchImpl(implementation, 50); +#endif +#ifdef ENABLE_FIELD_INT_51 + case 51: return new SketchImpl(implementation, 51); +#endif +#ifdef ENABLE_FIELD_INT_52 + case 52: return new SketchImpl(implementation, 52); +#endif +#ifdef ENABLE_FIELD_INT_53 + case 53: return new SketchImpl(implementation, 53); +#endif +#ifdef ENABLE_FIELD_INT_54 + case 54: return new SketchImpl(implementation, 54); +#endif +#ifdef ENABLE_FIELD_INT_55 + case 55: return new SketchImpl(implementation, 55); +#endif +#ifdef ENABLE_FIELD_INT_56 + case 56: return new SketchImpl(implementation, 56); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri7Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_49 + case 49: return new SketchImpl(implementation, 49); +#endif +#ifdef ENABLE_FIELD_INT_52 + case 52: return new SketchImpl(implementation, 52); +#endif +#ifdef ENABLE_FIELD_INT_54 + case 54: return new SketchImpl(implementation, 54); +#endif +#ifdef ENABLE_FIELD_INT_55 + case 55: return new SketchImpl(implementation, 55); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_8bytes.cpp b/src/fields/clmul_8bytes.cpp new file mode 100644 index 0000000000..8dc1089fee --- /dev/null +++ b/src/fields/clmul_8bytes.cpp @@ -0,0 +1,175 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_8) + +#include "clmul_common_impl.h" + +#include "../int_utils.h" +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_57 +// 57 bit field +typedef RecLinTrans StatTable57; +constexpr StatTable57 SQR_TABLE_57({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x22, 0x88, 0x220, 0x880, 0x2200, 0x8800, 0x22000, 0x88000, 0x220000, 0x880000, 0x2200000, 0x8800000, 0x22000000, 0x88000000, 0x220000000, 0x880000000, 0x2200000000, 0x8800000000, 0x22000000000, 0x88000000000, 0x220000000000, 0x880000000000, 0x2200000000000, 0x8800000000000, 0x22000000000000, 0x88000000000000, 0x20000000000011, 0x80000000000044}); +constexpr StatTable57 SQR2_TABLE_57({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x88, 0x880, 0x8800, 0x88000, 0x880000, 0x8800000, 0x88000000, 0x880000000, 0x8800000000, 0x88000000000, 0x880000000000, 0x8800000000000, 0x88000000000000, 0x80000000000044, 0x404, 0x4040, 0x40400, 0x404000, 0x4040000, 0x40400000, 0x404000000, 0x4040000000, 0x40400000000, 0x404000000000, 0x4040000000000, 0x40400000000000, 0x4000000000022, 0x40000000000220, 0x2222, 0x22220, 0x222200, 0x2222000, 0x22220000, 0x222200000, 0x2222000000, 0x22220000000, 0x222200000000, 0x2222000000000, 0x22220000000000, 0x22200000000011, 0x22000000000101, 0x20000000001001}); +constexpr StatTable57 SQR4_TABLE_57({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x880, 0x8800000, 0x88000000000, 0x80000000000044, 0x404000, 0x4040000000, 0x40400000000000, 0x22220, 0x222200000, 0x2222000000000, 0x20000000001001, 0x10001000, 0x100010000000, 0x100000000088, 0x880088, 0x8800880000, 0x88008800000000, 0x88000000040400, 0x404040400, 0x4040404000000, 0x4040000002222, 0x22222222, 0x222222220000, 0x22222200000101, 0x22000001000001, 0x10000000100, 0x100000001000000, 0x10000088000, 0x100000880000000, 0x8800000088000, 0x880004040, 0x8800040400000, 0x404000004040, 0x40000040400220, 0x404002222000, 0x40022220000220, 0x22200002222011, 0x22220100010, 0x22201000100011, 0x10001000100010, 0x10001000108800, 0x10001088008800, 0x10880088008800, 0x880088008c04, 0x88008c040404, 0x8c0404040404, 0x4040404040426, 0x4040404262222, 0x4042622222222, 0x26222222222222, 0x22222222232201, 0x22222322000001, 0x23220000000001}); +constexpr StatTable57 SQR8_TABLE_57({0x1, 0x100010000000, 0x100000880000000, 0x88008c040404, 0x80000000022264, 0x26262604000101, 0x50023220100230, 0x222aa222000001, 0x20000404041401, 0x100404003222000, 0x32aa22aa23aa01, 0x2326048800088, 0x808100222ea722, 0x508e36ec548e34, 0x26222000022223, 0x22000223200001, 0x32001001108801, 0x91001000108844, 0x85048c04880044, 0x4c86ae64c80220, 0x6ea40546662003, 0x66662726ae22ab, 0x40622aa2aa40c8, 0x26063ea6364477, 0x2406744c950437, 0x8a33606aa727aa, 0xd09332ca7e2d9a, 0x7e2c14ce3e6c17, 0x22aa2626260405, 0x22200002626011, 0x27260000002223, 0x22208812aa3011, 0x2ba30040488001, 0x48c8c9caebe26, 0x28898040489001, 0x1048c008922aa26, 0x30c0c08a22b801, 0x12222666fa72601, 0x9afa60a8048eaa, 0x6aa1400afba131, 0xbc9c168c00800d, 0xc083aa60588bb0, 0xbeeefeae26e6e7, 0x8c44ee22226ae2, 0x8c44ee32205042, 0x2667443228d143, 0x36af14ba381d17, 0x72eb33981a3f35, 0x72c911aab20d9d, 0x72ca2b2aaacccd, 0x66463fae3f44ff, 0xac345eee3b4077, 0xe4977caefbe1fd, 0xf01b70a0dd0f9a, 0xf40f74bc580bbd, 0x17e2d1c6c5c0f35, 0x3624140232aa33}); +constexpr StatTable57 SQR16_TABLE_57({0x1, 0xaeeec814065447, 0x110889c99ba3004, 0x1c59ed582798e0e, 0x1c9c766272a2a74, 0x1422928a5250a00, 0x2c97eb48f402a1, 0x10c6d916b128dea, 0x3f2e6ca66ebf67, 0x93ac75fcd63ec8, 0x19263128e42246, 0x1fd1ca54b556091, 0x60ff38200c4e09, 0x1381808ede9982b, 0xc7a9ace2f9dc2a, 0x6c2ee414271c57, 0x3c16f4cffdbe17, 0xc627ec6fe179ee, 0x178f994adf6525b, 0x18be0b635ca1650, 0x4afcb2ae2e98b6, 0x6f81f53a7688dd, 0x45319b3e02c15c, 0x1044be090058910, 0xaa02d012fca063, 0x11fba4c5b80dbfa, 0xf9f44be142268b, 0x1e351a44eb480bf, 0xacf5c17bd0aedf, 0x6f2d74bab851eb, 0x1b8ac3589da9915, 0x1afeb885d3fdd67, 0x7d7d596dd60bbd, 0x1329567316f5723, 0xfdfe23b549fcef, 0xc985ed1e7a009e, 0x71f794bbac1b03, 0xc740582125f7d0, 0x1b3584e031e3b77, 0x29978a3c559ed3, 0xde04d46b4ae516, 0x2f6d6e1c749405, 0x1ec95b44a4251f3, 0xb95b0a5f451f2d, 0x1dc80aedaab9bf2, 0xd0354d3ff74808, 0x180889b484b0364, 0x196895708367d90, 0x104575064a09414, 0x19e88f14fc111ec, 0x1cf4088d3cffd88, 0x1e6c28b9a76c6d5, 0x81ba060c9e485e, 0x12b811107188d68, 0x5e6f10ca82cd88, 0x120882748af043d, 0x145fb82467c596e}); +constexpr StatTable57 QRT_TABLE_57({0xd0c3a82c902426, 0x232aa54103915e, 0x232aa54103915c, 0x1763e291e61699c, 0x232aa541039158, 0x1f424d678bb15e, 0x1763e291e616994, 0x26fd8122f10d36, 0x232aa541039148, 0x1e0a0206002000, 0x1f424d678bb17e, 0x5d72563f39d7e, 0x1763e291e6169d4, 0x1519beb9d597df4, 0x26fd8122f10db6, 0x150c3a87c90e4aa, 0x232aa541039048, 0x15514891f6179d4, 0x1e0a0206002200, 0x14ec9ba7a94c6aa, 0x1f424d678bb57e, 0x1e0f4286382420, 0x5d72563f3957e, 0x4000080000, 0x1763e291e6179d4, 0x1ac0e804882000, 0x1519beb9d595df4, 0x1f430d6793b57e, 0x26fd8122f14db6, 0x3c68e806882000, 0x150c3a87c9064aa, 0x1484fe18b915e, 0x232aa541029048, 0x14f91eb9b595df4, 0x15514891f6379d4, 0x48f6a82380420, 0x1e0a0206042200, 0x14b1beb99595df4, 0x14ec9ba7a9cc6aa, 0x4cf2a82b00420, 0x1f424d679bb57e, 0x26aa0002000000, 0x1e0f4286182420, 0x173f1039dd17df4, 0x5d72563b3957e, 0x4aa0002000000, 0x4000880000, 0x16d31eb9b595df4, 0x1763e291f6179d4, 0x20000000000000, 0x1ac0e806882000, 0x2caa0002000000, 0x1519beb99595df4, 0, 0x1f430d6f93b57e, 0x73e90d6d93b57e, 0x26fd8132f14db6}); +typedef Field Field57; +typedef FieldTri, &SQR_TABLE_57, &SQR2_TABLE_57, &SQR4_TABLE_57, &SQR8_TABLE_57, &SQR16_TABLE_57, &QRT_TABLE_57, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri57; +#endif + +#ifdef ENABLE_FIELD_INT_58 +// 58 bit field +typedef RecLinTrans StatTable58; +constexpr StatTable58 LOAD_TABLE_58({0x1, 0x77dd188d5d600, 0x41a7213270def0, 0x10921661867b40a, 0x880b92a6f74da3, 0x143e72cde8f4484, 0x1863cb65c3eee0e, 0x2a1aa4a82154057, 0x22265135db9e135, 0x1446f023770d6d0, 0x183c4be7b4fae6, 0x1cc8a1187e99bd4, 0x3a1ea282e1e2ff1, 0x2700aae9dbcd275, 0x1571f84428a416e, 0xc8eb0b234b8a57, 0x23227afc0d9ba75, 0x1de9497779018c7, 0x5898e896d43329, 0x1501bd1b83bb55f, 0xbb56c28ce180b, 0x188e087d2dbf7e0, 0x36eee77125d8ec9, 0xde4235cbe95166, 0x1c71e4d57306163, 0x2a7e1b1ae5d87ee, 0x3a685560450c909, 0x1cd0545bc185c4b, 0x151779b11f09892, 0x2ab069803c4d787, 0x3bf279c0b825ad5, 0x15edc1ef3d2513c, 0x37bf223b4d0d045, 0x262a786b0324cd3, 0x27658294b696713, 0x33771167b0137e8, 0x86a73ef2dc3271, 0xc64453d2ff05, 0x14c55bc975ce8c, 0x3581164b1e4826e, 0x461dde5468bc26, 0x3f31528346e9451, 0x3f2669a5324a555, 0xd0b1c042854400, 0x32cc9899ea263e3, 0x2a423a8a96f4e95, 0x3e64acd727b470b, 0x2f1f1c1de9c997d, 0x268f2df0a8ab060, 0x14fd82231712442, 0x106e14e5e9e8f8c, 0x2686a15d911a24c, 0x182f831c5142b40, 0x36ca3e60fc7c678, 0x22ba581841d83ff, 0x11539696ce13d17, 0x558c3670aecb8e, 0x2b9b2d828d6d864}); +constexpr StatTable58 SAVE_TABLE_58({0x1, 0xde56167ca60c8e, 0x166391328282dfb, 0x2c6ed660535e701, 0xf83470e499e0e, 0x3686f752cff05fd, 0x1ada23d28022d0b, 0x2a2ac069b41ffd4, 0x2d40f316b40053e, 0x3fb69372877a1f3, 0x13def6e665e9b30, 0x23eb4222bc98b90, 0x2991c5ab618f62c, 0x1c4b63ee1e37a86, 0xdcb10179c77602, 0x708837c2f0ee59, 0x151fe1b533a6d99, 0x44613653cb9d83, 0x33dc64f2b5abae6, 0x27d704726f1f9ba, 0x2fdef2de96892ad, 0x3fd032a21834dbe, 0x1ce2548191e42ab, 0x431410a40ab44b, 0x206f1338c9a75e1, 0x130035675a32179, 0xdf781bb8adbd09, 0x1aaaf085ea624e0, 0x1df0605123c28e9, 0x28d3b3631320c9c, 0x81951a3af55e95, 0x388c776adc88ca1, 0x3ebed178f719885, 0x3c4546b19b0fe51, 0x129564a29700d09, 0x3f642277d82c520, 0x3a46d24ff0ac3fd, 0x1e75e367d627740, 0x33b01746a0f4aad, 0x2af930ca2fa61f, 0x3fcea0ca3af7aac, 0x230722de56e3f4a, 0x3541e58cc5afefd, 0x32cf711ae15ba7e, 0x11d3670d510fc6f, 0x6ddd78eec82112, 0x216210641885856, 0x87535f37c08809, 0x1fa464b5f82155b, 0xdbd43e91708494, 0x1500e23396dd2c4, 0x16cf4098632235f, 0x37e9117da8979ba, 0x6f8bfa04f66a7, 0x18dff008060e626, 0x196286fd9dbad1c, 0x35078156610f8ab, 0x7a669ff8398fea}); +constexpr StatTable58 SQR_TABLE_58({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x63, 0x18c, 0x630, 0x18c0, 0x6300, 0x18c00, 0x63000, 0x18c000, 0x630000, 0x18c0000, 0x6300000, 0x18c00000, 0x63000000, 0x18c000000, 0x630000000, 0x18c0000000, 0x6300000000, 0x18c00000000, 0x63000000000, 0x18c000000000, 0x630000000000, 0x18c0000000000, 0x6300000000000, 0x18c00000000000, 0x63000000000000, 0x18c000000000000, 0x230000000000063, 0xc000000000014a, 0x300000000000528}); +constexpr StatTable58 SQR2_TABLE_58({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x18c, 0x18c0, 0x18c00, 0x18c000, 0x18c0000, 0x18c00000, 0x18c000000, 0x18c0000000, 0x18c00000000, 0x18c000000000, 0x18c0000000000, 0x18c00000000000, 0x18c000000000000, 0xc000000000014a, 0x1405, 0x14050, 0x140500, 0x1405000, 0x14050000, 0x140500000, 0x1405000000, 0x14050000000, 0x140500000000, 0x1405000000000, 0x14050000000000, 0x140500000000000, 0x50000000001ef, 0x50000000001ef0, 0x10000000001ef63, 0x1ef7bc, 0x1ef7bc0, 0x1ef7bc00, 0x1ef7bc000, 0x1ef7bc0000, 0x1ef7bc00000, 0x1ef7bc000000, 0x1ef7bc0000000, 0x1ef7bc00000000, 0x1ef7bc000000000, 0x2f7bc0000000129, 0x37bc0000000112d, 0x3bc000000011027, 0x3c0000000110022}); +constexpr StatTable58 SQR4_TABLE_58({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x18c0, 0x18c00000, 0x18c000000000, 0xc000000000014a, 0x1405000, 0x14050000000, 0x140500000000000, 0x1ef7bc, 0x1ef7bc0000, 0x1ef7bc00000000, 0x3bc000000011027, 0x110001100, 0x1100011000000, 0x11000000194c, 0x1000000194c018c, 0x194c0194c000, 0x14c0194c000014a, 0x194c00001545500, 0x15455154550, 0x154551545500000, 0x1154550001f18df, 0x150001f18c63193, 0x1f18c6318c7c00, 0xc6318c7c01010a, 0x18c7c0101000014, 0x1010000000101, 0x1000000010118c0, 0x10118d8c000, 0x10118d8c0000000, 0xd8c0000018d98a, 0x18d9811050, 0x18d98110500000, 0x18110500001411a, 0x500001410eb94c, 0x1410eb94bbc00, 0x10eb94bbc001ef0, 0x14bbc001ee85ab2, 0x1ee85aac1111, 0x2e85aac11110129, 0x2ac11110111097a, 0x111011109445c8c, 0x11109445c9554c0, 0x1445c9554d95406, 0x9554d954189419, 0xd954189414901f, 0x1894149014051f, 0x149014051e478f, 0x14051e478ee2ec, 0x11e478ee2edef63, 0x38ee2edef7aded3, 0x2edef7adef6bdc8, 0x37adef6bdf07c2d, 0x2f6bdf07c0018f9, 0x1f07c0018c018d1}); +constexpr StatTable58 SQR8_TABLE_58({0x1, 0x1100011000000, 0x10118d8c0000000, 0xd954189414901f, 0xc018def7a2f6f6, 0x1e19cc6d44444e, 0x2b2e8450d1ef706, 0x196294c624791e5, 0x2a9441aa2b74da8, 0x1c6810fa7a2fe66, 0x4e0f0eff6badbb, 0x26faf3a76e59127, 0x11aa58d6498919f, 0x3b3ce4e04f23b30, 0x2ed3f70684ae8d7, 0x8d64c737fc5014, 0x1516c589c4fd458, 0x5ba5cee14ea182, 0x368ad344d93d4ae, 0x15e2547ea25ba2a, 0xdecb4283969d9a, 0x2f2a95e5c791149, 0x3fc958586bc93d2, 0x3216bfeab663783, 0xced412d3f6e530, 0x85fb7bcb26d797, 0x19be97fdbcede01, 0x192a5409529ebf4, 0x98b4f8527795b1, 0x192e8188bbc9aac, 0x322f07e9abdf6d0, 0x2a4a5cd6239de91, 0x4c97dec82e63a, 0x37d6397e1d26aee, 0x1939dc6d77a98db, 0x2e23b8e5b0a982a, 0x2787751f5aa0dba, 0x3f81252033f3cc8, 0x1171b73d009f511, 0x8811f0328040bb, 0x3a659ae0b1d2417, 0xc8b454d91baa72, 0x197b01428520b86, 0x1872c8c17f7fe81, 0x143f7913f4c7f5, 0x3a71b7542e7ec68, 0x3e60d3d49155d34, 0x11d5f10402402c8, 0x2be8db11809a1df, 0x3667129f17b1d6a, 0x2749715db24cd0a, 0x185d6130cdfee96, 0x3abdc4d78640154, 0x39bd5ea2e22f89a, 0x3f9a113c1095209, 0xdb4c8bd4f72f4e, 0x32ae35f0ad0b4ee, 0x2f3770997f16cc}); +constexpr StatTable58 SQR16_TABLE_58({0x1, 0x2fcd2228a7c16ab, 0x846ef4a277243e, 0x1d2bf9061084cfb, 0x23598fddcd64f64, 0xcc36f3a7174e2a, 0x365a50c11b89583, 0x611bed1afae48, 0x22a03fae7957244, 0x45e6546308ff3e, 0x3aebd6f3893b9b4, 0x2bf4a9d5586f8db, 0x32fd7d2d5d6f867, 0x14819feeb813a6f, 0x100ab4d9ad808fc, 0x11c0fd674209c71, 0x3701211690581e7, 0x5c33087013a39a, 0x188935ebbc048bb, 0x10787f930a52538, 0xd49849206986b2, 0x17d1298ba5b565, 0x5d465e006f3142, 0x569a5ce90e9bff, 0x2b591716524b4cb, 0x32f7d39faa352cf, 0x10f701fea440dc0, 0x11c5f10a9d3c9d5, 0x18457154a0bf6ea, 0x15516f140726673, 0x1cf780781353aa4, 0x2a7d7e0e83c4bbc, 0x276c009e3198958, 0x220b8531adc2c11, 0x937d7effc370ab, 0x27632fc1b91dac1, 0x3b36628aa135d3f, 0x37230eddd77f21a, 0x1c1b5e0f410eca9, 0x3200c9c78a9127f, 0x3a55e6fb19e6dc4, 0x150cb064eb271f7, 0x5c74759db43ae1, 0x37046240fba02a9, 0x1937118eb920f04, 0x2795ad9a663a0c9, 0x1d4297ad3d62e8a, 0x3b927d82816e04d, 0x15b56f89c278c21, 0x2b8e4ef675619d6, 0x2e0823575b9bb28, 0xdeb4b405ed7e9c, 0x83d627c04e5155, 0x391134c52f7ae67, 0x9e2c9657999608, 0x3b1e574e9a4eb3a, 0x2b58dd062cd0021, 0x38d1fb86f1978ab}); +constexpr StatTable58 QRT_TABLE_58({0x21b9dfe73454bc2, 0x351ca3a13788360, 0x351ca3a13788362, 0x1ad5a042934094, 0x351ca3a13788366, 0x48f62c33f34cc, 0x1ad5a04293409c, 0x14b1f9a41eb8342, 0x351ca3a13788376, 0x3682437996f7786, 0x48f62c33f34ec, 0x21ad5a152920174, 0x1ad5a0429340dc, 0x3766ef998858a86, 0x14b1f9a41eb83c2, 0x151ca3a437843c2, 0x351ca3a13788276, 0x1e5ac7c1aff42c, 0x3682437996f7586, 0x3767ee558c7856a, 0x48f62c33f30ec, 0x39fb408a690330, 0x21ad5a152920974, 0x372f1d7dbf4255a, 0x1ad5a0429350dc, 0x39bb888af33330, 0x3766ef99885aa86, 0x27b58e0ba2df00, 0x14b1f9a41ebc3c2, 0x1540d06c191bcf2, 0x151ca3a4378c3c2, 0x39ee0d0a17f4c0, 0x351ca3a13798276, 0x2049f6c5379fdb4, 0x1e5ac7c1adf42c, 0x1ac5a182d64bf0, 0x3682437996b7586, 0x16cbe3c0a2c7c1e, 0x3767ee558cf856a, 0x372a1d35b20aa6a, 0x48f62c32f30ec, 0x26ab144a891cdc, 0x39fb408a490330, 0x205df3712ae76a8, 0x21ad5a152d20974, 0x34fb58f12e386b6, 0x372f1d7db74255a, 0x21b4a5f53871674, 0x1ad5a0439350dc, 0x1d602e40318fdc, 0x39bb8888f33330, 0x179bb8888f3332e, 0x3766ef99c85aa86, 0x2cec9eb2f5d0aa8, 0x27b58e03a2df00, 0x6caa1452491cdc, 0x14b1f9a51ebc3c2, 0}); +typedef Field Field58; +typedef RecLinTrans StatTableTRI58; +constexpr StatTableTRI58 SQR_TABLE_TRI58({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x80001, 0x200004, 0x800010, 0x2000040, 0x8000100, 0x20000400, 0x80001000, 0x200004000, 0x800010000, 0x2000040000, 0x8000100000, 0x20000400000, 0x80001000000, 0x200004000000, 0x800010000000, 0x2000040000000, 0x8000100000000, 0x20000400000000, 0x80001000000000, 0x200004000000000, 0x10000100002, 0x40000400008, 0x100001000020, 0x400004000080, 0x1000010000200, 0x4000040000800, 0x10000100002000, 0x40000400008000, 0x100001000020000}); +constexpr StatTableTRI58 SQR2_TABLE_TRI58({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x200004, 0x2000040, 0x20000400, 0x200004000, 0x2000040000, 0x20000400000, 0x200004000000, 0x2000040000000, 0x20000400000000, 0x200004000000000, 0x40000400008, 0x400004000080, 0x4000040000800, 0x40000400008000, 0x4000000001, 0x40000000010, 0x400000000100, 0x4000000001000, 0x40000000010000, 0x180001, 0x1800010, 0x18000100, 0x180001000, 0x1800010000, 0x18000100000, 0x180001000000, 0x1800010000000, 0x18000100000000, 0x180001000000000, 0x10000300006, 0x100003000060, 0x1000030000600, 0x10000300006000, 0x100003000060000, 0x30000400004, 0x300004000040, 0x3000040000400, 0x30000400004000, 0x300004000040000, 0x4000020000c, 0x4000020000c0, 0x4000020000c00, 0x4000020000c000}); +constexpr StatTableTRI58 SQR4_TABLE_TRI58({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x2000040, 0x20000400000, 0x200004000000000, 0x40000400008000, 0x4000000001000, 0x18000100, 0x180001000000, 0x10000300006, 0x100003000060000, 0x30000400004000, 0x4000020000c00, 0x200004000100, 0x40001400008, 0x14000000001, 0x140000000010000, 0x380005000, 0x3800050000000, 0x5000070000e0, 0x70000400014, 0x3000040001c0001, 0x40001a0001c000, 0x1a000140001000, 0x1400024000680, 0x240004000050, 0x40000180009, 0x1800010001, 0x18000100010000, 0x1000130000600, 0x1300004000040, 0x4000220004c, 0x22000440001, 0x220004400010000, 0x44000540008800, 0x5400000001100, 0x1b800150, 0x1b8001500000, 0x380015000300006, 0x15000370006e000, 0x37000440005400, 0x440003a000dc0, 0x3a0005400110, 0x20005400160000e, 0x140016400068001, 0x164000400015000, 0x4000398005900, 0x3980051000100, 0x5100063000e6, 0x100063000460014, 0x2300044001c4001, 0x44001820018c00, 0x18200104001100, 0x1040021400608, 0x214004000041, 0x140040000010008}); +constexpr StatTableTRI58 SQR8_TABLE_TRI58({0x1, 0x40001400008, 0x1300004000040, 0x4000398005900, 0x21a004140001000, 0x15007370046e014, 0x14004001b810158, 0x18022100441001, 0x1005130063600e6, 0x4200004181059, 0x447a1a3f41ccd0, 0x383d151573100e6, 0x101600544019940, 0x270027c0059c000, 0x4818070100e1, 0x20545402560168e, 0x76071e40419414, 0x38001501bb00157, 0x18054101401149, 0x104343116260a0c, 0x17b9c06c9180809, 0x35b793b6107d791, 0x2e706624276b452, 0x3a543cf805118, 0x278004063619444, 0x22aae45555d4155, 0x105597ba5075e80, 0x364504676052ce, 0x35800790000401, 0x264044141418809, 0x3313274051a405d, 0x52c1b85195848, 0x57f03b8205e9e, 0x22c5044070044e0, 0x1547370047f115, 0x10402383a848d51, 0x16220024510a1, 0x4050579e30c9e7, 0x15e201b4605c018, 0x297e7fd6e672cc2, 0x286f01429f08ff7, 0x31c56646279854c, 0x36fd34ece6e98e6, 0x31e6939431f00b9, 0x311386d18673a0a, 0x2b6524f5cf195aa, 0x2dd63711ff50016, 0x1585649073391ae, 0x1004431143e1ab5, 0x13be61cf659d4d9, 0x98a87036371777, 0x66673706472d14, 0x273867fcbd99159, 0x27c4c58464098e9, 0x347304213c56db, 0x721f05c140cc15, 0x38144503ed007d9, 0x2e054541404549}); +constexpr StatTableTRI58 SQR16_TABLE_TRI58({0x1, 0x3f4d56f7779e1f0, 0xe27368ee2eeacd, 0x135c653e9699a2f, 0x6b0f78c5b96a46, 0x25fa3044c7e0248, 0x2a078335aa8c788, 0x2b2fb5e8ec09222, 0x214fe2bd0b14a22, 0x10b6f34977f0f41, 0x3dc4a1564361cee, 0xa2ae7c793a9fcf, 0x7fc45e1a362304, 0x3ec19729047ce58, 0x1ef9b26acd27396, 0x225a72a9b2db21a, 0xaaa90ccba715d8, 0x2da6362d54cd62, 0x37dae1e3484d433, 0x1ced37972ce3594, 0x164d907773ab8b9, 0xbeaf6f3fc883a1, 0x1d8ac7ee4682652, 0x102fa1481f0470a, 0x3e17062fd515fba, 0x21652276c35fe65, 0x57862a59d3fa78, 0x36b077a8057cde3, 0x287ce593d9cee2f, 0x290b965ae5d215a, 0x2cc2a18d887125c, 0xc46c603fd8423b, 0xdcd705a0e16776, 0x3307e00c6585a3f, 0x2d82d4b6c18532d, 0x28efe74f174d530, 0x2ddbc57b95adaac, 0x31d41679a107eb4, 0x1f24f6f872cb97f, 0x32718f9b0a03ff6, 0x1f283546f68ca0c, 0x158f309c150c885, 0x1ccaf78ea1873ea, 0x30e3b732bf1875f, 0xcce47efdb9ecb1, 0xcf3954987b5601, 0xebdc136185c456, 0x388046727963e11, 0x22e117909faee51, 0x3215b67613a2a60, 0x172480d3a2f11de, 0x382552280610b4d, 0x3c53c5d9c350cce, 0x6edc0d3330295e, 0x3452a6b8c868f37, 0x398cd7e93017ecc, 0x2e1ec37c30a741e, 0xb00d11006ffa14}); +constexpr StatTableTRI58 QRT_TABLE_TRI58({0x2450096792a5c5c, 0x610014271011c, 0x610014271011e, 0x1f0cb811314ea88, 0x610014271011a, 0x8000000420, 0x1f0cb811314ea80, 0x265407ad8a20bcc, 0x610014271010a, 0x3d18be98392ebd0, 0x8000000400, 0xc29b930e407056, 0x1f0cb811314eac0, 0x1fcef001154dee8, 0x265407ad8a20b4c, 0xc69b924c61f94a, 0x610014271000a, 0x211006895845190, 0x3d18be98392e9d0, 0x54007accac09cc, 0x8000000000, 0xc08b934e107854, 0xc29b930e407856, 0x275407adc220bcc, 0x1f0cb811314fac0, 0x1f6db815164ea8a, 0x1fcef001154fee8, 0x1b2db801945e396, 0x265407ad8a24b4c, 0x21100ec95865590, 0xc69b924c61794a, 0x273507b1e530ad6, 0x610014270000a, 0x1b4cb835b34e29c, 0x211006895865190, 0x3839bf20d47e016, 0x3d18be98396e9d0, 0x3858bd34f36e01c, 0x54007acca409cc, 0, 0x8000100000, 0xc29a130e507856, 0xc08b934e307854, 0x13253921d448296, 0xc29b930e007856, 0x13c60935f6486bc, 0x275407adca20bcc, 0x3571be8c5e6c9da, 0x1f0cb811214fac0, 0x410014261011c, 0x1f6db815364ea8a, 0x13a50921d1486b6, 0x1fcef001554fee8, 0x64001249245a5c, 0x1b2db801145e396, 0x8610014670200a, 0x265407ac8a24b4c, 0x1a5cbfbdeb0f30c}); +typedef FieldTri FieldTri58; +#endif + +#ifdef ENABLE_FIELD_INT_59 +// 59 bit field +typedef RecLinTrans StatTable59; +constexpr StatTable59 SQR_TABLE_59({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x12a, 0x4a8, 0x12a0, 0x4a80, 0x12a00, 0x4a800, 0x12a000, 0x4a8000, 0x12a0000, 0x4a80000, 0x12a00000, 0x4a800000, 0x12a000000, 0x4a8000000, 0x12a0000000, 0x4a80000000, 0x12a00000000, 0x4a800000000, 0x12a000000000, 0x4a8000000000, 0x12a0000000000, 0x4a80000000000, 0x12a00000000000, 0x4a800000000000, 0x12a000000000000, 0x4a8000000000000, 0x2a000000000012a, 0x28000000000043d, 0x200000000001061}); +constexpr StatTable59 SQR2_TABLE_59({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x12a, 0x12a0, 0x12a00, 0x12a000, 0x12a0000, 0x12a00000, 0x12a000000, 0x12a0000000, 0x12a00000000, 0x12a000000000, 0x12a0000000000, 0x12a00000000000, 0x12a000000000000, 0x2a000000000012a, 0x200000000001061, 0x10444, 0x104440, 0x1044400, 0x10444000, 0x104440000, 0x1044400000, 0x10444000000, 0x104440000000, 0x1044400000000, 0x10444000000000, 0x104440000000000, 0x4440000000012a, 0x4440000000012a0, 0x440000000012ea8, 0x40000000012ee28, 0x12ee628, 0x12ee6280, 0x12ee62800, 0x12ee628000, 0x12ee6280000, 0x12ee62800000, 0x12ee628000000, 0x12ee6280000000, 0x12ee62800000000, 0x2ee62800000012a, 0x6e6280000001061, 0x662800000010079, 0x62800000010016c, 0x28000000100103c}); +constexpr StatTable59 SQR4_TABLE_59({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x12a0, 0x12a00000, 0x12a000000000, 0x2a000000000012a, 0x1044400, 0x10444000000, 0x104440000000000, 0x40000000012ee28, 0x12ee628000, 0x12ee6280000000, 0x662800000010079, 0x100101010, 0x1001010100000, 0x101010000012a0, 0x10000012a12b2a, 0x12a12b2b2a00, 0x2a12b2b2a00012a, 0x32b2a0001045461, 0x200010454541421, 0x104545414044000, 0x45414044012ef02, 0x4044012ef4d49aa, 0x12ef4d49e0ce28, 0x74d49e0ce290079, 0x1e0ce290000011d, 0x62900000100016d, 0x100010013a0, 0x100010013a00000, 0x10013a00012a000, 0x3a00012a012a12a, 0x12a012a116e400, 0x12a116e4010444, 0x116e40104450444, 0x40104450457ea6c, 0x4450457ea2692a0, 0x457ea2692ee7020, 0x22692ee706f7059, 0x2ee706f707e73ba, 0x6f707e73901116, 0x7e7390111013b6, 0x390111013b12b16, 0x11013b12b299b2a, 0x3b12b299b398b2a, 0x3299b398b17de61, 0x3398b17de543b4f, 0x317de543b7b1065, 0x6543b7b1053bb27, 0x37b1053bb4d0b6b, 0x53bb4d0b5b95ca, 0x34d0b5b95cfbf5b, 0x35b95cfbf6885b5, 0x5cfbf688587c89a, 0x7688587c8cf3adb, 0x587c8cf3aa00050, 0xcf3aa00001000d}); +constexpr StatTable59 SQR8_TABLE_59({0x1, 0x1001010100000, 0x10013a00012a000, 0x3398b17de543b4f, 0x2a00116b8c2812a, 0x7cbf06ffa4d5cd6, 0x1288f1cf576c2e0, 0x3047cfc394d3391, 0x322d00452b2c451, 0x226dcb1999949d1, 0x2e2e5ab30351bc0, 0x10b2afcfca2edc6, 0x7ff39b98a3372a8, 0x2d7b439441ae332, 0x5603b26a2dae616, 0x3a13899c470338a, 0x16e8a14f0113f3c, 0x754f4aa46d3bb2, 0x38aa45436b16334, 0x634468d6b3f47b5, 0x248ca58bd03241d, 0x255d1fbddf51ae7, 0x7f4b46a330ef6bc, 0x3b3159b37b1a654, 0x7bbff798b50cf3e, 0x568afef7a72512a, 0x701d7955e599ab3, 0x3e7aed5ec2e2c82, 0x5c4d118847ff477, 0x21264d599c12421, 0x4d287fc89bb5a71, 0x6d1f30202fff956, 0x6c54d2de7c68bf8, 0x350c930ed65aed3, 0x5630ddede4ba32c, 0x7c18282af602d36, 0x198a362bf3c8a07, 0x40dde880541e01c, 0x49c0e7e7438c0c7, 0x3ade2abe6845a50, 0x6ffad83e7ac09c4, 0x52185a0d23e667a, 0x2e8c821b63a858a, 0x770e59a57577b23, 0x2fe0ea55e7032a6, 0x23cf0c9a1565a09, 0x1c53d32d80a4427, 0x23164f78db9fa8b, 0x691c4ffab038e2a, 0x33fc91a8a831d85, 0x48039e34eec4e05, 0x2581dbb898c10b5, 0x374067097dfc9f9, 0x241611fdbd3f8e7, 0x1b9f2934941d831, 0x1940c046b9b4a62, 0x5333ac5e7a608f6, 0xe9fa1f11b06830, 0x3d3bbe0ab819c34}); +constexpr StatTable59 SQR16_TABLE_59({0x1, 0x857cdd2d43d447, 0xf829d2f68520b5, 0x19fe843a13f84fd, 0xcac85f3b30aa13, 0x5c7d9cd6997e169, 0x21e7ab9693a08f3, 0xe5cda6478df23f, 0xc3e206ed797b25, 0x755908ad7cca1c1, 0x16236a14b269480, 0x5fedfd73877a5e3, 0x6d66cb2c634cab2, 0x1b60fade310cb41, 0x5dcfd76c147e4ff, 0x2e686c220dcdc6a, 0x1d348a9dfc46113, 0x4e97ec4ce1b1081, 0x20ccc4ae0ada275, 0x5ec224932d09f73, 0x385cecd0572d2a0, 0x520f6a5162503d4, 0x3ff8003ba0976e, 0x5a314f7726ffcb7, 0x505c4f556b43e5a, 0x259ddd3f8c27783, 0x25441858e820409, 0x2714ab44ef6c58b, 0x53437cae5c3011c, 0x122c6454cb53ac0, 0x349b57934525af9, 0x394e01a9ab9a786, 0x665a91eb8e73f0d, 0x4c4e86cc5c98631, 0x7983a92ec037fe2, 0x67919ad3e0a3d69, 0x685c3d6c72af62e, 0x4eafca0e4b49fd7, 0x69534a8afbbeee, 0x720f8307d28c8cb, 0x49828239c03d1b7, 0x4c7e6edd9907a53, 0x1fe81ca4466f8fb, 0x19a865c194c7a23, 0x518bbfec9151454, 0x5b7bfbc756a7e4d, 0x146cc66da8b0754, 0x58e7cba08f0b29b, 0x1b578332a8f1985, 0x72d1c4f9eacac25, 0x6fc4f312025b99a, 0x199f6741974302b, 0x3edcb2e16193874, 0x38b45862414392c, 0x3a6669ab6604f52, 0x227da450a65496e, 0x4e85a5c57a7f719, 0x36b5dbf304b88be, 0x2ba8a1264ef68a0}); +constexpr StatTable59 QRT_TABLE_59({0x38d905ab028567a, 0x789fa6ed3b44d72, 0x789fa6ed3b44d70, 0x74ec857e93d828c, 0x789fa6ed3b44d74, 0x116b3c1203c96, 0x74ec857e93d8284, 0xc25ebc3871e280, 0x789fa6ed3b44d64, 0x47a37c3d910b6, 0x116b3c1203cb6, 0xc7322d7a8f48de, 0x74ec857e93d82c4, 0xb509a0ea52e496, 0xc25ebc3871e200, 0x74fdee4681d3e0c, 0x789fa6ed3b44c64, 0x7ffbbd080b2f09a, 0x47a37c3d912b6, 0xd5c937bae506c8, 0x116b3c12038b6, 0xb173c76987625e, 0xc7322d7a8f40de, 0x7591ff36b3a682c, 0x74ec857e93d92c4, 0x72b253bfbfc90c4, 0xb509a0ea52c496, 0x79f2e7b10e6d452, 0xc25ebc3871a200, 0x78c86e951086aac, 0x74fdee4681dbe0c, 0x78c96eb514c602c, 0x789fa6ed3b54c64, 0xc34818b95658e8, 0x7ffbbd080b0f09a, 0x7399f563b1980f2, 0x47a37c3dd12b6, 0xa29e0e28c58880, 0xd5c937baed06c8, 0x788ac23520ac82c, 0x116b3c13038b6, 0xa2c857e83d92b6, 0xb173c769a7625e, 0x608da990122e48, 0xc7322d7acf40de, 0xa3a89269eebefe, 0x7591ff36bba682c, 0xa25ebc2871a200, 0x74ec857e83d92c4, 0x11f62e419f1cfe, 0x72b253bf9fc90c4, 0x7425ebc2871a272, 0xb509a0ee52c496, 0x4ed8555979c8de, 0x79f2e7b18e6d452, 0x6c3580d5915d4d2, 0xc25ebc2871a200, 0, 0x78c86e971086aac}); +typedef Field Field59; +#endif + +#ifdef ENABLE_FIELD_INT_60 +// 60 bit field +typedef RecLinTrans StatTableTRI60; +constexpr StatTableTRI60 SQR_TABLE_TRI60({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000, 0xc0000000, 0x300000000, 0xc00000000, 0x3000000000, 0xc000000000, 0x30000000000, 0xc0000000000, 0x300000000000, 0xc00000000000, 0x3000000000000, 0xc000000000000, 0x30000000000000, 0xc0000000000000, 0x300000000000000, 0xc00000000000000}); +constexpr StatTableTRI60 SQR2_TABLE_TRI60({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x3, 0x30, 0x300, 0x3000, 0x30000, 0x300000, 0x3000000, 0x30000000, 0x300000000, 0x3000000000, 0x30000000000, 0x300000000000, 0x3000000000000, 0x30000000000000, 0x300000000000000, 0x5, 0x50, 0x500, 0x5000, 0x50000, 0x500000, 0x5000000, 0x50000000, 0x500000000, 0x5000000000, 0x50000000000, 0x500000000000, 0x5000000000000, 0x50000000000000, 0x500000000000000, 0xf, 0xf0, 0xf00, 0xf000, 0xf0000, 0xf00000, 0xf000000, 0xf0000000, 0xf00000000, 0xf000000000, 0xf0000000000, 0xf00000000000, 0xf000000000000, 0xf0000000000000, 0xf00000000000000}); +constexpr StatTableTRI60 SQR4_TABLE_TRI60({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x30, 0x300000, 0x3000000000, 0x30000000000000, 0x500, 0x5000000, 0x50000000000, 0x500000000000000, 0xf000, 0xf0000000, 0xf00000000000, 0x11, 0x110000, 0x1100000000, 0x11000000000000, 0x330, 0x3300000, 0x33000000000, 0x330000000000000, 0x5500, 0x55000000, 0x550000000000, 0x50000000000000f, 0xff000, 0xff0000000, 0xff00000000000, 0x101, 0x1010000, 0x10100000000, 0x101000000000000, 0x3030, 0x30300000, 0x303000000000, 0x30000000000005, 0x50500, 0x505000000, 0x5050000000000, 0x5000000000000f0, 0xf0f000, 0xf0f0000000, 0xf0f00000000000, 0x1111, 0x11110000, 0x111100000000, 0x111000000000003, 0x33330, 0x333300000, 0x3333000000000, 0x330000000000055, 0x555500, 0x5555000000, 0x55550000000000, 0x500000000000fff, 0xffff000, 0xffff0000000, 0xffff00000000000}); +constexpr StatTableTRI60 SQR8_TABLE_TRI60({0x1, 0x110000, 0x10100000000, 0x111000000000003, 0x300030, 0x33003300000, 0x30303000000005, 0x330000000555555, 0x50000000500, 0x50000005500000f, 0x5050000f0f000, 0x5000ffff0000fff, 0xf000f000f011, 0xff00ff1010101, 0xf0e11111111111, 0x31, 0x3210000, 0x313100000000, 0x221000000000056, 0x5300530, 0x563056300000, 0x5353530000000f5, 0x63000000faaaaaa, 0xf5000000f500, 0x500000fa500010e, 0xf5f50011e1f000, 0x5010ffef0010ffe, 0x11f011f011f321, 0x10ef10ec1313131, 0x1e2d22222222222, 0x501, 0x55110000, 0x5040100000000, 0x411000000000ffc, 0xf030f030, 0xff33ff3300000, 0xc0c03000001114, 0x330000100555554, 0x11050000110500, 0x50001015500303f, 0x114050333c0f003, 0x5300fcff0300fcf, 0x330f330f330a511, 0x3fc03af4040404, 0x395b44444444444, 0xf531, 0xfa6210000, 0xf5c43100000000, 0x721000000010fa8, 0x11f521f530, 0x10ea73ea6300000, 0x4d4c530000322d7, 0x63000310faaaa9b, 0x321f5000321f500, 0x500313ea505343e, 0x2d4f55677d1f056, 0x310acef5310ace, 0x621a621a62e562e, 0x43bc4cb34c4c4c4, 0x878788888888888}); +constexpr StatTableTRI60 SQR16_TABLE_TRI60({0x1, 0x563055110000, 0x111010233c0f003, 0x1200afffa8baffc, 0x5356030553000c5, 0x7145cf221744a77, 0x5748045489aaaaf, 0x7d52fcee4febdb3, 0x221f633c000a012, 0x41431fb55d4f4c8, 0x7f126132f4be5d5, 0x323da1f43c3a7e0, 0x373b24844474766, 0x6cc378a25584eb, 0x7ef66648aae4aca, 0x33003000031, 0xc0c03fb7c0f1fb, 0x174757777d10536, 0x2116210a52facb3, 0x5316fc100c1fb35, 0x7aae07597d161e1, 0x6752c4decfb6b7f, 0xf590fa78d56bf3, 0x1be67573275f157, 0xe3e0e9e0d61817, 0x25ac0012251ff6c, 0x407de1e40e3a849, 0x7a7264848fdf67e, 0x3bb8ba7d3879348, 0x498941f57060c6c, 0x5000000f0f501, 0x10fa8cfc1213ac0, 0x51a500f5501aab9, 0x73ef9049dcace64, 0x526a202f322f6e7, 0x2789a852500ca93, 0x4d1346684907509, 0x7d02bcfe4febdb2, 0x330a0329aba0521, 0x50a33c66415f5eb, 0x2e99dced402a73d, 0xf78f2f1a2dbcfe, 0x793a675db461a6a, 0x73848cd4c2f25d2, 0x54fa22d244aa9c6, 0xfae22e13e01501, 0x538ead296f222e5, 0x4da65592d2a750a, 0x40f91ebc14fcd2a, 0x5e73ff2f3c21c03, 0x4c72dce55551460, 0x3ffa59f8e5aef0a, 0x30057fa7b802f82, 0x36efe87d58aa6e4, 0x3bc96a196d71957, 0x5a82cfde2ad602f, 0x1f9bce94df9d3bf, 0x43c91d9b6bcabba, 0x2193c1833502ba3, 0xd28f516c1311d3d}); +constexpr StatTableTRI60 QRT_TABLE_TRI60({0x6983c00fe00104a, 0x804570322e054e6, 0x804570322e054e4, 0x15673387e0a4e4, 0x804570322e054e0, 0x100010110, 0x15673387e0a4ec, 0x920d01f34442a70, 0x804570322e054f0, 0x7a8dc0f2e4058f0, 0x100010130, 0x120c01f140462f0, 0x15673387e0a4ac, 0x7bdbb2ca9a4fe5c, 0x920d01f34442af0, 0xe9c6b039ce0c4ac, 0x804570322e055f0, 0xfac8b080ca20c00, 0x7a8dc0f2e405af0, 0x7a8dc4b2e4a59f0, 0x100010530, 0x10000100000, 0x120c01f14046af0, 0x131a02d91c5db6c, 0x15673387e0b4ac, 0x15623387d0b4ac, 0x7bdbb2ca9a4de5c, 0x7ffbbbca0a8ee5c, 0x920d01f34446af0, 0x800000020000000, 0xe9c6b039ce044ac, 0x81130302500f000, 0x804570322e155f0, 0x935b72eb3a48e9c, 0xfac8b080ca00c00, 0x120c016140563c0, 0x7a8dc0f2e445af0, 0x7bcbb3ca8a4ee5c, 0x7a8dc4b2e4259f0, 0xc4000a0300, 0x100110530, 0x11623285c1b19c, 0x10000300000, 0x420890090c3000, 0x120c01f14446af0, 0x68d7b33b9e0b4ac, 0x131a02d9145db6c, 0xe8ccb1e18a56fc0, 0x15673386e0b4ac, 0x7aadc8f2e485af0, 0x15623385d0b4ac, 0x4a0990093c3000, 0x7bdbb2cada4de5c, 0xf9d6b3389e0b4ac, 0x7ffbbbca8a8ee5c, 0xdf6ba38cec84ac, 0x920d01f24446af0, 0x520d01f24446af0, 0x800000000000000, 0}); +typedef FieldTri FieldTri60; +#endif + +#ifdef ENABLE_FIELD_INT_61 +// 61 bit field +typedef RecLinTrans StatTable61; +constexpr StatTable61 SQR_TABLE_61({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x138000, 0x4e0000, 0x1380000, 0x4e00000, 0x13800000, 0x4e000000, 0x138000000, 0x4e0000000, 0x1380000000, 0x4e00000000, 0x13800000000, 0x4e000000000, 0x138000000000, 0x4e0000000000, 0x1380000000000, 0x4e00000000000, 0x13800000000000, 0x4e000000000000, 0x138000000000000, 0x4e0000000000000, 0x1380000000000000, 0xe0000000000004e, 0x180000000000011f}); +constexpr StatTable61 SQR2_TABLE_61({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x1000000000000000, 0x138, 0x1380, 0x13800, 0x138000, 0x1380000, 0x13800000, 0x138000000, 0x1380000000, 0x13800000000, 0x138000000000, 0x1380000000000, 0x13800000000000, 0x138000000000000, 0x1380000000000000, 0x180000000000011f, 0x1054, 0x10540, 0x105400, 0x1054000, 0x10540000, 0x105400000, 0x1054000000, 0x10540000000, 0x105400000000, 0x1054000000000, 0x10540000000000, 0x105400000000000, 0x1054000000000000, 0x540000000000138, 0x14000000000013ce, 0x13d96, 0x13d960, 0x13d9600, 0x13d96000, 0x13d960000, 0x13d9600000, 0x13d96000000, 0x13d960000000, 0x13d9600000000, 0x13d96000000000, 0x13d960000000000, 0x13d9600000000000, 0x1d9600000000011f, 0x196000000000101a, 0x1600000000010004}); +constexpr StatTable61 SQR4_TABLE_61({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x138, 0x1380000, 0x13800000000, 0x138000000000000, 0x10540, 0x105400000, 0x1054000000000, 0x540000000000138, 0x13d9600, 0x13d96000000, 0x13d960000000000, 0x1600000000010004, 0x100111000, 0x1001110000000, 0x11100000000138, 0x10000000013812b8, 0x13812ab8000, 0x13812ab80000000, 0x12ab800000010540, 0x105514114, 0x1055141140000, 0x551411400000138, 0x1140000013d84f6, 0x13d84f72f60, 0x13d84f72f600000, 0x4f72f6000010004, 0xf6000010000010f, 0x1000001010100, 0x10101000138, 0x101010001380000, 0x100013800013938, 0x138000139393800, 0x1393938010540, 0x193938010540011f, 0x180105400104445f, 0x540010444454138, 0x1044445413d9600, 0x445413d96013cae, 0x13d96013caaab96, 0x16013caaab970004, 0x1caaab970011111f, 0xb9700111101100b, 0x11110110010028, 0x11011001002812b8, 0x1001002812aab938, 0x2812aab92b8138, 0x12aab92b81382ec0, 0x192b81382ed1400b, 0x1382ed140105514, 0xed1401055150567, 0x105515056890f6, 0x1515056890f613ce, 0x56890f613d84e58, 0x10f613d84e5db85c, 0x13d84e5db84f6010, 0xe5db84f6000000e, 0x184f600000010123}); +constexpr StatTable61 SQR8_TABLE_61({0x1, 0x100111000, 0x10101000138, 0x1001002812aab938, 0x1000001390478, 0x113916c2d28792b8, 0x1904457c4545aa5f, 0x13aa7f0f280c5e20, 0x1047900101540, 0x13be84504128808e, 0x839d72c6e39c0f1, 0x16a18bbeafc6bac6, 0x7290382d6ea1584, 0x1d7d80a66b181691, 0x19d2aaa6110c5d47, 0x1b613d85f602c96f, 0x3812870738, 0x113dbce704cbbd40, 0xd92856e5392f94b, 0x84f76c3d7c304a3, 0x1a519225fe5ce8cf, 0x1704aca0c7190b8e, 0xb7fb1620ed7d025, 0x12831368539314f6, 0x748fb7c048744be, 0x78cc8029440fcba, 0x10eb05b6015eb730, 0xfd3c38351ebc6bd, 0x1665bcfabbfbe624, 0x136549cb4738e1ec, 0x6db6139d4b707f2, 0x1000057853aeac78, 0x104401500109340, 0x554c25992c8f3d8, 0x192dd4b6c0886747, 0x219c35ac73165fc, 0xdf27daa47ee296b, 0x73ab415a10863d2, 0x1f06884b4f2dc1dd, 0xb56c8c3efd7847f, 0x7a6a82768a4a3f2, 0x8773791c3b9f69f, 0x1e4d128bbd8fa105, 0x16977fb4d8984d86, 0xb9a5106882f60bf, 0xc5102ee91822469, 0xdab44dc3cdf7a0b, 0x18d48e2841f63e4, 0x165b8e4d03de40d4, 0x11a7aec6ef42385a, 0x17064ddd9b5041ea, 0xf89b61f74d1f401, 0x18583a8c57e6cb7f, 0x607279105fda3be, 0x905e9c0d58240c7, 0x1ed3c0319519fa7d, 0xa3227b6d1cc17a1, 0xf6cb7bb2aa84563, 0xdda77eb9b649e97, 0x15480a00ec829caf, 0x62cb6da6128c272}); +constexpr StatTable61 SQR16_TABLE_61({0x1, 0x1c7cd18a3a216933, 0xd201ddad374eb4, 0xee4694049c47289, 0x40db9f51130a1e6, 0x134cab3c67ec43f4, 0x97823873a2fc00f, 0xc08b772e8161a43, 0x128159f3d3611eac, 0x1f002f36181d6c4, 0x9de899abbd8d18f, 0x1a6ecb093fbb558b, 0xa6a1251b5961643, 0x1b285c169fb6616d, 0x9c04f5fcf0a4ce5, 0xd050c0ab89025ad, 0xdab152bf63418d9, 0xad3e33af7686059, 0x1561180155ac0dc8, 0x1d9e862521ab7d29, 0xa21b06e1e7632b5, 0x29b84e35cfc95ac, 0x17a27c78dac90e2c, 0x1312fa5f7b1e4ea2, 0xfe66bf53de6a93d, 0x182041e17dde85e9, 0x1289eb06f1803a2e, 0x129449a509af818c, 0x1f308057c81ab449, 0x419981420870054, 0x19f853b859910eb1, 0x9b422c0e9d60871, 0x9e6aec92bfcfa99, 0x15a788f1748b8f44, 0x1fa9a9c171dd83a1, 0x14096af6c0840cc6, 0x1bbe256976515067, 0x14f853fd9e5c0002, 0xf6256b0235f7a8, 0x37e727448043cf6, 0xbb0f467dd137c3f, 0x2538d574ceec19e, 0x15ff26c652c82188, 0x1c22b1e2a9ed31f3, 0x1f56b4b705c21301, 0x1502df3e9aa51832, 0x89c3dec02a6a543, 0x15eac5a464a4f736, 0x1d5023636fc14fa7, 0x499c5d458f9699e, 0x355b147c1703428, 0x1864a11df3efee51, 0x9af0f612e9c1265, 0x9c613962a1c08d9, 0x1cee6fc68f73b3f7, 0x185720007e663719, 0x101dd90a4502bf06, 0x1569af254da87eb0, 0x1781376276013a90, 0x10d2bf3d5e191483, 0x6215713bdc7d250}); +constexpr StatTable61 QRT_TABLE_61({0x171d34fcdac955d0, 0x12cfc8c049e1c96, 0x12cfc8c049e1c94, 0x71d34fcdac955c2, 0x12cfc8c049e1c90, 0x631c871de564852, 0x71d34fcdac955ca, 0x129fa6407f27300, 0x12cfc8c049e1c80, 0x7094f6fdd0a3b12, 0x631c871de564872, 0xdb28cee59c8256a, 0x71d34fcdac9558a, 0xc8a0be15a915472, 0x129fa6407f27380, 0x12dfcb4058e0b80, 0x12cfc8c049e1d80, 0x117d7f04ad0118, 0x7094f6fdd0a3912, 0x621b576dbe35b6a, 0x631c871de564c72, 0x13c808a013a1ee0, 0xdb28cee59c82d6a, 0x113d79842a0272, 0x71d34fcdac9458a, 0x719776b580b6a98, 0xc8a0be15a917472, 0x6633498d6db760a, 0x129fa6407f23380, 0xbd4ae9e8c3e7560, 0x12dfcb4058e8b80, 0x8000000a, 0x12cfc8c049f1d80, 0x634ce9add3b26ea, 0x117d7f04af0118, 0xda3f19c5d66258a, 0x7094f6fdd0e3912, 0xb87427e85e71560, 0x621b576dbeb5b6a, 0xc8b0b085b8c4e0a, 0x631c871de464c72, 0x1538fc8649458a, 0x13c808a011a1ee0, 0xcddbca6d1cfe360, 0xdb28cee59882d6a, 0xae80f550d1ffff2, 0x113d7984aa0272, 0xda7770f5f195912, 0x71d34fcdbc9458a, 0x137c8a049a1ee0, 0x719776b5a0b6a98, 0xded39a9d236ba78, 0xc8a0be15e917472, 0x6732488ca7ce0a, 0x6633498dedb760a, 0xc0406d0527cb80a, 0x129fa6417f23380, 0x3d4ae9eac3e756a, 0xbd4ae9eac3e7560, 0, 0x12dfcb4458e8b80}); +typedef Field Field61; +#endif + +#ifdef ENABLE_FIELD_INT_62 +// 62 bit field +typedef RecLinTrans StatTable62; +constexpr StatTable62 SQR_TABLE_62({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x20000001, 0x80000004, 0x200000010, 0x800000040, 0x2000000100, 0x8000000400, 0x20000001000, 0x80000004000, 0x200000010000, 0x800000040000, 0x2000000100000, 0x8000000400000, 0x20000001000000, 0x80000004000000, 0x200000010000000, 0x800000040000000, 0x2000000100000000, 0x440000002, 0x1100000008, 0x4400000020, 0x11000000080, 0x44000000200, 0x110000000800, 0x440000002000, 0x1100000008000, 0x4400000020000, 0x11000000080000, 0x44000000200000, 0x110000000800000, 0x440000002000000, 0x1100000008000000}); +constexpr StatTable62 SQR2_TABLE_62({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x1000000000000000, 0x80000004, 0x800000040, 0x8000000400, 0x80000004000, 0x800000040000, 0x8000000400000, 0x80000004000000, 0x800000040000000, 0x440000002, 0x4400000020, 0x44000000200, 0x440000002000, 0x4400000020000, 0x44000000200000, 0x440000002000000, 0x400000000000001, 0x20000011, 0x200000110, 0x2000001100, 0x20000011000, 0x200000110000, 0x2000001100000, 0x20000011000000, 0x200000110000000, 0x2000001100000000, 0x11100000008, 0x111000000080, 0x1110000000800, 0x11100000008000, 0x111000000080000, 0x1110000000800000, 0x1100000088000004, 0x1000000800000044, 0x8080000444, 0x80800004440, 0x808000044400, 0x8080000444000, 0x80800004440000, 0x808000044400000, 0x80000404000002, 0x800004040000020, 0x40440000202, 0x404400002020, 0x4044000020200, 0x40440000202000, 0x404400002020000}); +constexpr StatTable62 SQR4_TABLE_62({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x80000004, 0x800000040000, 0x440000002, 0x4400000020000, 0x20000011, 0x200000110000, 0x2000001100000000, 0x11100000008000, 0x1000000800000044, 0x8080000444000, 0x800004040000020, 0x40440000202000, 0x400000000000101, 0x20001011000, 0x200010110000000, 0x101110000000800, 0x1100008088000404, 0x80808004044400, 0x80044404000202, 0x444044002020200, 0x440002002001110, 0x20002011101100, 0x20110011000080, 0x1100111000800080, 0x1110080000804400, 0x800080844004440, 0x808400044402000, 0x404400002021, 0x44000000210001, 0x300010110, 0x3000101100000, 0x101118000000c, 0x1118000800c0004, 0x8084c0040446, 0x84c00444460002, 0x4440460020213, 0x404600022130011, 0x2000201120111011, 0x2011301110118000, 0x3011001900008044, 0x1918080044c044, 0x1808004840440064, 0x484c4000646020, 0xc40004040200121, 0x40460001213100, 0x600010111000101, 0x101120001011800, 0x1200018198000404, 0x181910004044800, 0x110004c488000606, 0x4c4808006064400, 0x80046404001312, 0x464044013120200, 0x440112002001190, 0x1120002011901100, 0x20190011004480, 0x1900111044800080, 0x1110480000806400}); +constexpr StatTable62 SQR8_TABLE_62({0x1, 0x400000000000101, 0x44000000210001, 0x40460001213100, 0x404500002021, 0xe40014150200121, 0x80b400145512000, 0x1495c4000646820, 0x8000808c4004445, 0xd0800c8c8440561, 0x1045c80080ad6405, 0x1988b0805419944, 0x190110048480008e, 0x2891049dcc008662, 0x8ac190411026482, 0x241574511233a020, 0x1120002031901110, 0x3040203922110044, 0x1110792020b25580, 0x282a4830647355, 0x2c60001037102032, 0x26e4507065221080, 0x2036c57040579390, 0x3450409552c0cc02, 0x5c4000c66824017, 0x5508ce8e2845301, 0x4934eca8d59343, 0x1c28a918f7c9c0d1, 0xb080581194c8e4, 0x1018495dc440e46a, 0x1ac80985d8604226, 0xc7044545722023, 0x145120003031900, 0x4440003a0200005, 0x134447a19a002514, 0x510e645a31f1135, 0xae4834446175200, 0x264f451435730311, 0x7220c2004155891, 0x2153045891358c65, 0x154154800ca02904, 0x54dc0c88ce92565, 0xdc54bc04d28bc20, 0x8c54b0401283d8b, 0x29088e8109411f28, 0x12d0dc41982620a, 0xc0030c89a712640, 0x1dc8192422907592, 0x145554681022a075, 0x470792013225580, 0x346c6a7130667300, 0x39147d7004b077b2, 0x6c83e2d354461c6, 0xcf6d0046247a030, 0x3221c0f063a45c80, 0x303645fc20539787, 0x21004cf150409b10, 0x1dd380444d78042, 0x1c709df8b7145381, 0x185834a4e8d51327, 0x1420e118b389a4d1, 0xf0c41811b4e8c4}); +constexpr StatTable62 SQR16_TABLE_62({0x1, 0x147095f0731417c5, 0x3189fad107702e11, 0x3d3937fd86a460ab, 0x3ff26c959b47c587, 0x1e2ecbec4bf22bd6, 0x168ebaeceaf71b82, 0x216d6c4471f75c10, 0x1f6d31ccabfaee58, 0x1652ef2066ec0c61, 0x3d62ef6847f808fc, 0x26a33c99ec1b43d4, 0x32f26e79367c91ed, 0x361dcdd0d1e73240, 0xe2d494d081269e2, 0x33d231b9098b6045, 0x3c4e93c22fb78a3, 0x2f655fa56e578df3, 0x3a2b9600532c2609, 0x864e125951bbdb7, 0x2e2fca705bb62c58, 0x28e0629106401eaa, 0x7ac20f0ed6cdc1f, 0x3bd50add28a35850, 0x1a6e5ea19a59ab5d, 0x2add6d1d8c0aaefb, 0x2c3cf9842e6956a3, 0x1906944685f2c7c, 0x925997c95ed1de2, 0xcb9eb5d43c6f2e9, 0x1795f2b48a0fa71d, 0x19de5de41acc2100, 0x2e30c3a8444ef165, 0x29433812a3c4b1cd, 0xcbfa65dcdae6d63, 0x2580f2100e56c068, 0x25ce14544acc08cb, 0x24fa7059a7c87e18, 0x2a01d608b5d57d70, 0x3cefa2f54bdabc51, 0x29225fd40de84dea, 0x2d2276d8df087f20, 0x1a077580d9c5e840, 0x33b71879319b7de1, 0x16017e84617bddf4, 0x2596d6b0bd1a954c, 0x10267caddadbf666, 0x22c43bd90eaa3e05, 0xcaf6704a39c29fc, 0x25a0b38132106551, 0x1a78d1fcfd98f2a2, 0x1924d0b08fe1cc34, 0x3ea0a05c4cb14ee5, 0xa9b505540022072, 0x1e65cd1d5556d710, 0x3682cccd684103f1, 0x20a58fb864d70967, 0x35bfeeacb88f9b9b, 0x3b72dce9c4b09b87, 0x839908c285aaa64, 0x2ed676dc722e9732, 0x3dd67b08dc071450}); +constexpr StatTable62 QRT_TABLE_62({0x30268b6fba455d2c, 0x200000006, 0x200000004, 0x3d67cb6c1fe66c76, 0x200000000, 0x3fc4f1901abfa400, 0x3d67cb6c1fe66c7e, 0x35e79b6c0a66bcbe, 0x200000010, 0x1e9372bc57a9941e, 0x3fc4f1901abfa420, 0x21ec9d424957a5b0, 0x3d67cb6c1fe66c3e, 0x1cb35a6e52f5fb0e, 0x35e79b6c0a66bc3e, 0x215481024c13a730, 0x200000110, 0x1c324a6c52f75b08, 0x1e9372bc57a9961e, 0x3764a9d00f676820, 0x3fc4f1901abfa020, 0x355481020e132730, 0x21ec9d424957adb0, 0x3c43c32c0f34301e, 0x3d67cb6c1fe67c3e, 0x1496122c45259728, 0x1cb35a6e52f5db0e, 0x15e418405b72ec20, 0x35e79b6c0a66fc3e, 0x30268b6e3a445c38, 0x215481024c132730, 0x100010114, 0x200010110, 0, 0x1c324a6c52f55b08, 0x215581044d133776, 0x1e9372bc57ad961e, 0x2155810e4d133766, 0x3764a9d00f6f6820, 0x2157833c4d12323e, 0x3fc4f1901aafa020, 0x1c324a4252f55b58, 0x355481020e332730, 0x28332fc0509d41e, 0x21ec9d424917adb0, 0x215783be4d12332e, 0x3c43c32c0fb4301e, 0x2157822c4d06363e, 0x3d67cb6c1ee67c3e, 0x23f6b9d2484afb78, 0x1496122c47259728, 0x14b8184047648a80, 0x1cb35a6e56f5db0e, 0x3fe4f1901aefa820, 0x15e418405372ec20, 0x3d5fd72c1be276be, 0x35e79b6c1a66fc3e, 0x14b038d24774cf10, 0x30268b6e1a445c38, 0x1d17022e43a7172e, 0x215481020c132730, 0x2157022e4d07372e}); +typedef Field Field62; +typedef FieldTri, &SQR_TABLE_62, &SQR2_TABLE_62, &SQR4_TABLE_62, &SQR8_TABLE_62, &SQR16_TABLE_62, &QRT_TABLE_62, IdTrans, &ID_TRANS, &ID_TRANS> FieldTri62; +#endif + +#ifdef ENABLE_FIELD_INT_63 +// 63 bit field +typedef RecLinTrans StatTableTRI63; +constexpr StatTableTRI63 SQR_TABLE_TRI63({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4000000000000000, 0x6, 0x18, 0x60, 0x180, 0x600, 0x1800, 0x6000, 0x18000, 0x60000, 0x180000, 0x600000, 0x1800000, 0x6000000, 0x18000000, 0x60000000, 0x180000000, 0x600000000, 0x1800000000, 0x6000000000, 0x18000000000, 0x60000000000, 0x180000000000, 0x600000000000, 0x1800000000000, 0x6000000000000, 0x18000000000000, 0x60000000000000, 0x180000000000000, 0x600000000000000, 0x1800000000000000, 0x6000000000000000}); +constexpr StatTableTRI63 SQR2_TABLE_TRI63({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x1000000000000000, 0x6, 0x60, 0x600, 0x6000, 0x60000, 0x600000, 0x6000000, 0x60000000, 0x600000000, 0x6000000000, 0x60000000000, 0x600000000000, 0x6000000000000, 0x60000000000000, 0x600000000000000, 0x6000000000000000, 0x14, 0x140, 0x1400, 0x14000, 0x140000, 0x1400000, 0x14000000, 0x140000000, 0x1400000000, 0x14000000000, 0x140000000000, 0x1400000000000, 0x14000000000000, 0x140000000000000, 0x1400000000000000, 0x4000000000000006, 0x78, 0x780, 0x7800, 0x78000, 0x780000, 0x7800000, 0x78000000, 0x780000000, 0x7800000000, 0x78000000000, 0x780000000000, 0x7800000000000, 0x78000000000000, 0x780000000000000, 0x7800000000000000}); +constexpr StatTableTRI63 SQR4_TABLE_TRI63({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x6, 0x60000, 0x600000000, 0x6000000000000, 0x14, 0x140000, 0x1400000000, 0x14000000000000, 0x78, 0x780000, 0x7800000000, 0x78000000000000, 0x110, 0x1100000, 0x11000000000, 0x110000000000000, 0x660, 0x6600000, 0x66000000000, 0x660000000000000, 0x1540, 0x15400000, 0x154000000000, 0x1540000000000000, 0x7f80, 0x7f800000, 0x7f8000000000, 0x7f80000000000000, 0x10100, 0x101000000, 0x1010000000000, 0x100000000000006, 0x60600, 0x606000000, 0x6060000000000, 0x600000000000014, 0x141400, 0x1414000000, 0x14140000000000, 0x1400000000000078, 0x787800, 0x7878000000, 0x78780000000000, 0x7800000000000110, 0x1111000, 0x11110000000, 0x111100000000000, 0x1000000000000666, 0x6666000, 0x66660000000, 0x666600000000000, 0x6000000000001554, 0x15554000, 0x155540000000, 0x1555400000000000, 0x4000000000007ffe, 0x7fff8000, 0x7fff80000000, 0x7fff800000000000}); +constexpr StatTableTRI63 SQR8_TABLE_TRI63({0x1, 0x110, 0x10100, 0x1111000, 0x100010000, 0x11001100000, 0x1010101000000, 0x111111110000000, 0x100000006, 0x11000000660, 0x1010000060600, 0x111100006666000, 0x1000600060006, 0x110066006600660, 0x106060606060606, 0x1666666666666666, 0x12, 0x1320, 0x121200, 0x13332000, 0x1200120000, 0x132013200000, 0x12121212000000, 0x1333333320000000, 0x120000006c, 0x132000006ac0, 0x121200006c6c00, 0x133320006aaac000, 0x12006c006c006c, 0x13206ac06ac06ac0, 0x126c6c6c6c6c6c6c, 0x4aaaaaaaaaaaaaaa, 0x104, 0x11440, 0x1050400, 0x115544000, 0x10401040000, 0x1144114400000, 0x105050504000000, 0x1555555440000006, 0x10400000618, 0x1144000067980, 0x1050400061e1800, 0x155440067ff98006, 0x104061806180618, 0x1446798679867986, 0x21e1e1e1e1e1e1e, 0x3fffffffffffffec, 0x1248, 0x136c80, 0x125a4800, 0x137fec8000, 0x124812480000, 0x136c936c800000, 0x125a5a5a48000000, 0x7fffffec8000006a, 0x124800006db0, 0x136c80006b6b00, 0x125a48006dddb000, 0x7fec806b006b006a, 0x12486db06db06db0, 0x6ceb6b6b6b6b6b6a, 0x25dddddddddddddc}); +constexpr StatTableTRI63 SQR16_TABLE_TRI63({0x1, 0x10006, 0x100000014, 0x1000600140078, 0x116, 0x1160674, 0x11600001538, 0x116067415387e90, 0x10114, 0x101120678, 0x1011400141510, 0x112066c15687e66, 0x1170338, 0x117054a0a90, 0x1170338152c3f60, 0x54a1fbc41888532, 0x100010110, 0x1000701160660, 0x1010400141546, 0x102060c153e7f92, 0x11601170760, 0x116076301121340, 0x1171258152c6df4, 0x142a78fc131d6a4a, 0x1011500050540, 0x113067b055e1f86, 0x1110440042477e, 0x102261da46f39362, 0x117022e054b0b80, 0x45c09af143a3f72, 0x106721d847ee9ae4, 0x408a833f0a833f0a, 0x100010106, 0x1000701000614, 0x101120014147e, 0x114067814067902, 0x11601171074, 0x116076316066138, 0x117054c152d40e4, 0x33e0a853e0b842a, 0x1011500131278, 0x113066d12126d16, 0x7077c017b681e, 0x76e12736f057056, 0x117022e12493290, 0x45c1ead5f26a912, 0x76518c96bc5efa4, 0xb97397297387286, 0x1700171666, 0x17006516147554, 0x17174a012d3f8a, 0x173872913964814e, 0x160216157534, 0x16026219014b3eb8, 0x16144d1d3902f39c, 0x3964974c65925d30, 0x17163b005d59f8, 0x164974c75837d462, 0x17062a404d28cfa, 0x65854b0a96152d3c, 0x16152c2a5943b390, 0x5854b1be6419dd1e, 0x6045c19c854b1fba}); +constexpr StatTableTRI63 QRT_TABLE_TRI63({0, 0x100010114, 0x100010116, 0x1001701051372, 0x100010112, 0x1000040220, 0x100170105137a, 0x5107703453bba, 0x100010102, 0x101130117155a, 0x1000040200, 0x40000200800, 0x100170105133a, 0x103151a137276d8, 0x5107703453b3a, 0x134e65fc7c222be0, 0x100010002, 0x100030103115a, 0x101130117175a, 0x106052d103f4de2, 0x1000040600, 0x15122707691d3a, 0x40000200000, 0x4530770bc57b3a, 0x100170105033a, 0x103011a131256d8, 0x103151a137256d8, 0x176f29eb55c7a8da, 0x5107703457b3a, 0x130b158b7767d0da, 0x134e65fc7c22abe0, 0x7bcaf59d2f62d3e2, 0x100000002, 0x1001401041260, 0x100030101115a, 0x5107e03443ab8, 0x101130113175a, 0x1043701251b3a, 0x106052d10374de2, 0x134e657d7c232be2, 0x1000140600, 0x106073d103b4be2, 0x15122707491d3a, 0x4438600ac07800, 0x40000600000, 0x176a199c5682d3e0, 0x4530770b457b3a, 0x7bca759c2f62d3e0, 0x100170005033a, 0x6116d02572de2, 0x103011a111256d8, 0x1346656d7c372de2, 0x103151a177256d8, 0x643c600aa07800, 0x176f29eb5dc7a8da, 0x7b4b758b2f67d0da, 0x5107713457b3a, 0x104570776b457b3a, 0x130b158b5767d0da, 0x734e65fc3c22abe0, 0x134e65fc3c22abe0, 0x4000000000000000, 0x7bcaf59daf62d3e2}); +typedef FieldTri FieldTri63; +#endif + +#ifdef ENABLE_FIELD_INT_64 +// 64 bit field +typedef RecLinTrans StatTable64; +constexpr StatTable64 SQR_TABLE_64({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4000000000000000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0x1b00000, 0x6c00000, 0x1b000000, 0x6c000000, 0x1b0000000, 0x6c0000000, 0x1b00000000, 0x6c00000000, 0x1b000000000, 0x6c000000000, 0x1b0000000000, 0x6c0000000000, 0x1b00000000000, 0x6c00000000000, 0x1b000000000000, 0x6c000000000000, 0x1b0000000000000, 0x6c0000000000000, 0x1b00000000000000, 0x6c00000000000000, 0xb00000000000001b, 0xc00000000000005a}); +constexpr StatTable64 SQR2_TABLE_64({0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, 0x1000000, 0x10000000, 0x100000000, 0x1000000000, 0x10000000000, 0x100000000000, 0x1000000000000, 0x10000000000000, 0x100000000000000, 0x1000000000000000, 0x1b, 0x1b0, 0x1b00, 0x1b000, 0x1b0000, 0x1b00000, 0x1b000000, 0x1b0000000, 0x1b00000000, 0x1b000000000, 0x1b0000000000, 0x1b00000000000, 0x1b000000000000, 0x1b0000000000000, 0x1b00000000000000, 0xb00000000000001b, 0x145, 0x1450, 0x14500, 0x145000, 0x1450000, 0x14500000, 0x145000000, 0x1450000000, 0x14500000000, 0x145000000000, 0x1450000000000, 0x14500000000000, 0x145000000000000, 0x1450000000000000, 0x450000000000001b, 0x50000000000001dc, 0x1db7, 0x1db70, 0x1db700, 0x1db7000, 0x1db70000, 0x1db700000, 0x1db7000000, 0x1db70000000, 0x1db700000000, 0x1db7000000000, 0x1db70000000000, 0x1db700000000000, 0x1db7000000000000, 0xdb7000000000001b, 0xb70000000000011f, 0x7000000000001105}); +constexpr StatTable64 SQR4_TABLE_64({0x1, 0x10000, 0x100000000, 0x1000000000000, 0x1b, 0x1b0000, 0x1b00000000, 0x1b000000000000, 0x145, 0x1450000, 0x14500000000, 0x145000000000000, 0x1db7, 0x1db70000, 0x1db700000000, 0x1db7000000000000, 0x11011, 0x110110000, 0x1101100000000, 0x101100000000001b, 0x1ab1ab, 0x1ab1ab0000, 0x1ab1ab00000000, 0xb1ab00000000015e, 0x1514515, 0x15145150000, 0x151451500000000, 0x4515000000001c6b, 0x1c6db6c7, 0x1c6db6c70000, 0x1c6db6c700000000, 0xb6c700000001010f, 0x101000101, 0x1010001010000, 0x10001010000001b, 0x1010000001b1b00, 0x1b1b001b1b, 0x1b1b001b1b0000, 0x1b001b1b00000145, 0x1b1b000001444500, 0x14445014445, 0x144450144450000, 0x4501444500001dac, 0x444500001daab71b, 0x1daab71daab7, 0x1daab71daab70000, 0xb71daab70001110e, 0xaab700011101101f, 0x1110110110111, 0x110110110111001b, 0x10110111001aab1b, 0x111001aab1ab1ab, 0x1aab1ab1ab1aab, 0xab1ab1ab1aab015e, 0xb1ab1aab0150145e, 0x1aab015014514515, 0x150145145145015, 0x1451451450151c70, 0x451450151c71db6b, 0x50151c71db6db6dc, 0x1c71db6db6db71c7, 0xdb6db6db71c6000b, 0xb6db71c60001000f, 0x71c6000100000005}); +constexpr StatTable64 SQR8_TABLE_64({0x1, 0x11011, 0x101000101, 0x1110110110111, 0x100000001001a, 0x10110001100aa1a1, 0x100011a1b1a011a, 0x100baa100bb1aa0a, 0x1a00000144, 0x1ba1ba01505504, 0x1a001b5f4401441a, 0xa0eb1eea544fee41, 0x15e0144001a1ce8, 0xf5ee551fbc9d4f5d, 0x1b4543b0eee81b44, 0xb89a98b89a98b894, 0x10dbc, 0x11d76167c, 0x10cb1bd0cb1bc, 0x1c6b617617606a67, 0xdbc00010da6ad43, 0x167d1d6d105be392, 0xbd170ae2484f0af7, 0x162bc80d36e8d468, 0x1aad58014ae5f0, 0x63df9865e4bbbb5, 0x43fc5a4cbafe0d17, 0xe3d18fd6f8de2666, 0x49e2e5eab134a710, 0x1c78a1664f19bdd8, 0xf0829cea9886f08a, 0x4d8f634d8f625cdd, 0x100514550, 0x1104554401050, 0x15115140114154b, 0x10050551444aec57, 0x4551004b4277f24b, 0xef2afe861bdfb, 0x1d64ceb6c85ed2c9, 0x4975810172576524, 0x73cf4644451101e, 0x4fd1b234005fb6a7, 0x1bddd12e486f9a6f, 0xaa3c6f23ad5e9724, 0xa02b0a9206ef4923, 0x18a08533d5a4e65e, 0x1fc83ef027d0132b, 0x5e54f45f48c9a13c, 0x10deeff7bf8c0, 0x1d21c38d4f8874db, 0x10886029449884cd, 0xfe25b26c0190be86, 0xf5345525adfcb67e, 0xb606f05c0f274ae6, 0x49303a49c3147e89, 0xe3dec1f0cb3467b8, 0xf3dd197b59b91bb7, 0x6e062ec482dfc7e, 0xc24c087e94b8c9c, 0x42e75f2649a63926, 0x4646807e89775aa9, 0xca57e67631079503, 0xf738d302cd26e621, 0xda8702da9702da9d}); +constexpr StatTable64 SQR16_TABLE_64({0x1, 0x15f0144001a114f, 0x1aad43011ba1e5, 0xe34916e80106e21d, 0x11cefef6be466, 0xab943b855d3d776b, 0x1c77b6cf4edf1bd0, 0x46923ddea5ce4e34, 0x5455145e48670f13, 0xfb7d34d8e2b804bb, 0xbbe0dfe164a4d5b4, 0x431d528b1f73a8a2, 0xc259794b79e2607, 0x5945c54c76a8d132, 0xf5cb8b3860386917, 0xb345180ffd7a5551, 0xbaf1bebe1ae4ad02, 0x45562dad588c6260, 0x55b2852b76a728c4, 0xb5908b73d457d739, 0xa5a058173d115951, 0x11e605f10dd49e16, 0xb122096fef2a82a8, 0xfb95933559736ac7, 0x42652cf9ded5daa5, 0xe9a56590d5ab5301, 0xb8cef5ec20abb26f, 0xb50edcd1421d92e0, 0x12ac73f1d2f67094, 0x1c5815d4c184bd2, 0xe227a4ef0cd1165c, 0xe8d4a3a319b07491, 0xb0ef530df44bb042, 0xfbcbf52ff08d7ea3, 0xa0eaea8c7f69bf70, 0xedc22185164a14b1, 0xbfb9f37fc5eb3abc, 0x3712083e323193a, 0xe7bdca1397a3c26c, 0xf2d44dcbd1d02306, 0xa8fcad00bc810b9c, 0x4f7014f9d2186ea, 0x1b4d4ccc40f8060f, 0xe9ecf1e0105dab78, 0xe34e682846de9f1d, 0xace6cd21bf5ef658, 0x10f0cfa8cf3326ff, 0x71a97b1c73b8a63, 0xe1398cba3a3345d1, 0xa439e4c62ecb0615, 0x4bcce9efcca8db40, 0x176e95394759914e, 0xb5c7335e43a80f7f, 0xeb5439d8e177d64d, 0xa6af064a2d733f41, 0x5efc52c7e2f99007, 0x4a6efe65d270460b, 0xfe0ff44f5baa9a6a, 0x104c70edd05ffd6f, 0xf07d029f554aa763, 0x1c3c3cc0aca30a16, 0x7a0a5f6c85237d50, 0x1b862fb6b961ed37, 0xdcd1bd32f8a7d3ba}); +constexpr StatTable64 QRT_TABLE_64({0x19c9369f278adc02, 0x84b2b22ab2383ee4, 0x84b2b22ab2383ee6, 0x9d7b84b495b3e3f6, 0x84b2b22ab2383ee2, 0x37c470b49213f790, 0x9d7b84b495b3e3fe, 0x1000a0105137c, 0x84b2b22ab2383ef2, 0x368e964a8edce1fc, 0x37c470b49213f7b0, 0x19c9368e278fdf4c, 0x9d7b84b495b3e3be, 0x2e4da23cbc7d4570, 0x1000a010513fc, 0x84f35772bac24232, 0x84b2b22ab2383ff2, 0x37c570ba9314e4fc, 0x368e964a8edce3fc, 0xb377c390213cdb0e, 0x37c470b49213f3b0, 0x85ed5a3aa99c24f2, 0x19c9368e278fd74c, 0xaabff0000780000e, 0x9d7b84b495b3f3be, 0x84b6b3dab03038f2, 0x2e4da23cbc7d6570, 0x511ea03494ffc, 0x1000a010553fc, 0xae0c0220343c6c0e, 0x84f35772bac2c232, 0x800000008000000e, 0x84b2b22ab2393ff2, 0xb376c29c202bc97e, 0x37c570ba9316e4fc, 0x9c3062488879e6ce, 0x368e964a8ed8e3fc, 0x41e42c08e47e70, 0xb377c3902134db0e, 0x85b9b108a60f56ce, 0x37c470b49203f3b0, 0x19dd3b6e21f3cb4c, 0x85ed5a3aa9bc24f2, 0x198ddf682c428ac0, 0x19c9368e27cfd74c, 0x4b7c68431ca84b0, 0xaabff0000700000e, 0x8040655489ffefbe, 0x9d7b84b494b3f3be, 0x18c1354e32bfa74c, 0x84b6b3dab23038f2, 0xaaf613cc0f74627e, 0x2e4da23cb87d6570, 0x3248b3d6b3342a8c, 0x511ea0b494ffc, 0xb60813c00e70700e, 0x1000a110553fc, 0x1e0d022a05393ffc, 0xae0c0220143c6c0e, 0xe0c0220143c6c00, 0x84f35772fac2c232, 0xc041e55948fbfdce, 0x800000000000000e, 0}); +typedef Field Field64; +#endif +} + +Sketch* ConstructClMul8Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_57 + case 57: return new SketchImpl(implementation, 57); +#endif +#ifdef ENABLE_FIELD_INT_58 + case 58: return new SketchImpl(implementation, 58); +#endif +#ifdef ENABLE_FIELD_INT_59 + case 59: return new SketchImpl(implementation, 59); +#endif +#ifdef ENABLE_FIELD_INT_61 + case 61: return new SketchImpl(implementation, 61); +#endif +#ifdef ENABLE_FIELD_INT_62 + case 62: return new SketchImpl(implementation, 62); +#endif +#ifdef ENABLE_FIELD_INT_64 + case 64: return new SketchImpl(implementation, 64); +#endif + } + return nullptr; +} + +Sketch* ConstructClMulTri8Bytes(int bits, int implementation) { + switch (bits) { +#ifdef ENABLE_FIELD_INT_57 + case 57: return new SketchImpl(implementation, 57); +#endif +#ifdef ENABLE_FIELD_INT_58 + case 58: return new SketchImpl(implementation, 58); +#endif +#ifdef ENABLE_FIELD_INT_60 + case 60: return new SketchImpl(implementation, 60); +#endif +#ifdef ENABLE_FIELD_INT_62 + case 62: return new SketchImpl(implementation, 62); +#endif +#ifdef ENABLE_FIELD_INT_63 + case 63: return new SketchImpl(implementation, 63); +#endif + } + return nullptr; +} diff --git a/src/fields/clmul_common_impl.h b/src/fields/clmul_common_impl.h new file mode 100644 index 0000000000..3d179a1081 --- /dev/null +++ b/src/fields/clmul_common_impl.h @@ -0,0 +1,170 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_FIELDS_CLMUL_COMMON_IMPL_H_ +#define _MINISKETCH_FIELDS_CLMUL_COMMON_IMPL_H_ 1 + +#include +#include + +#include "../int_utils.h" +#include "../lintrans.h" + +namespace { + +// The memory sanitizer in clang < 11 cannot reason through _mm_clmulepi64_si128 calls. +// Disable memory sanitization in the functions using them for those compilers. +#if defined(__clang__) && (__clang_major__ < 11) +# if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# define NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# endif +# endif +#endif +#ifndef NO_SANITIZE_MEMORY +# define NO_SANITIZE_MEMORY +#endif + +template NO_SANITIZE_MEMORY I MulWithClMulReduce(I a, I b) +{ + static constexpr I MASK = Mask(); + + const __m128i MOD128 = _mm_cvtsi64_si128(MOD); + __m128i product = _mm_clmulepi64_si128(_mm_cvtsi64_si128((uint64_t)a), _mm_cvtsi64_si128((uint64_t)b), 0x00); + if (BITS <= 32) { + __m128i high1 = _mm_srli_epi64(product, BITS); + __m128i red1 = _mm_clmulepi64_si128(high1, MOD128, 0x00); + __m128i high2 = _mm_srli_epi64(red1, BITS); + __m128i red2 = _mm_clmulepi64_si128(high2, MOD128, 0x00); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } else if (BITS == 64) { + __m128i red1 = _mm_clmulepi64_si128(product, MOD128, 0x01); + __m128i red2 = _mm_clmulepi64_si128(red1, MOD128, 0x01); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)); + } else if ((BITS % 8) == 0) { + __m128i high1 = _mm_srli_si128(product, BITS / 8); + __m128i red1 = _mm_clmulepi64_si128(high1, MOD128, 0x00); + __m128i high2 = _mm_srli_si128(red1, BITS / 8); + __m128i red2 = _mm_clmulepi64_si128(high2, MOD128, 0x00); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } else { + __m128i high1 = _mm_or_si128(_mm_srli_epi64(product, BITS), _mm_srli_si128(_mm_slli_epi64(product, 64 - BITS), 8)); + __m128i red1 = _mm_clmulepi64_si128(high1, MOD128, 0x00); + if ((uint64_t(MOD) >> (66 - BITS)) == 0) { + __m128i high2 = _mm_srli_epi64(red1, BITS); + __m128i red2 = _mm_clmulepi64_si128(high2, MOD128, 0x00); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } else { + __m128i high2 = _mm_or_si128(_mm_srli_epi64(red1, BITS), _mm_srli_si128(_mm_slli_epi64(red1, 64 - BITS), 8)); + __m128i red2 = _mm_clmulepi64_si128(high2, MOD128, 0x00); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } + } +} + +template NO_SANITIZE_MEMORY I MulTrinomial(I a, I b) +{ + static constexpr I MASK = Mask(); + + __m128i product = _mm_clmulepi64_si128(_mm_cvtsi64_si128((uint64_t)a), _mm_cvtsi64_si128((uint64_t)b), 0x00); + if (BITS <= 32) { + __m128i high1 = _mm_srli_epi64(product, BITS); + __m128i red1 = _mm_xor_si128(high1, _mm_slli_epi64(high1, POS)); + if (POS == 1) { + return _mm_cvtsi128_si64(_mm_xor_si128(product, red1)) & MASK; + } else { + __m128i high2 = _mm_srli_epi64(red1, BITS); + __m128i red2 = _mm_xor_si128(high2, _mm_slli_epi64(high2, POS)); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } + } else { + __m128i high1 = _mm_or_si128(_mm_srli_epi64(product, BITS), _mm_srli_si128(_mm_slli_epi64(product, 64 - BITS), 8)); + if (BITS + POS <= 66) { + __m128i red1 = _mm_xor_si128(high1, _mm_slli_epi64(high1, POS)); + if (POS == 1) { + return _mm_cvtsi128_si64(_mm_xor_si128(product, red1)) & MASK; + } else if (BITS + POS <= 66) { + __m128i high2 = _mm_srli_epi64(red1, BITS); + __m128i red2 = _mm_xor_si128(high2, _mm_slli_epi64(high2, POS)); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } + } else { + const __m128i MOD128 = _mm_cvtsi64_si128(1 + (((uint64_t)1) << POS)); + __m128i red1 = _mm_clmulepi64_si128(high1, MOD128, 0x00); + __m128i high2 = _mm_or_si128(_mm_srli_epi64(red1, BITS), _mm_srli_si128(_mm_slli_epi64(red1, 64 - BITS), 8)); + __m128i red2 = _mm_xor_si128(high2, _mm_slli_epi64(high2, POS)); + return _mm_cvtsi128_si64(_mm_xor_si128(_mm_xor_si128(product, red1), red2)) & MASK; + } + } +} + +/** Implementation of fields that use the SSE clmul intrinsic for multiplication. */ +template struct GenField +{ + typedef BitsInt O; + typedef LFSR L; + + static inline constexpr I Sqr1(I a) { return SQR->template Map(a); } + static inline constexpr I Sqr2(I a) { return SQR2->template Map(a); } + static inline constexpr I Sqr4(I a) { return SQR4->template Map(a); } + static inline constexpr I Sqr8(I a) { return SQR8->template Map(a); } + static inline constexpr I Sqr16(I a) { return SQR16->template Map(a); } + +public: + typedef I Elem; + + inline constexpr int Bits() const { return B; } + + inline constexpr Elem Mul2(Elem val) const { return L::Call(val); } + + inline Elem Mul(Elem a, Elem b) const { return MUL(a, b); } + + class Multiplier + { + Elem m_val; + public: + inline constexpr explicit Multiplier(const GenField&, Elem a) : m_val(a) {} + constexpr Elem operator()(Elem a) const { return MUL(m_val, a); } + }; + + /** Compute the square of a. */ + inline constexpr Elem Sqr(Elem val) const { return SQR->template Map(val); } + + /** Compute x such that x^2 + x = a (undefined result if no solution exists). */ + inline constexpr Elem Qrt(Elem val) const { return QRT->template Map(val); } + + /** Compute the inverse of x1. */ + inline Elem Inv(Elem val) const { return InvLadder(val); } + + /** Generate a random field element. */ + Elem FromSeed(uint64_t seed) const { + uint64_t k0 = 0x434c4d554c466c64ull; // "CLMULFld" + uint64_t k1 = seed; + uint64_t count = ((uint64_t)B) << 32; + I ret; + do { + ret = O::Mask(I(SipHash(k0, k1, count++))); + } while(ret == 0); + return LOAD->template Map(ret); + } + + Elem Deserialize(BitReader& in) const { return LOAD->template Map(in.Read()); } + + void Serialize(BitWriter& out, Elem val) const { out.Write(SAVE->template Map(val)); } + + constexpr Elem FromUint64(uint64_t x) const { return LOAD->template Map(O::Mask(I(x))); } + constexpr uint64_t ToUint64(Elem val) const { return uint64_t(SAVE->template Map(val)); } +}; + +template +using Field = GenField, F, SQR, SQR2, SQR4, SQR8, SQR16, QRT, T, LOAD, SAVE>; + +template +using FieldTri = GenField, F, SQR, SQR2, SQR4, SQR8, SQR16, QRT, T, LOAD, SAVE>; + +} + +#endif diff --git a/src/fields/generic_1byte.cpp b/src/fields/generic_1byte.cpp new file mode 100644 index 0000000000..5ce42dc5f7 --- /dev/null +++ b/src/fields/generic_1byte.cpp @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_1) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_2 +// 2 bit field +typedef RecLinTrans StatTable2; +typedef RecLinTrans DynTable2; +constexpr StatTable2 SQR_TABLE_2({0x1, 0x3}); +constexpr StatTable2 QRT_TABLE_2({0x2, 0}); +typedef Field Field2; +#endif + +#ifdef ENABLE_FIELD_INT_3 +// 3 bit field +typedef RecLinTrans StatTable3; +typedef RecLinTrans DynTable3; +constexpr StatTable3 SQR_TABLE_3({0x1, 0x4, 0x6}); +constexpr StatTable3 QRT_TABLE_3({0, 0x4, 0x6}); +typedef Field Field3; +#endif + +#ifdef ENABLE_FIELD_INT_4 +// 4 bit field +typedef RecLinTrans StatTable4; +typedef RecLinTrans DynTable4; +constexpr StatTable4 SQR_TABLE_4({0x1, 0x4, 0x3, 0xc}); +constexpr StatTable4 QRT_TABLE_4({0x6, 0xa, 0x8, 0}); +typedef Field Field4; +#endif + +#ifdef ENABLE_FIELD_INT_5 +// 5 bit field +typedef RecLinTrans StatTable5; +typedef RecLinTrans DynTable5; +constexpr StatTable5 SQR_TABLE_5({0x1, 0x4, 0x10, 0xa, 0xd}); +constexpr StatTable5 QRT_TABLE_5({0x14, 0x8, 0xa, 0, 0xe}); +typedef Field Field5; +#endif + +#ifdef ENABLE_FIELD_INT_6 +// 6 bit field +typedef RecLinTrans StatTable6; +typedef RecLinTrans DynTable6; +constexpr StatTable6 SQR_TABLE_6({0x1, 0x4, 0x10, 0x3, 0xc, 0x30}); +constexpr StatTable6 QRT_TABLE_6({0x3a, 0x26, 0x24, 0x14, 0x20, 0}); +typedef Field Field6; +#endif + +#ifdef ENABLE_FIELD_INT_7 +// 7 bit field +typedef RecLinTrans StatTable7; +typedef RecLinTrans DynTable7; +constexpr StatTable7 SQR_TABLE_7({0x1, 0x4, 0x10, 0x40, 0x6, 0x18, 0x60}); +constexpr StatTable7 QRT_TABLE_7({0, 0x14, 0x16, 0x72, 0x12, 0x40, 0x7a}); +typedef Field Field7; +#endif + +#ifdef ENABLE_FIELD_INT_8 +// 8 bit field +typedef RecLinTrans StatTable8; +typedef RecLinTrans DynTable8; +constexpr StatTable8 SQR_TABLE_8({0x1, 0x4, 0x10, 0x40, 0x1b, 0x6c, 0xab, 0x9a}); +constexpr StatTable8 QRT_TABLE_8({0xbc, 0x2a, 0x28, 0x86, 0x2c, 0xde, 0x8e, 0}); +typedef Field Field8; +#endif +} + +Sketch* ConstructGeneric1Byte(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_2 + case 2: return new SketchImpl(implementation, 2); +#endif +#ifdef ENABLE_FIELD_INT_3 + case 3: return new SketchImpl(implementation, 3); +#endif +#ifdef ENABLE_FIELD_INT_4 + case 4: return new SketchImpl(implementation, 4); +#endif +#ifdef ENABLE_FIELD_INT_5 + case 5: return new SketchImpl(implementation, 5); +#endif +#ifdef ENABLE_FIELD_INT_6 + case 6: return new SketchImpl(implementation, 6); +#endif +#ifdef ENABLE_FIELD_INT_7 + case 7: return new SketchImpl(implementation, 7); +#endif +#ifdef ENABLE_FIELD_INT_8 + case 8: return new SketchImpl(implementation, 8); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_2bytes.cpp b/src/fields/generic_2bytes.cpp new file mode 100644 index 0000000000..12bf3110a6 --- /dev/null +++ b/src/fields/generic_2bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_2) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_9 +// 9 bit field +typedef RecLinTrans StatTable9; +typedef RecLinTrans DynTable9; +constexpr StatTable9 SQR_TABLE_9({0x1, 0x4, 0x10, 0x40, 0x100, 0x6, 0x18, 0x60, 0x180}); +constexpr StatTable9 QRT_TABLE_9({0, 0x4e, 0x4c, 0x1aa, 0x48, 0x22, 0x1a2, 0x100, 0x58}); +typedef Field Field9; +#endif + +#ifdef ENABLE_FIELD_INT_10 +// 10 bit field +typedef RecLinTrans StatTable10; +typedef RecLinTrans DynTable10; +constexpr StatTable10 SQR_TABLE_10({0x1, 0x4, 0x10, 0x40, 0x100, 0x9, 0x24, 0x90, 0x240, 0x112}); +constexpr StatTable10 QRT_TABLE_10({0xec, 0x86, 0x84, 0x30e, 0x80, 0x3c2, 0x306, 0, 0x90, 0x296}); +typedef Field Field10; +#endif + +#ifdef ENABLE_FIELD_INT_11 +// 11 bit field +typedef RecLinTrans StatTable11; +typedef RecLinTrans DynTable11; +constexpr StatTable11 SQR_TABLE_11({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0xa, 0x28, 0xa0, 0x280, 0x205}); +constexpr StatTable11 QRT_TABLE_11({0x734, 0x48, 0x4a, 0x1de, 0x4e, 0x35e, 0x1d6, 0x200, 0x5e, 0, 0x37e}); +typedef Field Field11; +#endif + +#ifdef ENABLE_FIELD_INT_12 +// 12 bit field +typedef RecLinTrans StatTable12; +typedef RecLinTrans DynTable12; +constexpr StatTable12 SQR_TABLE_12({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x9, 0x24, 0x90, 0x240, 0x900, 0x412}); +constexpr StatTable12 QRT_TABLE_12({0x48, 0xc10, 0xc12, 0x208, 0xc16, 0xd82, 0x200, 0x110, 0xc06, 0, 0xda2, 0x5a4}); +typedef Field Field12; +#endif + +#ifdef ENABLE_FIELD_INT_13 +// 13 bit field +typedef RecLinTrans StatTable13; +typedef RecLinTrans DynTable13; +constexpr StatTable13 SQR_TABLE_13({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x36, 0xd8, 0x360, 0xd80, 0x161b, 0x185a}); +constexpr StatTable13 QRT_TABLE_13({0xcfc, 0x1500, 0x1502, 0x382, 0x1506, 0x149c, 0x38a, 0x118, 0x1516, 0, 0x14bc, 0x100e, 0x3ca}); +typedef Field Field13; +#endif + +#ifdef ENABLE_FIELD_INT_14 +// 14 bit field +typedef RecLinTrans StatTable14; +typedef RecLinTrans DynTable14; +constexpr StatTable14 SQR_TABLE_14({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x21, 0x84, 0x210, 0x840, 0x2100, 0x442, 0x1108}); +constexpr StatTable14 QRT_TABLE_14({0x13f2, 0x206, 0x204, 0x3e06, 0x200, 0x1266, 0x3e0e, 0x114, 0x210, 0, 0x1246, 0x2848, 0x3e4e, 0x2258}); +typedef Field Field14; +#endif + +#ifdef ENABLE_FIELD_INT_15 +// 15 bit field +typedef RecLinTrans StatTable15; +typedef RecLinTrans DynTable15; +constexpr StatTable15 SQR_TABLE_15({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x6, 0x18, 0x60, 0x180, 0x600, 0x1800, 0x6000}); +constexpr StatTable15 QRT_TABLE_15({0, 0x114, 0x116, 0x428, 0x112, 0x137a, 0x420, 0x6d62, 0x102, 0x73a, 0x135a, 0x6460, 0x460, 0x4000, 0x6de2}); +typedef Field Field15; +#endif + +#ifdef ENABLE_FIELD_INT_16 +// 16 bit field +typedef RecLinTrans StatTable16; +typedef RecLinTrans DynTable16; +constexpr StatTable16 SQR_TABLE_16({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x2b, 0xac, 0x2b0, 0xac0, 0x2b00, 0xac00, 0xb056, 0xc10e}); +constexpr StatTable16 QRT_TABLE_16({0x732, 0x72b8, 0x72ba, 0x7e96, 0x72be, 0x78b2, 0x7e9e, 0x8cba, 0x72ae, 0xfa24, 0x7892, 0x5892, 0x7ede, 0xbec6, 0x8c3a, 0}); +typedef Field Field16; +#endif +} + +Sketch* ConstructGeneric2Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_9 + case 9: return new SketchImpl(implementation, 9); +#endif +#ifdef ENABLE_FIELD_INT_10 + case 10: return new SketchImpl(implementation, 10); +#endif +#ifdef ENABLE_FIELD_INT_11 + case 11: return new SketchImpl(implementation, 11); +#endif +#ifdef ENABLE_FIELD_INT_12 + case 12: return new SketchImpl(implementation, 12); +#endif +#ifdef ENABLE_FIELD_INT_13 + case 13: return new SketchImpl(implementation, 13); +#endif +#ifdef ENABLE_FIELD_INT_14 + case 14: return new SketchImpl(implementation, 14); +#endif +#ifdef ENABLE_FIELD_INT_15 + case 15: return new SketchImpl(implementation, 15); +#endif +#ifdef ENABLE_FIELD_INT_16 + case 16: return new SketchImpl(implementation, 16); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_3bytes.cpp b/src/fields/generic_3bytes.cpp new file mode 100644 index 0000000000..13e85bd1a1 --- /dev/null +++ b/src/fields/generic_3bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_3) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_17 +// 17 bit field +typedef RecLinTrans StatTable17; +typedef RecLinTrans DynTable17; +constexpr StatTable17 SQR_TABLE_17({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x8012}); +constexpr StatTable17 QRT_TABLE_17({0, 0x4c3e, 0x4c3c, 0x1a248, 0x4c38, 0x428, 0x1a240, 0x1b608, 0x4c28, 0x206, 0x408, 0x4000, 0x1a200, 0x18006, 0x1b688, 0x14d2e, 0x4d28}); +typedef Field Field17; +#endif + +#ifdef ENABLE_FIELD_INT_18 +// 18 bit field +typedef RecLinTrans StatTable18; +typedef RecLinTrans DynTable18; +constexpr StatTable18 SQR_TABLE_18({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x10012}); +constexpr StatTable18 QRT_TABLE_18({0x9208, 0x422, 0x420, 0x8048, 0x424, 0x68b0, 0x8040, 0x30086, 0x434, 0x1040, 0x6890, 0x30ca2, 0x8000, 0x32896, 0x30006, 0, 0x534, 0x20532}); +typedef Field Field18; +#endif + +#ifdef ENABLE_FIELD_INT_19 +// 19 bit field +typedef RecLinTrans StatTable19; +typedef RecLinTrans DynTable19; +constexpr StatTable19 SQR_TABLE_19({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x3804e, 0x6011f}); +constexpr StatTable19 QRT_TABLE_19({0x5d6b0, 0x2f476, 0x2f474, 0x1d6a2, 0x2f470, 0x42a, 0x1d6aa, 0x1060, 0x2f460, 0x19e92, 0x40a, 0x1da98, 0x1d6ea, 0x28c78, 0x10e0, 0xf56a, 0x2f560, 0, 0x19c92}); +typedef Field Field19; +#endif + +#ifdef ENABLE_FIELD_INT_20 +// 20 bit field +typedef RecLinTrans StatTable20; +typedef RecLinTrans DynTable20; +constexpr StatTable20 SQR_TABLE_20({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x90000, 0x40012}); +constexpr StatTable20 QRT_TABLE_20({0xc5dea, 0xc0110, 0xc0112, 0xe11de, 0xc0116, 0x24814, 0xe11d6, 0x20080, 0xc0106, 0xfe872, 0x24834, 0xe4106, 0xe1196, 0x1d9a4, 0x20000, 0x31190, 0xc0006, 0, 0xfea72, 0x7ea74}); +typedef Field Field20; +#endif + +#ifdef ENABLE_FIELD_INT_21 +// 21 bit field +typedef RecLinTrans StatTable21; +typedef RecLinTrans DynTable21; +constexpr StatTable21 SQR_TABLE_21({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x80005}); +constexpr StatTable21 QRT_TABLE_21({0x1bd5fc, 0xbc196, 0xbc194, 0x74b96, 0xbc190, 0x1048, 0x74b9e, 0x672c8, 0xbc180, 0x4080, 0x1068, 0xc8200, 0x74bde, 0x64280, 0x67248, 0xc4280, 0xbc080, 0x80000, 0x4280, 0, 0x1468}); +typedef Field Field21; +#endif + +#ifdef ENABLE_FIELD_INT_22 +// 22 bit field +typedef RecLinTrans StatTable22; +typedef RecLinTrans DynTable22; +constexpr StatTable22 SQR_TABLE_22({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000}); +constexpr StatTable22 QRT_TABLE_22({0x210d16, 0x104a, 0x1048, 0x4088, 0x104c, 0x200420, 0x4080, 0x492dc, 0x105c, 0x1a67f0, 0x200400, 0x21155c, 0x40c0, 0x20346c, 0x4925c, 0x1af7ac, 0x115c, 0x2274ac, 0x1a65f0, 0x2a65f0, 0x200000, 0}); +typedef Field Field22; +#endif + +#ifdef ENABLE_FIELD_INT_23 +// 23 bit field +typedef RecLinTrans StatTable23; +typedef RecLinTrans DynTable23; +constexpr StatTable23 SQR_TABLE_23({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x42, 0x108, 0x420, 0x1080, 0x4200, 0x10800, 0x42000, 0x108000, 0x420000, 0x80042, 0x200108}); +constexpr StatTable23 QRT_TABLE_23({0, 0x1040, 0x1042, 0x43056, 0x1046, 0x121d76, 0x4305e, 0x40a0, 0x1056, 0x15176, 0x121d56, 0x7ee1f6, 0x4301e, 0x40000, 0x4020, 0x4f0be, 0x1156, 0x7cf0a0, 0x15376, 0x1ee9e8, 0x121956, 0x3ac9f6, 0x7ee9f6}); +typedef Field Field23; +#endif + +#ifdef ENABLE_FIELD_INT_24 +// 24 bit field +typedef RecLinTrans StatTable24; +typedef RecLinTrans DynTable24; +constexpr StatTable24 SQR_TABLE_24({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0xb0001b, 0xc0005a}); +constexpr StatTable24 QRT_TABLE_24({0x104e, 0xaf42a8, 0xaf42aa, 0xb78186, 0xaf42ae, 0x4090, 0xb7818e, 0x4a37c, 0xaf42be, 0x3688c0, 0x40b0, 0x80080e, 0xb781ce, 0xaf2232, 0x4a3fc, 0x856a82, 0xaf43be, 0x29c970, 0x368ac0, 0x968ace, 0x44b0, 0x77d570, 0x80000e, 0}); +typedef Field Field24; +#endif +} + +Sketch* ConstructGeneric3Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_17 + case 17: return new SketchImpl(implementation, 17); +#endif +#ifdef ENABLE_FIELD_INT_18 + case 18: return new SketchImpl(implementation, 18); +#endif +#ifdef ENABLE_FIELD_INT_19 + case 19: return new SketchImpl(implementation, 19); +#endif +#ifdef ENABLE_FIELD_INT_20 + case 20: return new SketchImpl(implementation, 20); +#endif +#ifdef ENABLE_FIELD_INT_21 + case 21: return new SketchImpl(implementation, 21); +#endif +#ifdef ENABLE_FIELD_INT_22 + case 22: return new SketchImpl(implementation, 22); +#endif +#ifdef ENABLE_FIELD_INT_23 + case 23: return new SketchImpl(implementation, 23); +#endif +#ifdef ENABLE_FIELD_INT_24 + case 24: return new SketchImpl(implementation, 24); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_4bytes.cpp b/src/fields/generic_4bytes.cpp new file mode 100644 index 0000000000..2a26b90521 --- /dev/null +++ b/src/fields/generic_4bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_4) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_25 +// 25 bit field +typedef RecLinTrans StatTable25; +typedef RecLinTrans DynTable25; +constexpr StatTable25 SQR_TABLE_25({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x800012}); +constexpr StatTable25 QRT_TABLE_25({0, 0x482110, 0x482112, 0x1b3c3e6, 0x482116, 0x4960ae, 0x1b3c3ee, 0x4088, 0x482106, 0x58a726, 0x49608e, 0x5ce52e, 0x1b3c3ae, 0x2006, 0x4008, 0x1c1a8, 0x482006, 0x1e96488, 0x58a526, 0x400000, 0x49648e, 0x1800006, 0x5ced2e, 0xb3d3a8, 0x1b3d3ae}); +typedef Field Field25; +#endif + +#ifdef ENABLE_FIELD_INT_26 +// 26 bit field +typedef RecLinTrans StatTable26; +typedef RecLinTrans DynTable26; +constexpr StatTable26 SQR_TABLE_26({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0x1b00000, 0x2c0001b, 0x300005a}); +constexpr StatTable26 QRT_TABLE_26({0x217b530, 0x2ae82a8, 0x2ae82aa, 0x2001046, 0x2ae82ae, 0x2de032e, 0x200104e, 0x70c10c, 0x2ae82be, 0x20151f2, 0x2de030e, 0xbc1400, 0x200100e, 0x178570, 0x70c18c, 0x2ae4232, 0x2ae83be, 0x211d742, 0x20153f2, 0x21f54f2, 0x2de070e, 0x5e0700, 0xbc1c00, 0x3abb97e, 0x200000e, 0}); +typedef Field Field26; +#endif + +#ifdef ENABLE_FIELD_INT_27 +// 27 bit field +typedef RecLinTrans StatTable27; +typedef RecLinTrans DynTable27; +constexpr StatTable27 SQR_TABLE_27({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x138000, 0x4e0000, 0x1380000, 0x4e00000, 0x380004e, 0x600011f}); +constexpr StatTable27 QRT_TABLE_27({0x6bf0530, 0x2be4496, 0x2be4494, 0x2bf0522, 0x2be4490, 0x1896cca, 0x2bf052a, 0x408a, 0x2be4480, 0x368ae72, 0x1896cea, 0x18d2ee0, 0x2bf056a, 0x1c76d6a, 0x400a, 0x336e9f8, 0x2be4580, 0x36baf12, 0x368ac72, 0x430360, 0x18968ea, 0x34a6b80, 0x18d26e0, 0xbf1560, 0x2bf156a, 0, 0x1c74d6a}); +typedef Field Field27; +#endif + +#ifdef ENABLE_FIELD_INT_28 +// 28 bit field +typedef RecLinTrans StatTable28; +typedef RecLinTrans DynTable28; +constexpr StatTable28 SQR_TABLE_28({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000}); +constexpr StatTable28 QRT_TABLE_28({0x121d57a, 0x40216, 0x40214, 0x8112578, 0x40210, 0x10110, 0x8112570, 0x12597ec, 0x40200, 0x6983e00, 0x10130, 0x972b99c, 0x8112530, 0x8002000, 0x125976c, 0x815a76c, 0x40300, 0x936b29c, 0x6983c00, 0x97bb8ac, 0x10530, 0x9103000, 0x972b19c, 0xf6384ac, 0x8113530, 0x4113530, 0x8000000, 0}); +typedef Field Field28; +#endif + +#ifdef ENABLE_FIELD_INT_29 +// 29 bit field +typedef RecLinTrans StatTable29; +typedef RecLinTrans DynTable29; +constexpr StatTable29 SQR_TABLE_29({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x280000, 0xa00000, 0x2800000, 0xa000000, 0x8000005}); +constexpr StatTable29 QRT_TABLE_29({0x1b8351dc, 0xb87135e, 0xb87135c, 0xda7b35e, 0xb871358, 0x621a116, 0xda7b356, 0x40200, 0xb871348, 0xc9e2620, 0x621a136, 0x478b16, 0xda7b316, 0x6762e20, 0x40280, 0x6202000, 0xb871248, 0x627a316, 0xc9e2420, 0xcd1ad36, 0x621a536, 0x760e20, 0x478316, 0xa760e20, 0xda7a316, 0x8000000, 0x6760e20, 0, 0x44280}); +typedef Field Field29; +#endif + +#ifdef ENABLE_FIELD_INT_30 +// 30 bit field +typedef RecLinTrans StatTable30; +typedef RecLinTrans DynTable30; +constexpr StatTable30 SQR_TABLE_30({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000}); +constexpr StatTable30 QRT_TABLE_30({0x2159df4a, 0x109134a, 0x1091348, 0x10114, 0x109134c, 0x3a203420, 0x1011c, 0x20004080, 0x109135c, 0x2005439c, 0x3a203400, 0x100400, 0x1015c, 0x3eb21930, 0x20004000, 0x20504c00, 0x109125c, 0x3b2b276c, 0x2005419c, 0x210450c0, 0x3a203000, 0x3e93186c, 0x100c00, 0x3aa23530, 0x1115c, 0x6b3286c, 0x3eb23930, 0xeb23930, 0x20000000, 0}); +typedef Field Field30; +#endif + +#ifdef ENABLE_FIELD_INT_31 +// 31 bit field +typedef RecLinTrans StatTable31; +typedef RecLinTrans DynTable31; +constexpr StatTable31 SQR_TABLE_31({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x4800000, 0x12000000, 0x48000000, 0x20000012}); +constexpr StatTable31 QRT_TABLE_31({0, 0x10110, 0x10112, 0x15076e, 0x10116, 0x117130e, 0x150766, 0x4743fa0, 0x10106, 0x1121008, 0x117132e, 0x176b248e, 0x150726, 0x172a2c88, 0x4743f20, 0x7eb81e86, 0x10006, 0x20008, 0x1121208, 0x56b2c8e, 0x117172e, 0x133f1bae, 0x176b2c8e, 0x7f2a0c8e, 0x151726, 0x10000000, 0x172a0c88, 0x60000006, 0x4747f20, 0x3eb89e80, 0x7eb89e86}); +typedef Field Field31; +#endif + +#ifdef ENABLE_FIELD_INT_32 +// 32 bit field +typedef RecLinTrans StatTable32; +typedef RecLinTrans DynTable32; +constexpr StatTable32 SQR_TABLE_32({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x8d, 0x234, 0x8d0, 0x2340, 0x8d00, 0x23400, 0x8d000, 0x234000, 0x8d0000, 0x2340000, 0x8d00000, 0x23400000, 0x8d000000, 0x3400011a, 0xd0000468, 0x40001037}); +constexpr StatTable32 QRT_TABLE_32({0x54fd1264, 0xc26fcd64, 0xc26fcd66, 0x238a7462, 0xc26fcd62, 0x973bccaa, 0x238a746a, 0x77766712, 0xc26fcd72, 0xc1bdd556, 0x973bcc8a, 0x572a094c, 0x238a742a, 0xb693be84, 0x77766792, 0x9555c03e, 0xc26fcc72, 0x568419f8, 0xc1bdd756, 0x96c3d2ca, 0x973bc88a, 0x54861fdc, 0x572a014c, 0xb79badc4, 0x238a642a, 0xb9b99fe0, 0xb6939e84, 0xc519fa86, 0x77762792, 0, 0x9555403e, 0x377627ba}); +typedef Field Field32; +#endif +} + +Sketch* ConstructGeneric4Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_25 + case 25: return new SketchImpl(implementation, 25); +#endif +#ifdef ENABLE_FIELD_INT_26 + case 26: return new SketchImpl(implementation, 26); +#endif +#ifdef ENABLE_FIELD_INT_27 + case 27: return new SketchImpl(implementation, 27); +#endif +#ifdef ENABLE_FIELD_INT_28 + case 28: return new SketchImpl(implementation, 28); +#endif +#ifdef ENABLE_FIELD_INT_29 + case 29: return new SketchImpl(implementation, 29); +#endif +#ifdef ENABLE_FIELD_INT_30 + case 30: return new SketchImpl(implementation, 30); +#endif +#ifdef ENABLE_FIELD_INT_31 + case 31: return new SketchImpl(implementation, 31); +#endif +#ifdef ENABLE_FIELD_INT_32 + case 32: return new SketchImpl(implementation, 32); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_5bytes.cpp b/src/fields/generic_5bytes.cpp new file mode 100644 index 0000000000..b06418184d --- /dev/null +++ b/src/fields/generic_5bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_5) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_33 +// 33 bit field +typedef RecLinTrans StatTable33; +typedef RecLinTrans DynTable33; +constexpr StatTable33 SQR_TABLE_33({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x802, 0x2008, 0x8020, 0x20080, 0x80200, 0x200800, 0x802000, 0x2008000, 0x8020000, 0x20080000, 0x80200000, 0x800401, 0x2001004, 0x8004010, 0x20010040, 0x80040100}); +constexpr StatTable33 QRT_TABLE_33({0xba504dd4, 0x1e2798ef2, 0x1e2798ef0, 0x6698a4ec, 0x1e2798ef4, 0x1c7f1bef0, 0x6698a4e4, 0x16da1b384, 0x1e2798ee4, 0x661ca6ec, 0x1c7f1bed0, 0x1483b87a6, 0x6698a4a4, 0x800000, 0x16da1b304, 0x1a185101c, 0x1e2798fe4, 0xaa400954, 0x661ca4ec, 0x667caeec, 0x1c7f1bad0, 0x400800, 0x1483b8fa6, 0, 0x6698b4a4, 0x1c61da4b8, 0x802000, 0x16e5dadec, 0x16da1f304, 0x62fc8eec, 0x1a185901c, 0x1661da5ec, 0x1e2788fe4}); +typedef Field Field33; +#endif + +#ifdef ENABLE_FIELD_INT_34 +// 34 bit field +typedef RecLinTrans StatTable34; +typedef RecLinTrans DynTable34; +constexpr StatTable34 SQR_TABLE_34({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x81, 0x204, 0x810, 0x2040, 0x8100, 0x20400, 0x81000, 0x204000, 0x810000, 0x2040000, 0x8100000, 0x20400000, 0x81000000, 0x204000000, 0x10000102, 0x40000408, 0x100001020}); +constexpr StatTable34 QRT_TABLE_34({0x2f973a1f6, 0x40202, 0x40200, 0x348102060, 0x40204, 0x8000420, 0x348102068, 0x1092195c8, 0x40214, 0x3f6881b6e, 0x8000400, 0x3f810383e, 0x348102028, 0x340002068, 0x109219548, 0x24015a774, 0x40314, 0x3f050343e, 0x3f688196e, 0x3f81c3a3a, 0x8000000, 0x24031a560, 0x3f810303e, 0xb08c1a12, 0x348103028, 0xb2881906, 0x340000068, 0, 0x10921d548, 0x2e131e576, 0x240152774, 0x18921d55e, 0x50314, 0x14015271c}); +typedef Field Field34; +#endif + +#ifdef ENABLE_FIELD_INT_35 +// 35 bit field +typedef RecLinTrans StatTable35; +typedef RecLinTrans DynTable35; +constexpr StatTable35 SQR_TABLE_35({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0xa, 0x28, 0xa0, 0x280, 0xa00, 0x2800, 0xa000, 0x28000, 0xa0000, 0x280000, 0xa00000, 0x2800000, 0xa000000, 0x28000000, 0xa0000000, 0x280000000, 0x200000005}); +constexpr StatTable35 QRT_TABLE_35({0x5c2038114, 0x2bf547ee8, 0x2bf547eea, 0x2bf1074e8, 0x2bf547eee, 0x1883d0736, 0x2bf1074e0, 0x100420, 0x2bf547efe, 0x400800, 0x1883d0716, 0x5e90e4a0, 0x2bf1074a0, 0x4e70ac20, 0x1004a0, 0x2f060c880, 0x2bf547ffe, 0x37d55fffe, 0x400a00, 0x3372573de, 0x1883d0316, 0x700c20, 0x5e90eca0, 0x10604880, 0x2bf1064a0, 0x18f35377e, 0x4e708c20, 0x33f557ffe, 0x1044a0, 0x1bf557ffe, 0x2f0604880, 0x200000000, 0x2bf557ffe, 0, 0x37d57fffe}); +typedef Field Field35; +#endif + +#ifdef ENABLE_FIELD_INT_36 +// 36 bit field +typedef RecLinTrans StatTable36; +typedef RecLinTrans DynTable36; +constexpr StatTable36 SQR_TABLE_36({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x201, 0x804, 0x2010, 0x8040, 0x20100, 0x80400, 0x201000, 0x804000, 0x2010000, 0x8040000, 0x20100000, 0x80400000, 0x201000000, 0x804000000, 0x10000402, 0x40001008, 0x100004020, 0x400010080}); +constexpr StatTable36 QRT_TABLE_36({0x40200, 0x8b0526186, 0x8b0526184, 0x240001000, 0x8b0526180, 0xcb6894d94, 0x240001008, 0xdb6880c22, 0x8b0526190, 0x8000200, 0xcb6894db4, 0x500424836, 0x240001048, 0x406cb2834, 0xdb6880ca2, 0x241200008, 0x8b0526090, 0xdb05021a6, 0x8000000, 0xdb01829b2, 0xcb68949b4, 0x1001000, 0x500424036, 0x106116406, 0x240000048, 0xcb29968a4, 0x406cb0834, 0, 0xdb6884ca2, 0x110010516, 0x241208008, 0x430434520, 0x8b0536090, 0x41208040, 0xdb05221a6, 0xb6884d14}); +typedef Field Field36; +#endif + +#ifdef ENABLE_FIELD_INT_37 +// 37 bit field +typedef RecLinTrans StatTable37; +typedef RecLinTrans DynTable37; +constexpr StatTable37 SQR_TABLE_37({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0xa6, 0x298, 0xa60, 0x2980, 0xa600, 0x29800, 0xa6000, 0x298000, 0xa60000, 0x2980000, 0xa600000, 0x29800000, 0xa6000000, 0x298000000, 0xa60000000, 0x980000053, 0x60000011f, 0x180000047c}); +constexpr StatTable37 QRT_TABLE_37({0xa3c62e7ba, 0xdc7a0c16a, 0xdc7a0c168, 0x12f7484546, 0xdc7a0c16c, 0xa9803a20, 0x12f748454e, 0xda07064a4, 0xdc7a0c17c, 0x123908de8e, 0xa9803a00, 0x122a888a8e, 0x12f748450e, 0x6790add8, 0xda0706424, 0x12e0a0384c, 0xdc7a0c07c, 0xcb28a2c2, 0x123908dc8e, 0xd09f85e86, 0xa9803e00, 0x124d682b6e, 0x122a88828e, 0x1738711a, 0x12f748550e, 0x73035b8, 0x67908dd8, 0xa0702438, 0xda0702424, 0xe0a0b860, 0x12e0a0b84c, 0x1c7a1c060, 0xdc7a1c07c, 0, 0xcb2aa2c2, 0x100000002c, 0x12390cdc8e}); +typedef Field Field37; +#endif + +#ifdef ENABLE_FIELD_INT_38 +// 38 bit field +typedef RecLinTrans StatTable38; +typedef RecLinTrans DynTable38; +constexpr StatTable38 SQR_TABLE_38({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x63, 0x18c, 0x630, 0x18c0, 0x6300, 0x18c00, 0x63000, 0x18c000, 0x630000, 0x18c0000, 0x6300000, 0x18c00000, 0x63000000, 0x18c000000, 0x630000000, 0x18c0000000, 0x2300000063, 0xc0000014a, 0x3000000528}); +constexpr StatTable38 QRT_TABLE_38({0x34b0ac6430, 0x2223262fa, 0x2223262f8, 0x35554405fe, 0x2223262fc, 0x355514098a, 0x35554405f6, 0x400840, 0x2223262ec, 0x1777726532, 0x35551409aa, 0x15c06fc0, 0x35554405b6, 0x1f5303fec, 0x4008c0, 0x236a21030, 0x2223263ec, 0x1a9008c00, 0x1777726732, 0x3692c60ab6, 0x3555140daa, 0x15556007ee, 0x15c067c0, 0x14a0b030f2, 0x35554415b6, 0x227c06d168, 0x1f5301fec, 0x16c3928fc2, 0x4048c0, 0x3a942c4c0, 0x236a29030, 0x1636a2902e, 0x2223363ec, 0x3a6e898276, 0x1a9028c00, 0x6de74eb2c, 0x1777766732, 0}); +typedef Field Field38; +#endif + +#ifdef ENABLE_FIELD_INT_39 +// 39 bit field +typedef RecLinTrans StatTable39; +typedef RecLinTrans DynTable39; +constexpr StatTable39 SQR_TABLE_39({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x22, 0x88, 0x220, 0x880, 0x2200, 0x8800, 0x22000, 0x88000, 0x220000, 0x880000, 0x2200000, 0x8800000, 0x22000000, 0x88000000, 0x220000000, 0x880000000, 0x2200000000, 0x800000011, 0x2000000044}); +constexpr StatTable39 QRT_TABLE_39({0x66b02a408c, 0x100420, 0x100422, 0x14206080, 0x100426, 0x5dccefab1c, 0x14206088, 0x9fc11e5b6, 0x100436, 0x5466bea62a, 0x5dccefab3c, 0x9aa110536, 0x142060c8, 0x54739ed6e2, 0x9fc11e536, 0xe7a82c080, 0x100536, 0x4002000, 0x5466bea42a, 0x6a4022000, 0x5dccefaf3c, 0x9e8118536, 0x9aa110d36, 0x5680e080, 0x142070c8, 0x7d293c5b6, 0x54739ef6e2, 0x8d680e080, 0x9fc11a536, 0x6d282c080, 0xe7a824080, 0x800000000, 0x110536, 0x2d680e080, 0x4022000, 0, 0x5466baa42a, 0x46b03a44aa, 0x6a40a2000}); +typedef Field Field39; +#endif + +#ifdef ENABLE_FIELD_INT_40 +// 40 bit field +typedef RecLinTrans StatTable40; +typedef RecLinTrans DynTable40; +constexpr StatTable40 SQR_TABLE_40({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x39, 0xe4, 0x390, 0xe40, 0x3900, 0xe400, 0x39000, 0xe4000, 0x390000, 0xe40000, 0x3900000, 0xe400000, 0x39000000, 0xe4000000, 0x390000000, 0xe40000000, 0x3900000000, 0xe400000000, 0x900000004b, 0x400000015e}); +constexpr StatTable40 QRT_TABLE_40({0x624b3cecc, 0xbc5c3f4c6, 0xbc5c3f4c4, 0xde1603e2c, 0xbc5c3f4c0, 0xaabec06cea, 0xde1603e24, 0x6cd9f724c2, 0xbc5c3f4d0, 0xcde1743818, 0xaabec06cca, 0xa138c314ca, 0xde1603e64, 0xaafc00f01a, 0x6cd9f72442, 0xcdca11bb4, 0xbc5c3f5d0, 0xa00002001a, 0xcde1743a18, 0xdf1407b90, 0xaabec068ca, 0xc043b482c8, 0xa138c31cca, 0xcb86977e3c, 0xde1602e64, 0x604596a326, 0xaafc00d01a, 0xcc1c165d0, 0x6cd9f76442, 0x673c94da26, 0xcdca19bb4, 0x67c0940a26, 0xbc5c2f5d0, 0xa4dca19bae, 0xa00000001a, 0x1bc5c2f5d0, 0xcde1703a18, 0, 0xdf1487b90, 0x8df1487b8a}); +typedef Field Field40; +#endif +} + +Sketch* ConstructGeneric5Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_33 + case 33: return new SketchImpl(implementation, 33); +#endif +#ifdef ENABLE_FIELD_INT_34 + case 34: return new SketchImpl(implementation, 34); +#endif +#ifdef ENABLE_FIELD_INT_35 + case 35: return new SketchImpl(implementation, 35); +#endif +#ifdef ENABLE_FIELD_INT_36 + case 36: return new SketchImpl(implementation, 36); +#endif +#ifdef ENABLE_FIELD_INT_37 + case 37: return new SketchImpl(implementation, 37); +#endif +#ifdef ENABLE_FIELD_INT_38 + case 38: return new SketchImpl(implementation, 38); +#endif +#ifdef ENABLE_FIELD_INT_39 + case 39: return new SketchImpl(implementation, 39); +#endif +#ifdef ENABLE_FIELD_INT_40 + case 40: return new SketchImpl(implementation, 40); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_6bytes.cpp b/src/fields/generic_6bytes.cpp new file mode 100644 index 0000000000..becb26e875 --- /dev/null +++ b/src/fields/generic_6bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_6) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_41 +// 41 bit field +typedef RecLinTrans StatTable41; +typedef RecLinTrans DynTable41; +constexpr StatTable41 SQR_TABLE_41({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x12, 0x48, 0x120, 0x480, 0x1200, 0x4800, 0x12000, 0x48000, 0x120000, 0x480000, 0x1200000, 0x4800000, 0x12000000, 0x48000000, 0x120000000, 0x480000000, 0x1200000000, 0x4800000000, 0x12000000000, 0x8000000012}); +constexpr StatTable41 QRT_TABLE_41({0, 0x1599a5e0b0, 0x1599a5e0b2, 0x105c119e0, 0x1599a5e0b6, 0x1a2030452a6, 0x105c119e8, 0x1a307c55b2e, 0x1599a5e0a6, 0x1ee3f47bc8e, 0x1a203045286, 0x400808, 0x105c119a8, 0x1a3038573a6, 0x1a307c55bae, 0x4d2882a520, 0x1599a5e1a6, 0x1ffbaa0b720, 0x1ee3f47be8e, 0x4d68c22528, 0x1a203045686, 0x200006, 0x400008, 0x1b79a21b200, 0x105c109a8, 0x1ef3886a526, 0x1a3038553a6, 0x1b692209200, 0x1a307c51bae, 0x5d99a4e1a6, 0x4d28822520, 0x185e109ae, 0x1599a4e1a6, 0x4e3f43be88, 0x1ffbaa2b720, 0x4000000000, 0x1ee3f43be8e, 0x18000000006, 0x4d68ca2528, 0xa203145680, 0x1a203145686}); +typedef Field Field41; +#endif + +#ifdef ENABLE_FIELD_INT_42 +// 42 bit field +typedef RecLinTrans StatTable42; +typedef RecLinTrans DynTable42; +constexpr StatTable42 SQR_TABLE_42({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x81, 0x204, 0x810, 0x2040, 0x8100, 0x20400, 0x81000, 0x204000, 0x810000, 0x2040000, 0x8100000, 0x20400000, 0x81000000, 0x204000000, 0x810000000, 0x2040000000, 0x8100000000, 0x20400000000, 0x1000000102, 0x4000000408, 0x10000001020}); +constexpr StatTable42 QRT_TABLE_42({0x810200080, 0x120810806, 0x120810804, 0x1068c1a1000, 0x120810800, 0x34005023008, 0x1068c1a1008, 0x800004080, 0x120810810, 0x162818a10, 0x34005023028, 0x42408a14, 0x1068c1a1048, 0x1001040, 0x800004000, 0xb120808906, 0x120810910, 0x34000020068, 0x162818810, 0x68c021400, 0x34005023428, 0x10004000, 0x42408214, 0x162418214, 0x1068c1a0048, 0xb002018116, 0x1003040, 0x10008180448, 0x800000000, 0x62c08b04, 0xb120800906, 0x2408d1a3060, 0x120800910, 0x34401003028, 0x34000000068, 0, 0x162858810, 0xa042058116, 0x68c0a1400, 0x8162858806, 0x34005123428, 0x3068c0a1468}); +typedef Field Field42; +#endif + +#ifdef ENABLE_FIELD_INT_43 +// 43 bit field +typedef RecLinTrans StatTable43; +typedef RecLinTrans DynTable43; +constexpr StatTable43 SQR_TABLE_43({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0xb2, 0x2c8, 0xb20, 0x2c80, 0xb200, 0x2c800, 0xb2000, 0x2c8000, 0xb20000, 0x2c80000, 0xb200000, 0x2c800000, 0xb2000000, 0x2c8000000, 0xb20000000, 0x2c80000000, 0xb200000000, 0x2c800000000, 0x32000000059, 0x4800000013d, 0x20000000446}); +constexpr StatTable43 QRT_TABLE_43({0x2bccc2d6f6c, 0x4bccc2d6f54, 0x4bccc2d6f56, 0x7cc7bc61df0, 0x4bccc2d6f52, 0x7d13b404b10, 0x7cc7bc61df8, 0x37456e9ac5a, 0x4bccc2d6f42, 0x4e042c6a6, 0x7d13b404b30, 0x4a56de9ef4c, 0x7cc7bc61db8, 0x14bc18d8e, 0x37456e9acda, 0x7c89f84fb1e, 0x4bccc2d6e42, 0x7ffae40d210, 0x4e042c4a6, 0x366f45dd06, 0x7d13b404f30, 0x496fcaf8cca, 0x4a56de9e74c, 0x370b62b6af4, 0x7cc7bc60db8, 0x1498185a8, 0x14bc1ad8e, 0x7e602c46a98, 0x37456e9ecda, 0x36ccc2c6e74, 0x7c89f847b1e, 0x7e27d06d516, 0x4bccc2c6e42, 0x7f93302c396, 0x7ffae42d210, 0x3dd3440706, 0x4e046c4a6, 0x78bbc09da36, 0x366f4ddd06, 0, 0x7d13b504f30, 0x8bbc09da00, 0x496fc8f8cca}); +typedef Field Field43; +#endif + +#ifdef ENABLE_FIELD_INT_44 +// 44 bit field +typedef RecLinTrans StatTable44; +typedef RecLinTrans DynTable44; +constexpr StatTable44 SQR_TABLE_44({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x21, 0x84, 0x210, 0x840, 0x2100, 0x8400, 0x21000, 0x84000, 0x210000, 0x840000, 0x2100000, 0x8400000, 0x21000000, 0x84000000, 0x210000000, 0x840000000, 0x2100000000, 0x8400000000, 0x21000000000, 0x84000000000, 0x10000000042, 0x40000000108}); +constexpr StatTable44 QRT_TABLE_44({0xf05334f4f6e, 0x4002016, 0x4002014, 0xf04350e6246, 0x4002010, 0x4935b379a26, 0xf04350e624e, 0xf84250c228e, 0x4002000, 0xf04300e521e, 0x4935b379a06, 0xb966838dd48, 0xf04350e620e, 0xf7b8b80feda, 0xf84250c220e, 0xf972e097d5e, 0x4002100, 0x8000020000, 0xf04300e501e, 0x430025000, 0x4935b379e06, 0xf976a09dc5e, 0xb966838d548, 0xf84218c029a, 0xf04350e720e, 0x4925f36bf06, 0xf7b8b80deda, 0xb047d3ee758, 0xf84250c620e, 0xf80350e720e, 0xf972e09fd5e, 0x8091825284, 0x4012100, 0x9015063210, 0x8000000000, 0xff31a028c5e, 0xf04300a501e, 0x44340b7100, 0x4300a5000, 0, 0x4935b279e06, 0xa976b2dce18, 0xf976a29dc5e, 0x8935b279e18}); +typedef Field Field44; +#endif + +#ifdef ENABLE_FIELD_INT_45 +// 45 bit field +typedef RecLinTrans StatTable45; +typedef RecLinTrans DynTable45; +constexpr StatTable45 SQR_TABLE_45({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x36, 0xd8, 0x360, 0xd80, 0x3600, 0xd800, 0x36000, 0xd8000, 0x360000, 0xd80000, 0x3600000, 0xd800000, 0x36000000, 0xd8000000, 0x360000000, 0xd80000000, 0x3600000000, 0xd800000000, 0x36000000000, 0xd8000000000, 0x16000000001b, 0x18000000005a}); +constexpr StatTable45 QRT_TABLE_45({0xede34e3e0fc, 0x1554148191aa, 0x1554148191a8, 0x1767be1dc4a6, 0x1554148191ac, 0x26bd4931492, 0x1767be1dc4ae, 0x233ab9c454a, 0x1554148191bc, 0x16939e8bb3dc, 0x26bd49314b2, 0x3c6ca8bac52, 0x1767be1dc4ee, 0x16caa5054c16, 0x233ab9c45ca, 0x14a1649628bc, 0x1554148190bc, 0x3c382881252, 0x16939e8bb1dc, 0x3c7ca0aa160, 0x26bd49310b2, 0x27f40158000, 0x3c6ca8ba452, 0x173fc092853c, 0x1767be1dd4ee, 0x16cbe284f25c, 0x16caa5056c16, 0x155559002f96, 0x233ab9c05ca, 0x26eb8908b32, 0x14a16496a8bc, 0x15440885333c, 0x1554148090bc, 0x17d60702e0, 0x3c3828a1252, 0x54548d10b2, 0x16939e8fb1dc, 0x3ac1e81b1d2, 0x3c7ca02a160, 0x166bd48310bc, 0x26bd48310b2, 0, 0x27f40358000, 0x10000000000e, 0x3c6cacba452}); +typedef Field Field45; +#endif + +#ifdef ENABLE_FIELD_INT_46 +// 46 bit field +typedef RecLinTrans StatTable46; +typedef RecLinTrans DynTable46; +constexpr StatTable46 SQR_TABLE_46({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000, 0xc0000000, 0x300000000, 0xc00000000, 0x3000000000, 0xc000000000, 0x30000000000, 0xc0000000000, 0x300000000000}); +constexpr StatTable46 QRT_TABLE_46({0x211c4fd486ba, 0x100104a, 0x1001048, 0x104d0492d4, 0x100104c, 0x20005040c820, 0x104d0492dc, 0x40008080, 0x100105c, 0x24835068ce00, 0x20005040c800, 0x200000400800, 0x104d04929c, 0x100904325c, 0x40008000, 0x25da9e77daf0, 0x100115c, 0x1184e1696f0, 0x24835068cc00, 0x24825169dd5c, 0x20005040cc00, 0x3ea3241c60c0, 0x200000400000, 0x211c4e5496f0, 0x104d04829c, 0x20005340d86c, 0x100904125c, 0x24835968de5c, 0x4000c000, 0x6400a0c0, 0x25da9e775af0, 0x118cf1687ac, 0x101115c, 0x1ea1745cacc0, 0x1184e1496f0, 0x20181e445af0, 0x2483506ccc00, 0x20240060c0, 0x24825161dd5c, 0x1e21755dbd9c, 0x20005050cc00, 0x26a3746cacc0, 0x3ea3243c60c0, 0xea3243c60c0, 0x200000000000, 0}); +typedef Field Field46; +#endif + +#ifdef ENABLE_FIELD_INT_47 +// 47 bit field +typedef RecLinTrans StatTable47; +typedef RecLinTrans DynTable47; +constexpr StatTable47 SQR_TABLE_47({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x42, 0x108, 0x420, 0x1080, 0x4200, 0x10800, 0x42000, 0x108000, 0x420000, 0x1080000, 0x4200000, 0x10800000, 0x42000000, 0x108000000, 0x420000000, 0x1080000000, 0x4200000000, 0x10800000000, 0x42000000000, 0x108000000000, 0x420000000000, 0x80000000042, 0x200000000108}); +constexpr StatTable47 QRT_TABLE_47({0, 0x1001040, 0x1001042, 0x1047043076, 0x1001046, 0x112471c241e, 0x104704307e, 0x4304e052168, 0x1001056, 0x10004000, 0x112471c243e, 0x172a09c949d6, 0x104704303e, 0x4002020, 0x4304e0521e8, 0x5400e220, 0x1001156, 0x172b08c85080, 0x10004200, 0x41200b0800, 0x112471c203e, 0x172f0cca50a0, 0x172a09c941d6, 0x7eb88a11c1d6, 0x104704203e, 0x1044042020, 0x4000020, 0x42001011156, 0x4304e0561e8, 0x172a28c95880, 0x54006220, 0x112931cc21e, 0x1011156, 0x53670f283e, 0x172b08ca5080, 0x7a80c414a03e, 0x10044200, 0x40000000000, 0x4120030800, 0x1928318801e, 0x112470c203e, 0x799283188000, 0x172f0cea50a0, 0x1eb88a91c1c8, 0x172a098941d6, 0x3ea8cc95e1f6, 0x7eb88a91c1d6}); +typedef Field Field47; +#endif + +#ifdef ENABLE_FIELD_INT_48 +// 48 bit field +typedef RecLinTrans StatTable48; +typedef RecLinTrans DynTable48; +constexpr StatTable48 SQR_TABLE_48({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x2d, 0xb4, 0x2d0, 0xb40, 0x2d00, 0xb400, 0x2d000, 0xb4000, 0x2d0000, 0xb40000, 0x2d00000, 0xb400000, 0x2d000000, 0xb4000000, 0x2d0000000, 0xb40000000, 0x2d00000000, 0xb400000000, 0x2d000000000, 0xb4000000000, 0x2d0000000000, 0xb40000000000, 0xd0000000005a, 0x40000000011f}); +constexpr StatTable48 QRT_TABLE_48({0xc00442c284f0, 0xc16b7fda410a, 0xc16b7fda4108, 0xada3b5c79fbe, 0xc16b7fda410c, 0x16f3c18d5b0, 0xada3b5c79fb6, 0x7090a381f64, 0xc16b7fda411c, 0xcafc15d179f8, 0x16f3c18d590, 0x6630880e534e, 0xada3b5c79ff6, 0xa13dd1f49826, 0x7090a381fe4, 0xb87560f6a74, 0xc16b7fda401c, 0xaaaaffff0012, 0xcafc15d17bf8, 0xaafd15f07bf6, 0x16f3c18d190, 0x60000020000e, 0x6630880e5b4e, 0xcb977fcb401c, 0xada3b5c78ff6, 0x6663420cad0, 0xa13dd1f4b826, 0xc0045fc2f41c, 0x7090a385fe4, 0x6762e24b834, 0xb87560fea74, 0xc6351fed241c, 0xc16b7fdb401c, 0x60065622ea7a, 0xaaaafffd0012, 0xdf9562bea74, 0xcafc15d57bf8, 0x6657ea057bea, 0xaafd15f87bf6, 0xa79329ddaa66, 0x16f3c08d190, 0xa39229f0aa66, 0x60000000000e, 0x175fb4468ad0, 0x6630884e5b4e, 0, 0xcb977f4b401c, 0x2630884e5b40}); +typedef Field Field48; +#endif +} + +Sketch* ConstructGeneric6Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_41 + case 41: return new SketchImpl(implementation, 41); +#endif +#ifdef ENABLE_FIELD_INT_42 + case 42: return new SketchImpl(implementation, 42); +#endif +#ifdef ENABLE_FIELD_INT_43 + case 43: return new SketchImpl(implementation, 43); +#endif +#ifdef ENABLE_FIELD_INT_44 + case 44: return new SketchImpl(implementation, 44); +#endif +#ifdef ENABLE_FIELD_INT_45 + case 45: return new SketchImpl(implementation, 45); +#endif +#ifdef ENABLE_FIELD_INT_46 + case 46: return new SketchImpl(implementation, 46); +#endif +#ifdef ENABLE_FIELD_INT_47 + case 47: return new SketchImpl(implementation, 47); +#endif +#ifdef ENABLE_FIELD_INT_48 + case 48: return new SketchImpl(implementation, 48); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_7bytes.cpp b/src/fields/generic_7bytes.cpp new file mode 100644 index 0000000000..8222f37a64 --- /dev/null +++ b/src/fields/generic_7bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_7) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_49 +// 49 bit field +typedef RecLinTrans StatTable49; +typedef RecLinTrans DynTable49; +constexpr StatTable49 SQR_TABLE_49({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x402, 0x1008, 0x4020, 0x10080, 0x40200, 0x100800, 0x402000, 0x1008000, 0x4020000, 0x10080000, 0x40200000, 0x100800000, 0x402000000, 0x1008000000, 0x4020000000, 0x10080000000, 0x40200000000, 0x100800000000, 0x402000000000, 0x1008000000000, 0x20000000402, 0x80000001008, 0x200000004020, 0x800000010080}); +constexpr StatTable49 QRT_TABLE_49({0, 0x10004196, 0x10004194, 0x5099461f080, 0x10004190, 0x40840600c20, 0x5099461f088, 0x58a56349cfde, 0x10004180, 0x48641a0c03fe, 0x40840600c00, 0x10084002848, 0x5099461f0c8, 0x4002048, 0x58a56349cf5e, 0x5088460a048, 0x10004080, 0x4c2852624dde, 0x48641a0c01fe, 0x14893129c280, 0x40840600800, 0x1eb23c323ace8, 0x10084002048, 0x48740a09417e, 0x5099461e0c8, 0x40852604d96, 0x4000048, 0x5cad2b29c37e, 0x58a563498f5e, 0x20000200, 0x50884602048, 0x10000000000, 0x10014080, 0x4c2a56624d96, 0x4c2852604dde, 0x1ee2347438ca0, 0x48641a0801fe, 0x480000000048, 0x14893121c280, 0x14091121c080, 0x40840700800, 0x1a5099561e17e, 0x1eb23c303ace8, 0x8740a894136, 0x10084402048, 0x18101c501ace8, 0x48740a89417e, 0x15dace6286f96, 0x5099561e0c8}); +typedef Field Field49; +#endif + +#ifdef ENABLE_FIELD_INT_50 +// 50 bit field +typedef RecLinTrans StatTable50; +typedef RecLinTrans DynTable50; +constexpr StatTable50 SQR_TABLE_50({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x1d, 0x74, 0x1d0, 0x740, 0x1d00, 0x7400, 0x1d000, 0x74000, 0x1d0000, 0x740000, 0x1d00000, 0x7400000, 0x1d000000, 0x74000000, 0x1d0000000, 0x740000000, 0x1d00000000, 0x7400000000, 0x1d000000000, 0x74000000000, 0x1d0000000000, 0x740000000000, 0x1d00000000000, 0x340000000001d, 0x1000000000053}); +constexpr StatTable50 QRT_TABLE_50({0xfbdfa3ae9d4c, 0x38143245a4878, 0x38143245a487a, 0x38527487e7492, 0x38143245a487e, 0x3124c61f56d2a, 0x38527487e749a, 0xfa8c91b087c0, 0x38143245a486e, 0x3eca48c6196be, 0x3124c61f56d0a, 0x380000040080a, 0x38527487e74da, 0x976b2d8b39b4, 0xfa8c91b08740, 0xfa8cd5b02724, 0x38143245a496e, 0x316291dd013fe, 0x3eca48c6194be, 0x10344122064, 0x3124c61f5690a, 0x68c5f006ee40, 0x380000040000a, 0x852749fe64d0, 0x38527487e64da, 0x37ef8e9d0e9da, 0x976b2d8b19b4, 0x37fabd1cef34a, 0xfa8c91b0c740, 0x96282d9159b4, 0xfa8cd5b0a724, 0x464a8249dd0, 0x38143245b496e, 0x37eaa8ddc94be, 0x316291dd213fe, 0x392446035690a, 0x3eca48c6594be, 0x974b258b4964, 0x103441a2064, 0x385a7c87fb4da, 0x3124c61e5690a, 0xeb8ad5d9a724, 0x68c5f026ee40, 0x3724c61e5690a, 0x380000000000a, 0x3a8c5f026ee4a, 0x8527497e64d0, 0, 0x38527497e64da, 0x2fbdfa2ae8d0a}); +typedef Field Field50; +#endif + +#ifdef ENABLE_FIELD_INT_51 +// 51 bit field +typedef RecLinTrans StatTable51; +typedef RecLinTrans DynTable51; +constexpr StatTable51 SQR_TABLE_51({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x96, 0x258, 0x960, 0x2580, 0x9600, 0x25800, 0x96000, 0x258000, 0x960000, 0x2580000, 0x9600000, 0x25800000, 0x96000000, 0x258000000, 0x960000000, 0x2580000000, 0x9600000000, 0x25800000000, 0x96000000000, 0x258000000000, 0x960000000000, 0x2580000000000, 0x160000000004b, 0x580000000012c, 0x6000000000426}); +constexpr StatTable51 QRT_TABLE_51({0x778bf2703d152, 0x2aaaafbff2092, 0x2aaaafbff2090, 0x4d2119c7e7780, 0x2aaaafbff2094, 0x65de1df8ae194, 0x4d2119c7e7788, 0x67d63d7ba262c, 0x2aaaafbff2084, 0x28ff003f4167c, 0x65de1df8ae1b4, 0x658397fb1d034, 0x4d2119c7e77c8, 0x4d7c9284526ba, 0x67d63d7ba26ac, 0x6666333fc0cbe, 0x2aaaafbff2184, 0x295b807ab55ee, 0x28ff003f4147c, 0x2aaabfffe0016, 0x65de1df8ae5b4, 0x209210349d60, 0x658397fb1d834, 0x4d215dc7cf1c8, 0x4d2119c7e67c8, 0x662b2b3d7b4be, 0x4d7c9284506ba, 0x255af00b36e0, 0x67d63d7ba66ac, 0x65de1fb8ac1a6, 0x6666333fc8cbe, 0x662f3b3ded4be, 0x2aaaafbfe2184, 0x663a9dbc3a426, 0x295b807a955ee, 0x4cdc9ec128928, 0x28ff003f0147c, 0x28a0c93cd511c, 0x2aaabfff60016, 0x65d73cf8e78d4, 0x65de1df9ae5b4, 0x4d5eddc44f1c8, 0x209210149d60, 0x357fcc506c8a, 0x658397ff1d834, 0, 0x4d215dcfcf1c8, 0x63f536f5d4554, 0x4d2119d7e67c8, 0x4000000000022, 0x662b2b1d7b4be}); +typedef Field Field51; +#endif + +#ifdef ENABLE_FIELD_INT_52 +// 52 bit field +typedef RecLinTrans StatTable52; +typedef RecLinTrans DynTable52; +constexpr StatTable52 SQR_TABLE_52({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x9, 0x24, 0x90, 0x240, 0x900, 0x2400, 0x9000, 0x24000, 0x90000, 0x240000, 0x900000, 0x2400000, 0x9000000, 0x24000000, 0x90000000, 0x240000000, 0x900000000, 0x2400000000, 0x9000000000, 0x24000000000, 0x90000000000, 0x240000000000, 0x900000000000, 0x2400000000000, 0x9000000000000, 0x4000000000012}); +constexpr StatTable52 QRT_TABLE_52({0xc108165459b0e, 0x10004086, 0x10004084, 0xc00000100104e, 0x10004080, 0x2041810a545b0, 0xc000001001046, 0x1181e055efc0, 0x10004090, 0x40810214390, 0x2041810a54590, 0xc000141019106, 0xc000001001006, 0x10816045ab40, 0x1181e055ef40, 0xc000111015196, 0x10004190, 0xe045c19af44a2, 0x40810214190, 0xe045809ad0532, 0x2041810a54190, 0xdb387a03fe646, 0xc000141019906, 0x2000000800000, 0xc000001000006, 0x2486548199c34, 0x108160458b40, 0x2041808a50534, 0x1181e055af40, 0xc0408312153d6, 0xc00011101d196, 0x21499f0e0eed0, 0x10014190, 0xe15dff9faabe2, 0xe045c19ad44a2, 0xdb787b01ea7d6, 0x40810254190, 0xe484409180532, 0xe045809a50532, 0xc14095164d896, 0x2041810b54190, 0x217dee8fb7a74, 0xdb387a01fe646, 0x441810b54190, 0xc000141419906, 0xc3386e15e7f46, 0x2000000000000, 0x1000141419900, 0xc000000000006, 0, 0x248654a199c34, 0xa48654a199c32}); +typedef Field Field52; +#endif + +#ifdef ENABLE_FIELD_INT_53 +// 53 bit field +typedef RecLinTrans StatTable53; +typedef RecLinTrans DynTable53; +constexpr StatTable53 SQR_TABLE_53({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x8e, 0x238, 0x8e0, 0x2380, 0x8e00, 0x23800, 0x8e000, 0x238000, 0x8e0000, 0x2380000, 0x8e00000, 0x23800000, 0x8e000000, 0x238000000, 0x8e0000000, 0x2380000000, 0x8e00000000, 0x23800000000, 0x8e000000000, 0x238000000000, 0x8e0000000000, 0x2380000000000, 0x8e00000000000, 0x3800000000047, 0xe00000000011c, 0x18000000000437}); +constexpr StatTable53 QRT_TABLE_53({0xf940b90844076, 0x1f940b90844052, 0x1f940b90844050, 0x9d2a063b43e64, 0x1f940b90844054, 0x936f69323ec14, 0x9d2a063b43e6c, 0xe12270a88898, 0x1f940b90844044, 0x1f917f00bb5a3c, 0x936f69323ec34, 0x1f622df85b46ee, 0x9d2a063b43e2c, 0x9bc65ab040b66, 0xe12270a88818, 0x958330b931986, 0x1f940b90844144, 0x98e2a06e32e0, 0x1f917f00bb583c, 0x1f877970dc1024, 0x936f69323e834, 0x16cc3c9b1558c2, 0x1f622df85b4eee, 0x16de1c3351dae8, 0x9d2a063b42e2c, 0x1fecdc7855f8ee, 0x9bc65ab042b66, 0x933821b1cb6fe, 0xe12270a8c818, 0x1f675958641c0e, 0x958330b939986, 0x9d97e050e960, 0x1f940b90854144, 0x1f820fa0e38adc, 0x98e2a06c32e0, 0x1650f0e358a010, 0x1f917f00bf583c, 0x1643af4b037a3a, 0x1f877970d41024, 0x1ffe2c281d8c16, 0x936f69333e834, 0xf00d50ffccf8, 0x16cc3c9b3558c2, 0x16bc31cbca943a, 0x1f622df81b4eee, 0xa6cbd8007232, 0x16de1c33d1dae8, 0x15d2a062b42e10, 0x9d2a062b42e2c, 0x1aa77896586ca, 0x1fecdc7a55f8ee, 0, 0x9bc65af042b66}); +typedef Field Field53; +#endif + +#ifdef ENABLE_FIELD_INT_54 +// 54 bit field +typedef RecLinTrans StatTable54; +typedef RecLinTrans DynTable54; +constexpr StatTable54 SQR_TABLE_54({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x201, 0x804, 0x2010, 0x8040, 0x20100, 0x80400, 0x201000, 0x804000, 0x2010000, 0x8040000, 0x20100000, 0x80400000, 0x201000000, 0x804000000, 0x2010000000, 0x8040000000, 0x20100000000, 0x80400000000, 0x201000000000, 0x804000000000, 0x2010000000000, 0x8040000000000, 0x20100000000000, 0x400000000402, 0x1000000001008, 0x4000000004020, 0x10000000010080}); +constexpr StatTable54 QRT_TABLE_54({0x201008000200, 0x26c10916494994, 0x26c10916494996, 0x40008008, 0x26c10916494992, 0x141a2434c12d12, 0x40008000, 0x36c00110594c22, 0x26c10916494982, 0x200000040200, 0x141a2434c12d32, 0x10010816104534, 0x40008040, 0x36da60b01308b2, 0x36c00110594ca2, 0x48200209000, 0x26c10916494882, 0x41b6da2d86106, 0x200000040000, 0x32db2c228965b0, 0x141a2434c12932, 0x9000000200048, 0x10010816104d34, 0x32db68b2832da4, 0x40009040, 0x40045928b4902, 0x36da60b01328b2, 0x1000040000, 0x36c00110590ca2, 0x101b69865a4120, 0x48200201000, 0x22da6434912884, 0x26c10916484882, 0x9000240208008, 0x41b6da2da6106, 0x22c14484c20180, 0x200000000000, 0x4016db29b6812, 0x32db2c228165b0, 0x9008200201048, 0x141a2434d12932, 0x32c36ca2c264b0, 0x9000000000048, 0x140a65b48a2c32, 0x10010816504d34, 0, 0x32db68b2032da4, 0x404490824814, 0x41009040, 0x14da60a4536126, 0x40045908b4902, 0x8000041009008, 0x36da60b41328b2, 0x6db68b2032c12}); +typedef Field Field54; +#endif + +#ifdef ENABLE_FIELD_INT_55 +// 55 bit field +typedef RecLinTrans StatTable55; +typedef RecLinTrans DynTable55; +constexpr StatTable55 SQR_TABLE_55({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x102, 0x408, 0x1020, 0x4080, 0x10200, 0x40800, 0x102000, 0x408000, 0x1020000, 0x4080000, 0x10200000, 0x40800000, 0x102000000, 0x408000000, 0x1020000000, 0x4080000000, 0x10200000000, 0x40800000000, 0x102000000000, 0x408000000000, 0x1020000000000, 0x4080000000000, 0x10200000000000, 0x40800000000000, 0x2000000000102, 0x8000000000408, 0x20000000001020}); +constexpr StatTable55 QRT_TABLE_55({0, 0x121d57b6623fde, 0x121d57b6623fdc, 0x68908340d10e00, 0x121d57b6623fd8, 0x100300510e20, 0x68908340d10e08, 0x10004096, 0x121d57b6623fc8, 0x100010000, 0x100300510e00, 0x7ea8c890a088e8, 0x68908340d10e48, 0x68809540871648, 0x10004016, 0x68808000808068, 0x121d57b6623ec8, 0x68909240d41c48, 0x100010200, 0x6884c170ad0216, 0x100300510a00, 0x68848160a50200, 0x7ea8c890a080e8, 0x7eecbca04ab4b6, 0x68908340d11e48, 0x120c54b62234c8, 0x68809540873648, 0x69929240d61c48, 0x10000016, 0x68808060800000, 0x68808000800068, 0x80000080, 0x121d57b6633ec8, 0x7ea8cb90a18ae8, 0x68909240d61c48, 0x16284090200080, 0x100050200, 0x474302a345e, 0x6884c170a50216, 0x166cbca0cab4de, 0x100300410a00, 0x1000000000000, 0x68848160850200, 0x688cc1f0a50296, 0x7ea8c890e080e8, 0x7e8cc1f0a50280, 0x7eecbca0cab4b6, 0x68000000000068, 0x68908341d11e48, 0x7880954487365e, 0x120c54b42234c8, 0x9929248d61c20, 0x68809544873648, 0x41121208561c20, 0x69929248d61c48}); +typedef Field Field55; +#endif + +#ifdef ENABLE_FIELD_INT_56 +// 56 bit field +typedef RecLinTrans StatTable56; +typedef RecLinTrans DynTable56; +constexpr StatTable56 SQR_TABLE_56({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x95, 0x254, 0x950, 0x2540, 0x9500, 0x25400, 0x95000, 0x254000, 0x950000, 0x2540000, 0x9500000, 0x25400000, 0x95000000, 0x254000000, 0x950000000, 0x2540000000, 0x9500000000, 0x25400000000, 0x95000000000, 0x254000000000, 0x950000000000, 0x2540000000000, 0x9500000000000, 0x25400000000000, 0x95000000000000, 0x5400000000012a, 0x5000000000043d, 0x40000000001061}); +constexpr StatTable56 QRT_TABLE_56({0x10004084, 0xd058f12fd5925e, 0xd058f12fd5925c, 0x41a60b5566d9f0, 0xd058f12fd59258, 0xbda60a142740ba, 0x41a60b5566d9f8, 0xd059f1afc5e688, 0xd058f12fd59248, 0xfc040841615a22, 0xbda60a1427409a, 0xbda60b5426c1ca, 0x41a60b5566d9b8, 0x1a60b4166b950, 0xd059f1afc5e608, 0xfc000041409822, 0xd058f12fd59348, 0xd1ee7a4ef4185c, 0xfc040841615822, 0x9049759b80b4a4, 0xbda60a1427449a, 0xd258e06f301e18, 0xbda60b5426c9ca, 0x6dfeeb3bf6d7d2, 0x41a60b5566c9b8, 0xbdef3ed4ae398a, 0x1a60b41669950, 0xd1ef3f8eeff04c, 0xd059f1afc5a608, 0xbda203340783de, 0xfc000041401822, 0x2dfefbaff2b27a, 0xd058f12fd49348, 0xfdb788a0706776, 0xd1ee7a4ef6185c, 0x2e5de0ae41337a, 0xfc040841655822, 0x41eb17d5ceecf8, 0x9049759b88b4a4, 0x40048874211afc, 0xbda60a1437449a, 0xd04a720f93400c, 0xd258e06f101e18, 0xbc559cf5ac7fce, 0xbda60b5466c9ca, 0x6dc9759b88b4d6, 0x6dfeeb3b76d7d2, 0x92feea7b275af0, 0x41a60b5466c9b8, 0, 0xbdef3ed6ae398a, 0x2811d5edd8ee2a, 0x1a60b45669950, 0xb1a60b5466c9ca, 0xd1ef3f86eff04c, 0xec493582c8f032}); +typedef Field Field56; +#endif +} + +Sketch* ConstructGeneric7Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_49 + case 49: return new SketchImpl(implementation, 49); +#endif +#ifdef ENABLE_FIELD_INT_50 + case 50: return new SketchImpl(implementation, 50); +#endif +#ifdef ENABLE_FIELD_INT_51 + case 51: return new SketchImpl(implementation, 51); +#endif +#ifdef ENABLE_FIELD_INT_52 + case 52: return new SketchImpl(implementation, 52); +#endif +#ifdef ENABLE_FIELD_INT_53 + case 53: return new SketchImpl(implementation, 53); +#endif +#ifdef ENABLE_FIELD_INT_54 + case 54: return new SketchImpl(implementation, 54); +#endif +#ifdef ENABLE_FIELD_INT_55 + case 55: return new SketchImpl(implementation, 55); +#endif +#ifdef ENABLE_FIELD_INT_56 + case 56: return new SketchImpl(implementation, 56); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_8bytes.cpp b/src/fields/generic_8bytes.cpp new file mode 100644 index 0000000000..8bb63e8d3e --- /dev/null +++ b/src/fields/generic_8bytes.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* This file was substantially auto-generated by doc/gen_params.sage. */ +#include "../fielddefines.h" + +#if defined(ENABLE_FIELD_BYTES_INT_8) + +#include "generic_common_impl.h" + +#include "../lintrans.h" +#include "../sketch_impl.h" + +#endif + +#include "../sketch.h" + +namespace { +#ifdef ENABLE_FIELD_INT_57 +// 57 bit field +typedef RecLinTrans StatTable57; +typedef RecLinTrans DynTable57; +constexpr StatTable57 SQR_TABLE_57({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x22, 0x88, 0x220, 0x880, 0x2200, 0x8800, 0x22000, 0x88000, 0x220000, 0x880000, 0x2200000, 0x8800000, 0x22000000, 0x88000000, 0x220000000, 0x880000000, 0x2200000000, 0x8800000000, 0x22000000000, 0x88000000000, 0x220000000000, 0x880000000000, 0x2200000000000, 0x8800000000000, 0x22000000000000, 0x88000000000000, 0x20000000000011, 0x80000000000044}); +constexpr StatTable57 QRT_TABLE_57({0xd0c3a82c902426, 0x232aa54103915e, 0x232aa54103915c, 0x1763e291e61699c, 0x232aa541039158, 0x1f424d678bb15e, 0x1763e291e616994, 0x26fd8122f10d36, 0x232aa541039148, 0x1e0a0206002000, 0x1f424d678bb17e, 0x5d72563f39d7e, 0x1763e291e6169d4, 0x1519beb9d597df4, 0x26fd8122f10db6, 0x150c3a87c90e4aa, 0x232aa541039048, 0x15514891f6179d4, 0x1e0a0206002200, 0x14ec9ba7a94c6aa, 0x1f424d678bb57e, 0x1e0f4286382420, 0x5d72563f3957e, 0x4000080000, 0x1763e291e6179d4, 0x1ac0e804882000, 0x1519beb9d595df4, 0x1f430d6793b57e, 0x26fd8122f14db6, 0x3c68e806882000, 0x150c3a87c9064aa, 0x1484fe18b915e, 0x232aa541029048, 0x14f91eb9b595df4, 0x15514891f6379d4, 0x48f6a82380420, 0x1e0a0206042200, 0x14b1beb99595df4, 0x14ec9ba7a9cc6aa, 0x4cf2a82b00420, 0x1f424d679bb57e, 0x26aa0002000000, 0x1e0f4286182420, 0x173f1039dd17df4, 0x5d72563b3957e, 0x4aa0002000000, 0x4000880000, 0x16d31eb9b595df4, 0x1763e291f6179d4, 0x20000000000000, 0x1ac0e806882000, 0x2caa0002000000, 0x1519beb99595df4, 0, 0x1f430d6f93b57e, 0x73e90d6d93b57e, 0x26fd8132f14db6}); +typedef Field Field57; +#endif + +#ifdef ENABLE_FIELD_INT_58 +// 58 bit field +typedef RecLinTrans StatTable58; +typedef RecLinTrans DynTable58; +constexpr StatTable58 SQR_TABLE_58({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x80001, 0x200004, 0x800010, 0x2000040, 0x8000100, 0x20000400, 0x80001000, 0x200004000, 0x800010000, 0x2000040000, 0x8000100000, 0x20000400000, 0x80001000000, 0x200004000000, 0x800010000000, 0x2000040000000, 0x8000100000000, 0x20000400000000, 0x80001000000000, 0x200004000000000, 0x10000100002, 0x40000400008, 0x100001000020, 0x400004000080, 0x1000010000200, 0x4000040000800, 0x10000100002000, 0x40000400008000, 0x100001000020000}); +constexpr StatTable58 QRT_TABLE_58({0x2450096792a5c5c, 0x610014271011c, 0x610014271011e, 0x1f0cb811314ea88, 0x610014271011a, 0x8000000420, 0x1f0cb811314ea80, 0x265407ad8a20bcc, 0x610014271010a, 0x3d18be98392ebd0, 0x8000000400, 0xc29b930e407056, 0x1f0cb811314eac0, 0x1fcef001154dee8, 0x265407ad8a20b4c, 0xc69b924c61f94a, 0x610014271000a, 0x211006895845190, 0x3d18be98392e9d0, 0x54007accac09cc, 0x8000000000, 0xc08b934e107854, 0xc29b930e407856, 0x275407adc220bcc, 0x1f0cb811314fac0, 0x1f6db815164ea8a, 0x1fcef001154fee8, 0x1b2db801945e396, 0x265407ad8a24b4c, 0x21100ec95865590, 0xc69b924c61794a, 0x273507b1e530ad6, 0x610014270000a, 0x1b4cb835b34e29c, 0x211006895865190, 0x3839bf20d47e016, 0x3d18be98396e9d0, 0x3858bd34f36e01c, 0x54007acca409cc, 0, 0x8000100000, 0xc29a130e507856, 0xc08b934e307854, 0x13253921d448296, 0xc29b930e007856, 0x13c60935f6486bc, 0x275407adca20bcc, 0x3571be8c5e6c9da, 0x1f0cb811214fac0, 0x410014261011c, 0x1f6db815364ea8a, 0x13a50921d1486b6, 0x1fcef001554fee8, 0x64001249245a5c, 0x1b2db801145e396, 0x8610014670200a, 0x265407ac8a24b4c, 0x1a5cbfbdeb0f30c}); +typedef Field Field58; +#endif + +#ifdef ENABLE_FIELD_INT_59 +// 59 bit field +typedef RecLinTrans StatTable59; +typedef RecLinTrans DynTable59; +constexpr StatTable59 SQR_TABLE_59({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x12a, 0x4a8, 0x12a0, 0x4a80, 0x12a00, 0x4a800, 0x12a000, 0x4a8000, 0x12a0000, 0x4a80000, 0x12a00000, 0x4a800000, 0x12a000000, 0x4a8000000, 0x12a0000000, 0x4a80000000, 0x12a00000000, 0x4a800000000, 0x12a000000000, 0x4a8000000000, 0x12a0000000000, 0x4a80000000000, 0x12a00000000000, 0x4a800000000000, 0x12a000000000000, 0x4a8000000000000, 0x2a000000000012a, 0x28000000000043d, 0x200000000001061}); +constexpr StatTable59 QRT_TABLE_59({0x38d905ab028567a, 0x789fa6ed3b44d72, 0x789fa6ed3b44d70, 0x74ec857e93d828c, 0x789fa6ed3b44d74, 0x116b3c1203c96, 0x74ec857e93d8284, 0xc25ebc3871e280, 0x789fa6ed3b44d64, 0x47a37c3d910b6, 0x116b3c1203cb6, 0xc7322d7a8f48de, 0x74ec857e93d82c4, 0xb509a0ea52e496, 0xc25ebc3871e200, 0x74fdee4681d3e0c, 0x789fa6ed3b44c64, 0x7ffbbd080b2f09a, 0x47a37c3d912b6, 0xd5c937bae506c8, 0x116b3c12038b6, 0xb173c76987625e, 0xc7322d7a8f40de, 0x7591ff36b3a682c, 0x74ec857e93d92c4, 0x72b253bfbfc90c4, 0xb509a0ea52c496, 0x79f2e7b10e6d452, 0xc25ebc3871a200, 0x78c86e951086aac, 0x74fdee4681dbe0c, 0x78c96eb514c602c, 0x789fa6ed3b54c64, 0xc34818b95658e8, 0x7ffbbd080b0f09a, 0x7399f563b1980f2, 0x47a37c3dd12b6, 0xa29e0e28c58880, 0xd5c937baed06c8, 0x788ac23520ac82c, 0x116b3c13038b6, 0xa2c857e83d92b6, 0xb173c769a7625e, 0x608da990122e48, 0xc7322d7acf40de, 0xa3a89269eebefe, 0x7591ff36bba682c, 0xa25ebc2871a200, 0x74ec857e83d92c4, 0x11f62e419f1cfe, 0x72b253bf9fc90c4, 0x7425ebc2871a272, 0xb509a0ee52c496, 0x4ed8555979c8de, 0x79f2e7b18e6d452, 0x6c3580d5915d4d2, 0xc25ebc2871a200, 0, 0x78c86e971086aac}); +typedef Field Field59; +#endif + +#ifdef ENABLE_FIELD_INT_60 +// 60 bit field +typedef RecLinTrans StatTable60; +typedef RecLinTrans DynTable60; +constexpr StatTable60 SQR_TABLE_60({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00, 0x3000, 0xc000, 0x30000, 0xc0000, 0x300000, 0xc00000, 0x3000000, 0xc000000, 0x30000000, 0xc0000000, 0x300000000, 0xc00000000, 0x3000000000, 0xc000000000, 0x30000000000, 0xc0000000000, 0x300000000000, 0xc00000000000, 0x3000000000000, 0xc000000000000, 0x30000000000000, 0xc0000000000000, 0x300000000000000, 0xc00000000000000}); +constexpr StatTable60 QRT_TABLE_60({0x6983c00fe00104a, 0x804570322e054e6, 0x804570322e054e4, 0x15673387e0a4e4, 0x804570322e054e0, 0x100010110, 0x15673387e0a4ec, 0x920d01f34442a70, 0x804570322e054f0, 0x7a8dc0f2e4058f0, 0x100010130, 0x120c01f140462f0, 0x15673387e0a4ac, 0x7bdbb2ca9a4fe5c, 0x920d01f34442af0, 0xe9c6b039ce0c4ac, 0x804570322e055f0, 0xfac8b080ca20c00, 0x7a8dc0f2e405af0, 0x7a8dc4b2e4a59f0, 0x100010530, 0x10000100000, 0x120c01f14046af0, 0x131a02d91c5db6c, 0x15673387e0b4ac, 0x15623387d0b4ac, 0x7bdbb2ca9a4de5c, 0x7ffbbbca0a8ee5c, 0x920d01f34446af0, 0x800000020000000, 0xe9c6b039ce044ac, 0x81130302500f000, 0x804570322e155f0, 0x935b72eb3a48e9c, 0xfac8b080ca00c00, 0x120c016140563c0, 0x7a8dc0f2e445af0, 0x7bcbb3ca8a4ee5c, 0x7a8dc4b2e4259f0, 0xc4000a0300, 0x100110530, 0x11623285c1b19c, 0x10000300000, 0x420890090c3000, 0x120c01f14446af0, 0x68d7b33b9e0b4ac, 0x131a02d9145db6c, 0xe8ccb1e18a56fc0, 0x15673386e0b4ac, 0x7aadc8f2e485af0, 0x15623385d0b4ac, 0x4a0990093c3000, 0x7bdbb2cada4de5c, 0xf9d6b3389e0b4ac, 0x7ffbbbca8a8ee5c, 0xdf6ba38cec84ac, 0x920d01f24446af0, 0x520d01f24446af0, 0x800000000000000, 0}); +typedef Field Field60; +#endif + +#ifdef ENABLE_FIELD_INT_61 +// 61 bit field +typedef RecLinTrans StatTable61; +typedef RecLinTrans DynTable61; +constexpr StatTable61 SQR_TABLE_61({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4e, 0x138, 0x4e0, 0x1380, 0x4e00, 0x13800, 0x4e000, 0x138000, 0x4e0000, 0x1380000, 0x4e00000, 0x13800000, 0x4e000000, 0x138000000, 0x4e0000000, 0x1380000000, 0x4e00000000, 0x13800000000, 0x4e000000000, 0x138000000000, 0x4e0000000000, 0x1380000000000, 0x4e00000000000, 0x13800000000000, 0x4e000000000000, 0x138000000000000, 0x4e0000000000000, 0x1380000000000000, 0xe0000000000004e, 0x180000000000011f}); +constexpr StatTable61 QRT_TABLE_61({0x171d34fcdac955d0, 0x12cfc8c049e1c96, 0x12cfc8c049e1c94, 0x71d34fcdac955c2, 0x12cfc8c049e1c90, 0x631c871de564852, 0x71d34fcdac955ca, 0x129fa6407f27300, 0x12cfc8c049e1c80, 0x7094f6fdd0a3b12, 0x631c871de564872, 0xdb28cee59c8256a, 0x71d34fcdac9558a, 0xc8a0be15a915472, 0x129fa6407f27380, 0x12dfcb4058e0b80, 0x12cfc8c049e1d80, 0x117d7f04ad0118, 0x7094f6fdd0a3912, 0x621b576dbe35b6a, 0x631c871de564c72, 0x13c808a013a1ee0, 0xdb28cee59c82d6a, 0x113d79842a0272, 0x71d34fcdac9458a, 0x719776b580b6a98, 0xc8a0be15a917472, 0x6633498d6db760a, 0x129fa6407f23380, 0xbd4ae9e8c3e7560, 0x12dfcb4058e8b80, 0x8000000a, 0x12cfc8c049f1d80, 0x634ce9add3b26ea, 0x117d7f04af0118, 0xda3f19c5d66258a, 0x7094f6fdd0e3912, 0xb87427e85e71560, 0x621b576dbeb5b6a, 0xc8b0b085b8c4e0a, 0x631c871de464c72, 0x1538fc8649458a, 0x13c808a011a1ee0, 0xcddbca6d1cfe360, 0xdb28cee59882d6a, 0xae80f550d1ffff2, 0x113d7984aa0272, 0xda7770f5f195912, 0x71d34fcdbc9458a, 0x137c8a049a1ee0, 0x719776b5a0b6a98, 0xded39a9d236ba78, 0xc8a0be15e917472, 0x6732488ca7ce0a, 0x6633498dedb760a, 0xc0406d0527cb80a, 0x129fa6417f23380, 0x3d4ae9eac3e756a, 0xbd4ae9eac3e7560, 0, 0x12dfcb4458e8b80}); +typedef Field Field61; +#endif + +#ifdef ENABLE_FIELD_INT_62 +// 62 bit field +typedef RecLinTrans StatTable62; +typedef RecLinTrans DynTable62; +constexpr StatTable62 SQR_TABLE_62({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x20000001, 0x80000004, 0x200000010, 0x800000040, 0x2000000100, 0x8000000400, 0x20000001000, 0x80000004000, 0x200000010000, 0x800000040000, 0x2000000100000, 0x8000000400000, 0x20000001000000, 0x80000004000000, 0x200000010000000, 0x800000040000000, 0x2000000100000000, 0x440000002, 0x1100000008, 0x4400000020, 0x11000000080, 0x44000000200, 0x110000000800, 0x440000002000, 0x1100000008000, 0x4400000020000, 0x11000000080000, 0x44000000200000, 0x110000000800000, 0x440000002000000, 0x1100000008000000}); +constexpr StatTable62 QRT_TABLE_62({0x30268b6fba455d2c, 0x200000006, 0x200000004, 0x3d67cb6c1fe66c76, 0x200000000, 0x3fc4f1901abfa400, 0x3d67cb6c1fe66c7e, 0x35e79b6c0a66bcbe, 0x200000010, 0x1e9372bc57a9941e, 0x3fc4f1901abfa420, 0x21ec9d424957a5b0, 0x3d67cb6c1fe66c3e, 0x1cb35a6e52f5fb0e, 0x35e79b6c0a66bc3e, 0x215481024c13a730, 0x200000110, 0x1c324a6c52f75b08, 0x1e9372bc57a9961e, 0x3764a9d00f676820, 0x3fc4f1901abfa020, 0x355481020e132730, 0x21ec9d424957adb0, 0x3c43c32c0f34301e, 0x3d67cb6c1fe67c3e, 0x1496122c45259728, 0x1cb35a6e52f5db0e, 0x15e418405b72ec20, 0x35e79b6c0a66fc3e, 0x30268b6e3a445c38, 0x215481024c132730, 0x100010114, 0x200010110, 0, 0x1c324a6c52f55b08, 0x215581044d133776, 0x1e9372bc57ad961e, 0x2155810e4d133766, 0x3764a9d00f6f6820, 0x2157833c4d12323e, 0x3fc4f1901aafa020, 0x1c324a4252f55b58, 0x355481020e332730, 0x28332fc0509d41e, 0x21ec9d424917adb0, 0x215783be4d12332e, 0x3c43c32c0fb4301e, 0x2157822c4d06363e, 0x3d67cb6c1ee67c3e, 0x23f6b9d2484afb78, 0x1496122c47259728, 0x14b8184047648a80, 0x1cb35a6e56f5db0e, 0x3fe4f1901aefa820, 0x15e418405372ec20, 0x3d5fd72c1be276be, 0x35e79b6c1a66fc3e, 0x14b038d24774cf10, 0x30268b6e1a445c38, 0x1d17022e43a7172e, 0x215481020c132730, 0x2157022e4d07372e}); +typedef Field Field62; +#endif + +#ifdef ENABLE_FIELD_INT_63 +// 63 bit field +typedef RecLinTrans StatTable63; +typedef RecLinTrans DynTable63; +constexpr StatTable63 SQR_TABLE_63({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4000000000000000, 0x6, 0x18, 0x60, 0x180, 0x600, 0x1800, 0x6000, 0x18000, 0x60000, 0x180000, 0x600000, 0x1800000, 0x6000000, 0x18000000, 0x60000000, 0x180000000, 0x600000000, 0x1800000000, 0x6000000000, 0x18000000000, 0x60000000000, 0x180000000000, 0x600000000000, 0x1800000000000, 0x6000000000000, 0x18000000000000, 0x60000000000000, 0x180000000000000, 0x600000000000000, 0x1800000000000000, 0x6000000000000000}); +constexpr StatTable63 QRT_TABLE_63({0, 0x100010114, 0x100010116, 0x1001701051372, 0x100010112, 0x1000040220, 0x100170105137a, 0x5107703453bba, 0x100010102, 0x101130117155a, 0x1000040200, 0x40000200800, 0x100170105133a, 0x103151a137276d8, 0x5107703453b3a, 0x134e65fc7c222be0, 0x100010002, 0x100030103115a, 0x101130117175a, 0x106052d103f4de2, 0x1000040600, 0x15122707691d3a, 0x40000200000, 0x4530770bc57b3a, 0x100170105033a, 0x103011a131256d8, 0x103151a137256d8, 0x176f29eb55c7a8da, 0x5107703457b3a, 0x130b158b7767d0da, 0x134e65fc7c22abe0, 0x7bcaf59d2f62d3e2, 0x100000002, 0x1001401041260, 0x100030101115a, 0x5107e03443ab8, 0x101130113175a, 0x1043701251b3a, 0x106052d10374de2, 0x134e657d7c232be2, 0x1000140600, 0x106073d103b4be2, 0x15122707491d3a, 0x4438600ac07800, 0x40000600000, 0x176a199c5682d3e0, 0x4530770b457b3a, 0x7bca759c2f62d3e0, 0x100170005033a, 0x6116d02572de2, 0x103011a111256d8, 0x1346656d7c372de2, 0x103151a177256d8, 0x643c600aa07800, 0x176f29eb5dc7a8da, 0x7b4b758b2f67d0da, 0x5107713457b3a, 0x104570776b457b3a, 0x130b158b5767d0da, 0x734e65fc3c22abe0, 0x134e65fc3c22abe0, 0x4000000000000000, 0x7bcaf59daf62d3e2}); +typedef Field Field63; +#endif + +#ifdef ENABLE_FIELD_INT_64 +// 64 bit field +typedef RecLinTrans StatTable64; +typedef RecLinTrans DynTable64; +constexpr StatTable64 SQR_TABLE_64({0x1, 0x4, 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000, 0x4000000, 0x10000000, 0x40000000, 0x100000000, 0x400000000, 0x1000000000, 0x4000000000, 0x10000000000, 0x40000000000, 0x100000000000, 0x400000000000, 0x1000000000000, 0x4000000000000, 0x10000000000000, 0x40000000000000, 0x100000000000000, 0x400000000000000, 0x1000000000000000, 0x4000000000000000, 0x1b, 0x6c, 0x1b0, 0x6c0, 0x1b00, 0x6c00, 0x1b000, 0x6c000, 0x1b0000, 0x6c0000, 0x1b00000, 0x6c00000, 0x1b000000, 0x6c000000, 0x1b0000000, 0x6c0000000, 0x1b00000000, 0x6c00000000, 0x1b000000000, 0x6c000000000, 0x1b0000000000, 0x6c0000000000, 0x1b00000000000, 0x6c00000000000, 0x1b000000000000, 0x6c000000000000, 0x1b0000000000000, 0x6c0000000000000, 0x1b00000000000000, 0x6c00000000000000, 0xb00000000000001b, 0xc00000000000005a}); +constexpr StatTable64 QRT_TABLE_64({0x19c9369f278adc02, 0x84b2b22ab2383ee4, 0x84b2b22ab2383ee6, 0x9d7b84b495b3e3f6, 0x84b2b22ab2383ee2, 0x37c470b49213f790, 0x9d7b84b495b3e3fe, 0x1000a0105137c, 0x84b2b22ab2383ef2, 0x368e964a8edce1fc, 0x37c470b49213f7b0, 0x19c9368e278fdf4c, 0x9d7b84b495b3e3be, 0x2e4da23cbc7d4570, 0x1000a010513fc, 0x84f35772bac24232, 0x84b2b22ab2383ff2, 0x37c570ba9314e4fc, 0x368e964a8edce3fc, 0xb377c390213cdb0e, 0x37c470b49213f3b0, 0x85ed5a3aa99c24f2, 0x19c9368e278fd74c, 0xaabff0000780000e, 0x9d7b84b495b3f3be, 0x84b6b3dab03038f2, 0x2e4da23cbc7d6570, 0x511ea03494ffc, 0x1000a010553fc, 0xae0c0220343c6c0e, 0x84f35772bac2c232, 0x800000008000000e, 0x84b2b22ab2393ff2, 0xb376c29c202bc97e, 0x37c570ba9316e4fc, 0x9c3062488879e6ce, 0x368e964a8ed8e3fc, 0x41e42c08e47e70, 0xb377c3902134db0e, 0x85b9b108a60f56ce, 0x37c470b49203f3b0, 0x19dd3b6e21f3cb4c, 0x85ed5a3aa9bc24f2, 0x198ddf682c428ac0, 0x19c9368e27cfd74c, 0x4b7c68431ca84b0, 0xaabff0000700000e, 0x8040655489ffefbe, 0x9d7b84b494b3f3be, 0x18c1354e32bfa74c, 0x84b6b3dab23038f2, 0xaaf613cc0f74627e, 0x2e4da23cb87d6570, 0x3248b3d6b3342a8c, 0x511ea0b494ffc, 0xb60813c00e70700e, 0x1000a110553fc, 0x1e0d022a05393ffc, 0xae0c0220143c6c0e, 0xe0c0220143c6c00, 0x84f35772fac2c232, 0xc041e55948fbfdce, 0x800000000000000e, 0}); +typedef Field Field64; +#endif +} + +Sketch* ConstructGeneric8Bytes(int bits, int implementation) +{ + switch (bits) { +#ifdef ENABLE_FIELD_INT_57 + case 57: return new SketchImpl(implementation, 57); +#endif +#ifdef ENABLE_FIELD_INT_58 + case 58: return new SketchImpl(implementation, 58); +#endif +#ifdef ENABLE_FIELD_INT_59 + case 59: return new SketchImpl(implementation, 59); +#endif +#ifdef ENABLE_FIELD_INT_60 + case 60: return new SketchImpl(implementation, 60); +#endif +#ifdef ENABLE_FIELD_INT_61 + case 61: return new SketchImpl(implementation, 61); +#endif +#ifdef ENABLE_FIELD_INT_62 + case 62: return new SketchImpl(implementation, 62); +#endif +#ifdef ENABLE_FIELD_INT_63 + case 63: return new SketchImpl(implementation, 63); +#endif +#ifdef ENABLE_FIELD_INT_64 + case 64: return new SketchImpl(implementation, 64); +#endif + default: return nullptr; + } +} diff --git a/src/fields/generic_common_impl.h b/src/fields/generic_common_impl.h new file mode 100644 index 0000000000..1d1d030b5f --- /dev/null +++ b/src/fields/generic_common_impl.h @@ -0,0 +1,70 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_FIELDS_GENERIC_COMMON_IMPL_H_ +#define _MINISKETCH_FIELDS_GENERIC_COMMON_IMPL_H_ 1 + +#include + +#include "../int_utils.h" +#include "../lintrans.h" + +namespace { + +/** Generic implementation for fields whose elements can be represented by an integer type. */ +template class Field +{ + typedef BitsInt O; + typedef LFSR L; + +public: + typedef I Elem; + constexpr int Bits() const { return B; } + + constexpr inline Elem Mul2(Elem val) const { return L::Call(val); } + + class Multiplier + { + T table; + public: + explicit Multiplier(const Field&, Elem a) { table.template Build(a); } + constexpr inline Elem operator()(Elem a) const { return table.template Map(a); } + }; + + Elem Mul(Elem a, Elem b) const { return GFMul(a, b); } + + /** Compute the square of a. */ + inline constexpr Elem Sqr(Elem a) const { return SQR->template Map(a); } + + /** Compute x such that x^2 + x = a (undefined result if no solution exists). */ + inline constexpr Elem Qrt(Elem a) const { return QRT->template Map(a); } + + /** Compute the inverse of x1. */ + Elem Inv(Elem a) const { return InvExtGCD(a); } + + /** Generate a random field element. */ + Elem FromSeed(uint64_t seed) const { + uint64_t k0 = 0x496e744669656c64ull; // "IntField" + uint64_t k1 = seed; + uint64_t count = ((uint64_t)B) << 32; + Elem ret; + do { + ret = O::Mask(I(SipHash(k0, k1, count++))); + } while(ret == 0); + return ret; + } + + Elem Deserialize(BitReader& in) const { return in.template Read(); } + + void Serialize(BitWriter& out, Elem val) const { out.template Write(val); } + + constexpr Elem FromUint64(uint64_t x) const { return O::Mask(I(x)); } + constexpr uint64_t ToUint64(Elem val) const { return uint64_t(val); } +}; + +} + +#endif diff --git a/src/int_utils.h b/src/int_utils.h new file mode 100644 index 0000000000..62b2c38a29 --- /dev/null +++ b/src/int_utils.h @@ -0,0 +1,290 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_INT_UTILS_H_ +#define _MINISKETCH_INT_UTILS_H_ + +#include + +#include +#include +#include + +#ifdef _MSC_VER +# include +#endif + +template +static constexpr inline uint64_t Rot(uint64_t x) { return (x << bits) | (x >> (64 - bits)); } + +static inline void SipHashRound(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3) { + v0 += v1; v1 = Rot<13>(v1); v1 ^= v0; + v0 = Rot<32>(v0); + v2 += v3; v3 = Rot<16>(v3); v3 ^= v2; + v0 += v3; v3 = Rot<21>(v3); v3 ^= v0; + v2 += v1; v1 = Rot<17>(v1); v1 ^= v2; + v2 = Rot<32>(v2); +} + +inline uint64_t SipHash(uint64_t k0, uint64_t k1, uint64_t data) { + uint64_t v0 = 0x736f6d6570736575ULL ^ k0; + uint64_t v1 = 0x646f72616e646f6dULL ^ k1; + uint64_t v2 = 0x6c7967656e657261ULL ^ k0; + uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ data; + SipHashRound(v0, v1, v2, v3); + SipHashRound(v0, v1, v2, v3); + v0 ^= data; + v3 ^= 0x800000000000000ULL; + SipHashRound(v0, v1, v2, v3); + SipHashRound(v0, v1, v2, v3); + v0 ^= 0x800000000000000ULL; + v2 ^= 0xFF; + SipHashRound(v0, v1, v2, v3); + SipHashRound(v0, v1, v2, v3); + SipHashRound(v0, v1, v2, v3); + SipHashRound(v0, v1, v2, v3); + return v0 ^ v1 ^ v2 ^ v3; +} + +class BitWriter { + unsigned char state = 0; + int offset = 0; + unsigned char* out; + +public: + BitWriter(unsigned char* output) : out(output) {} + + template + inline void Write(I val) { + int bits = BITS; + if (bits + offset >= 8) { + state |= ((val & ((I(1) << (8 - offset)) - 1)) << offset); + *(out++) = state; + val >>= (8 - offset); + bits -= 8 - offset; + offset = 0; + state = 0; + } + while (bits >= 8) { + *(out++) = val & 255; + val >>= 8; + bits -= 8; + } + state |= ((val & ((I(1) << bits) - 1)) << offset); + offset += bits; + } + + inline void Flush() { + if (offset) { + *(out++) = state; + state = 0; + offset = 0; + } + } +}; + +class BitReader { + unsigned char state = 0; + int offset = 0; + const unsigned char* in; + +public: + BitReader(const unsigned char* input) : in(input) {} + + template + inline I Read() { + int bits = BITS; + if (offset >= bits) { + I ret = state & ((1 << bits) - 1); + state >>= bits; + offset -= bits; + return ret; + } + I val = state; + int out = offset; + while (out + 8 <= bits) { + val |= ((I(*(in++))) << out); + out += 8; + } + if (out < bits) { + unsigned char c = *(in++); + val |= (c & ((I(1) << (bits - out)) - 1)) << out; + state = c >> (bits - out); + offset = 8 - (bits - out); + } else { + state = 0; + offset = 0; + } + return val; + } +}; + +/** Return a value of type I with its `bits` lowest bits set (bits must be > 0). */ +template +constexpr inline I Mask() { return ((I((I(-1)) << (std::numeric_limits::digits - BITS))) >> (std::numeric_limits::digits - BITS)); } + +/** Compute the smallest power of two that is larger than val. */ +template +static inline int CountBits(I val, int max) { +#ifdef HAVE_CLZ + (void)max; + if (val == 0) return 0; + if (std::numeric_limits::digits >= std::numeric_limits::digits) { + return std::numeric_limits::digits - __builtin_clz(val); + } else if (std::numeric_limits::digits >= std::numeric_limits::digits) { + return std::numeric_limits::digits - __builtin_clzl(val); + } else { + return std::numeric_limits::digits - __builtin_clzll(val); + } +#elif _MSC_VER + (void)max; + unsigned long index; + unsigned char ret; + if (std::numeric_limits::digits <= 32) { + ret = _BitScanReverse(&index, val); + } else { + ret = _BitScanReverse64(&index, val); + } + if (!ret) return 0; + return index; +#else + while (max && (val >> (max - 1) == 0)) --max; + return max; +#endif +} + +template +class BitsInt { +private: + static_assert(std::is_integral::value && std::is_unsigned::value, "BitsInt requires an unsigned integer type"); + static_assert(BITS > 0 && BITS <= std::numeric_limits::digits, "BitsInt requires 1 <= Bits <= representation type size"); + + static constexpr I MASK = Mask(); + +public: + + typedef I Repr; + + static constexpr int SIZE = BITS; + + static void inline Swap(I& a, I& b) { + std::swap(a, b); + } + + static constexpr inline bool IsZero(I a) { return a == 0; } + static constexpr inline I Mask(I val) { return val & MASK; } + static constexpr inline I Shift(I val, int bits) { return ((val << bits) & MASK); } + static constexpr inline I UnsafeShift(I val, int bits) { return (val << bits); } + + template + static constexpr inline int MidBits(I val) { + static_assert(Count > 0, "BITSInt::MidBits needs Count > 0"); + static_assert(Count + Offset <= BITS, "BitsInt::MidBits overflow of Count+Offset"); + return (val >> Offset) & ((I(1) << Count) - 1); + } + + template + static constexpr inline int TopBits(I val) { + static_assert(Count > 0, "BitsInt::TopBits needs Count > 0"); + static_assert(Count <= BITS, "BitsInt::TopBits needs Offset <= BITS"); + return val >> (BITS - Count); + } + + static inline constexpr I CondXorWith(I val, bool cond, I v) { + return val ^ (-I(cond) & v); + } + + template + static inline constexpr I CondXorWith(I val, bool cond) { + return val ^ (-I(cond) & MOD); + } + + static inline int Bits(I val, int max) { return CountBits(val, max); } +}; + +/** Class which implements a stateless LFSR for generic moduli. */ +template +struct LFSR { + typedef typename F::Repr I; + /** Shift a value `a` up once, treating it as an `N`-bit LFSR, with pattern `MOD`. */ + static inline constexpr I Call(const I& a) { + return F::template CondXorWith(F::Shift(a, 1), F::template TopBits<1>(a)); + } +}; + +/** Helper class for carryless multiplications. */ +template struct GFMulHelper; +template struct GFMulHelper +{ + static inline constexpr I Run(const I& a, const I& b) { return I(0); } +}; +template struct GFMulHelper +{ + static inline constexpr I Run(const I& a, const I& b) { return F::CondXorWith(GFMulHelper::Run(L::Call(a), b), F::template MidBits(b), a); } +}; + +/** Compute the carry-less multiplication of a and b, with N bits, using L as LFSR type. */ +template inline constexpr I GFMul(const I& a, const I& b) { return GFMulHelper::Run(a, b); } + +/** Compute the inverse of x using an extgcd algorithm. */ +template +inline I InvExtGCD(I x) +{ + if (F::IsZero(x)) return x; + I t(0), newt(1); + I r(MOD), newr = x; + int rlen = BITS + 1, newrlen = F::Bits(newr, BITS); + while (newr) { + int q = rlen - newrlen; + r ^= F::Shift(newr, q); + t ^= F::UnsafeShift(newt, q); + rlen = F::Bits(r, rlen - 1); + if (r < newr) { + F::Swap(t, newt); + F::Swap(r, newr); + std::swap(rlen, newrlen); + } + } + return t; +} + +/** Compute the inverse of x1 using an exponentiation ladder. + * + * The `MUL` argument is a multiplication function, `SQR` is a squaring function, and the `SQRi` arguments + * compute x**(2**i). + */ +template +inline I InvLadder(I x1) +{ + static constexpr int INV_EXP = BITS - 1; + I x2 = (INV_EXP >= 2) ? MUL(SQR(x1), x1) : I(); + I x4 = (INV_EXP >= 4) ? MUL(SQR2(x2), x2) : I(); + I x8 = (INV_EXP >= 8) ? MUL(SQR4(x4), x4) : I(); + I x16 = (INV_EXP >= 16) ? MUL(SQR8(x8), x8) : I(); + I x32 = (INV_EXP >= 32) ? MUL(SQR16(x16), x16) : I(); + I r; + if (INV_EXP >= 32) { + r = x32; + } else if (INV_EXP >= 16) { + r = x16; + } else if (INV_EXP >= 8) { + r = x8; + } else if (INV_EXP >= 4) { + r = x4; + } else if (INV_EXP >= 2) { + r = x2; + } else { + r = x1; + } + if (INV_EXP >= 32 && (INV_EXP & 16)) r = MUL(SQR16(r), x16); + if (INV_EXP >= 16 && (INV_EXP & 8)) r = MUL(SQR8(r), x8); + if (INV_EXP >= 8 && (INV_EXP & 4)) r = MUL(SQR4(r), x4); + if (INV_EXP >= 4 && (INV_EXP & 2)) r = MUL(SQR2(r), x2); + if (INV_EXP >= 2 && (INV_EXP & 1)) r = MUL(SQR(r), x1); + return SQR(r); +} + +#endif diff --git a/src/lintrans.h b/src/lintrans.h new file mode 100644 index 0000000000..b9d8ea8e1d --- /dev/null +++ b/src/lintrans.h @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_LINTRANS_H_ +#define _MINISKETCH_LINTRANS_H_ + +#include "int_utils.h" + +/** A type to represent integers in the type system. */ +template struct Num {}; + +/** A Linear N-bit transformation over the field I. */ +template class LinTrans { +private: + I table[1 << N]; +public: + LinTrans() = default; + + /* Construct a transformation over 3 to 8 bits, using the images of each bit. */ + constexpr LinTrans(I a, I b) : table{I(0), I(a), I(b), I(a ^ b)} {} + constexpr LinTrans(I a, I b, I c) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c)} {} + constexpr LinTrans(I a, I b, I c, I d) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c), I(d), I(a ^ d), I(b ^ d), I(a ^ b ^ d), I(c ^ d), I(a ^ c ^ d), I(b ^ c ^ d), I(a ^ b ^ c ^ d)} {} + constexpr LinTrans(I a, I b, I c, I d, I e) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c), I(d), I(a ^ d), I(b ^ d), I(a ^ b ^ d), I(c ^ d), I(a ^ c ^ d), I(b ^ c ^ d), I(a ^ b ^ c ^ d), I(e), I(a ^ e), I(b ^ e), I(a ^ b ^ e), I(c ^ e), I(a ^ c ^ e), I(b ^ c ^ e), I(a ^ b ^ c ^ e), I(d ^ e), I(a ^ d ^ e), I(b ^ d ^ e), I(a ^ b ^ d ^ e), I(c ^ d ^ e), I(a ^ c ^ d ^ e), I(b ^ c ^ d ^ e), I(a ^ b ^ c ^ d ^ e)} {} + constexpr LinTrans(I a, I b, I c, I d, I e, I f) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c), I(d), I(a ^ d), I(b ^ d), I(a ^ b ^ d), I(c ^ d), I(a ^ c ^ d), I(b ^ c ^ d), I(a ^ b ^ c ^ d), I(e), I(a ^ e), I(b ^ e), I(a ^ b ^ e), I(c ^ e), I(a ^ c ^ e), I(b ^ c ^ e), I(a ^ b ^ c ^ e), I(d ^ e), I(a ^ d ^ e), I(b ^ d ^ e), I(a ^ b ^ d ^ e), I(c ^ d ^ e), I(a ^ c ^ d ^ e), I(b ^ c ^ d ^ e), I(a ^ b ^ c ^ d ^ e), I(f), I(a ^ f), I(b^ f), I(a ^ b ^ f), I(c^ f), I(a ^ c ^ f), I(b ^ c ^ f), I(a ^ b ^ c ^ f), I(d ^ f), I(a ^ d ^ f), I(b ^ d ^ f), I(a ^ b ^ d ^ f), I(c ^ d ^ f), I(a ^ c ^ d ^ f), I(b ^ c ^ d ^ f), I(a ^ b ^ c ^ d ^ f), I(e ^ f), I(a ^ e ^ f), I(b ^ e ^ f), I(a ^ b ^ e ^ f), I(c ^ e ^ f), I(a ^ c ^ e ^ f), I(b ^ c ^ e ^ f), I(a ^ b ^ c ^ e ^ f), I(d ^ e ^ f), I(a ^ d ^ e ^ f), I(b ^ d ^ e ^ f), I(a ^ b ^ d ^ e ^ f), I(c ^ d ^ e ^ f), I(a ^ c ^ d ^ e ^ f), I(b ^ c ^ d ^ e ^ f), I(a ^ b ^ c ^ d ^ e ^ f)} {} + constexpr LinTrans(I a, I b, I c, I d, I e, I f, I g) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c), I(d), I(a ^ d), I(b ^ d), I(a ^ b ^ d), I(c ^ d), I(a ^ c ^ d), I(b ^ c ^ d), I(a ^ b ^ c ^ d), I(e), I(a ^ e), I(b ^ e), I(a ^ b ^ e), I(c ^ e), I(a ^ c ^ e), I(b ^ c ^ e), I(a ^ b ^ c ^ e), I(d ^ e), I(a ^ d ^ e), I(b ^ d ^ e), I(a ^ b ^ d ^ e), I(c ^ d ^ e), I(a ^ c ^ d ^ e), I(b ^ c ^ d ^ e), I(a ^ b ^ c ^ d ^ e), I(f), I(a ^ f), I(b^ f), I(a ^ b ^ f), I(c^ f), I(a ^ c ^ f), I(b ^ c ^ f), I(a ^ b ^ c ^ f), I(d ^ f), I(a ^ d ^ f), I(b ^ d ^ f), I(a ^ b ^ d ^ f), I(c ^ d ^ f), I(a ^ c ^ d ^ f), I(b ^ c ^ d ^ f), I(a ^ b ^ c ^ d ^ f), I(e ^ f), I(a ^ e ^ f), I(b ^ e ^ f), I(a ^ b ^ e ^ f), I(c ^ e ^ f), I(a ^ c ^ e ^ f), I(b ^ c ^ e ^ f), I(a ^ b ^ c ^ e ^ f), I(d ^ e ^ f), I(a ^ d ^ e ^ f), I(b ^ d ^ e ^ f), I(a ^ b ^ d ^ e ^ f), I(c ^ d ^ e ^ f), I(a ^ c ^ d ^ e ^ f), I(b ^ c ^ d ^ e ^ f), I(a ^ b ^ c ^ d ^ e ^ f), I(g), I(a ^ g), I(b ^ g), I(a ^ b ^ g), I(c ^ g), I(a ^ c ^ g), I(b ^ c ^ g), I(a ^ b ^ c ^ g), I(d ^ g), I(a ^ d ^ g), I(b ^ d ^ g), I(a ^ b ^ d ^ g), I(c ^ d ^ g), I(a ^ c ^ d ^ g), I(b ^ c ^ d ^ g), I(a ^ b ^ c ^ d ^ g), I(e ^ g), I(a ^ e ^ g), I(b ^ e ^ g), I(a ^ b ^ e ^ g), I(c ^ e ^ g), I(a ^ c ^ e ^ g), I(b ^ c ^ e ^ g), I(a ^ b ^ c ^ e ^ g), I(d ^ e ^ g), I(a ^ d ^ e ^ g), I(b ^ d ^ e ^ g), I(a ^ b ^ d ^ e ^ g), I(c ^ d ^ e ^ g), I(a ^ c ^ d ^ e ^ g), I(b ^ c ^ d ^ e ^ g), I(a ^ b ^ c ^ d ^ e ^ g), I(f ^ g), I(a ^ f ^ g), I(b^ f ^ g), I(a ^ b ^ f ^ g), I(c^ f ^ g), I(a ^ c ^ f ^ g), I(b ^ c ^ f ^ g), I(a ^ b ^ c ^ f ^ g), I(d ^ f ^ g), I(a ^ d ^ f ^ g), I(b ^ d ^ f ^ g), I(a ^ b ^ d ^ f ^ g), I(c ^ d ^ f ^ g), I(a ^ c ^ d ^ f ^ g), I(b ^ c ^ d ^ f ^ g), I(a ^ b ^ c ^ d ^ f ^ g), I(e ^ f ^ g), I(a ^ e ^ f ^ g), I(b ^ e ^ f ^ g), I(a ^ b ^ e ^ f ^ g), I(c ^ e ^ f ^ g), I(a ^ c ^ e ^ f ^ g), I(b ^ c ^ e ^ f ^ g), I(a ^ b ^ c ^ e ^ f ^ g), I(d ^ e ^ f ^ g), I(a ^ d ^ e ^ f ^ g), I(b ^ d ^ e ^ f ^ g), I(a ^ b ^ d ^ e ^ f ^ g), I(c ^ d ^ e ^ f ^ g), I(a ^ c ^ d ^ e ^ f ^ g), I(b ^ c ^ d ^ e ^ f ^ g), I(a ^ b ^ c ^ d ^ e ^ f ^ g)} {} + constexpr LinTrans(I a, I b, I c, I d, I e, I f, I g, I h) : table{I(0), I(a), I(b), I(a ^ b), I(c), I(a ^ c), I(b ^ c), I(a ^ b ^ c), I(d), I(a ^ d), I(b ^ d), I(a ^ b ^ d), I(c ^ d), I(a ^ c ^ d), I(b ^ c ^ d), I(a ^ b ^ c ^ d), I(e), I(a ^ e), I(b ^ e), I(a ^ b ^ e), I(c ^ e), I(a ^ c ^ e), I(b ^ c ^ e), I(a ^ b ^ c ^ e), I(d ^ e), I(a ^ d ^ e), I(b ^ d ^ e), I(a ^ b ^ d ^ e), I(c ^ d ^ e), I(a ^ c ^ d ^ e), I(b ^ c ^ d ^ e), I(a ^ b ^ c ^ d ^ e), I(f), I(a ^ f), I(b^ f), I(a ^ b ^ f), I(c^ f), I(a ^ c ^ f), I(b ^ c ^ f), I(a ^ b ^ c ^ f), I(d ^ f), I(a ^ d ^ f), I(b ^ d ^ f), I(a ^ b ^ d ^ f), I(c ^ d ^ f), I(a ^ c ^ d ^ f), I(b ^ c ^ d ^ f), I(a ^ b ^ c ^ d ^ f), I(e ^ f), I(a ^ e ^ f), I(b ^ e ^ f), I(a ^ b ^ e ^ f), I(c ^ e ^ f), I(a ^ c ^ e ^ f), I(b ^ c ^ e ^ f), I(a ^ b ^ c ^ e ^ f), I(d ^ e ^ f), I(a ^ d ^ e ^ f), I(b ^ d ^ e ^ f), I(a ^ b ^ d ^ e ^ f), I(c ^ d ^ e ^ f), I(a ^ c ^ d ^ e ^ f), I(b ^ c ^ d ^ e ^ f), I(a ^ b ^ c ^ d ^ e ^ f), I(g), I(a ^ g), I(b ^ g), I(a ^ b ^ g), I(c ^ g), I(a ^ c ^ g), I(b ^ c ^ g), I(a ^ b ^ c ^ g), I(d ^ g), I(a ^ d ^ g), I(b ^ d ^ g), I(a ^ b ^ d ^ g), I(c ^ d ^ g), I(a ^ c ^ d ^ g), I(b ^ c ^ d ^ g), I(a ^ b ^ c ^ d ^ g), I(e ^ g), I(a ^ e ^ g), I(b ^ e ^ g), I(a ^ b ^ e ^ g), I(c ^ e ^ g), I(a ^ c ^ e ^ g), I(b ^ c ^ e ^ g), I(a ^ b ^ c ^ e ^ g), I(d ^ e ^ g), I(a ^ d ^ e ^ g), I(b ^ d ^ e ^ g), I(a ^ b ^ d ^ e ^ g), I(c ^ d ^ e ^ g), I(a ^ c ^ d ^ e ^ g), I(b ^ c ^ d ^ e ^ g), I(a ^ b ^ c ^ d ^ e ^ g), I(f ^ g), I(a ^ f ^ g), I(b^ f ^ g), I(a ^ b ^ f ^ g), I(c^ f ^ g), I(a ^ c ^ f ^ g), I(b ^ c ^ f ^ g), I(a ^ b ^ c ^ f ^ g), I(d ^ f ^ g), I(a ^ d ^ f ^ g), I(b ^ d ^ f ^ g), I(a ^ b ^ d ^ f ^ g), I(c ^ d ^ f ^ g), I(a ^ c ^ d ^ f ^ g), I(b ^ c ^ d ^ f ^ g), I(a ^ b ^ c ^ d ^ f ^ g), I(e ^ f ^ g), I(a ^ e ^ f ^ g), I(b ^ e ^ f ^ g), I(a ^ b ^ e ^ f ^ g), I(c ^ e ^ f ^ g), I(a ^ c ^ e ^ f ^ g), I(b ^ c ^ e ^ f ^ g), I(a ^ b ^ c ^ e ^ f ^ g), I(d ^ e ^ f ^ g), I(a ^ d ^ e ^ f ^ g), I(b ^ d ^ e ^ f ^ g), I(a ^ b ^ d ^ e ^ f ^ g), I(c ^ d ^ e ^ f ^ g), I(a ^ c ^ d ^ e ^ f ^ g), I(b ^ c ^ d ^ e ^ f ^ g), I(a ^ b ^ c ^ d ^ e ^ f ^ g), I(h), I(a ^ h), I(b ^ h), I(a ^ b ^ h), I(c ^ h), I(a ^ c ^ h), I(b ^ c ^ h), I(a ^ b ^ c ^ h), I(d ^ h), I(a ^ d ^ h), I(b ^ d ^ h), I(a ^ b ^ d ^ h), I(c ^ d ^ h), I(a ^ c ^ d ^ h), I(b ^ c ^ d ^ h), I(a ^ b ^ c ^ d ^ h), I(e ^ h), I(a ^ e ^ h), I(b ^ e ^ h), I(a ^ b ^ e ^ h), I(c ^ e ^ h), I(a ^ c ^ e ^ h), I(b ^ c ^ e ^ h), I(a ^ b ^ c ^ e ^ h), I(d ^ e ^ h), I(a ^ d ^ e ^ h), I(b ^ d ^ e ^ h), I(a ^ b ^ d ^ e ^ h), I(c ^ d ^ e ^ h), I(a ^ c ^ d ^ e ^ h), I(b ^ c ^ d ^ e ^ h), I(a ^ b ^ c ^ d ^ e ^ h), I(f ^ h), I(a ^ f ^ h), I(b^ f ^ h), I(a ^ b ^ f ^ h), I(c^ f ^ h), I(a ^ c ^ f ^ h), I(b ^ c ^ f ^ h), I(a ^ b ^ c ^ f ^ h), I(d ^ f ^ h), I(a ^ d ^ f ^ h), I(b ^ d ^ f ^ h), I(a ^ b ^ d ^ f ^ h), I(c ^ d ^ f ^ h), I(a ^ c ^ d ^ f ^ h), I(b ^ c ^ d ^ f ^ h), I(a ^ b ^ c ^ d ^ f ^ h), I(e ^ f ^ h), I(a ^ e ^ f ^ h), I(b ^ e ^ f ^ h), I(a ^ b ^ e ^ f ^ h), I(c ^ e ^ f ^ h), I(a ^ c ^ e ^ f ^ h), I(b ^ c ^ e ^ f ^ h), I(a ^ b ^ c ^ e ^ f ^ h), I(d ^ e ^ f ^ h), I(a ^ d ^ e ^ f ^ h), I(b ^ d ^ e ^ f ^ h), I(a ^ b ^ d ^ e ^ f ^ h), I(c ^ d ^ e ^ f ^ h), I(a ^ c ^ d ^ e ^ f ^ h), I(b ^ c ^ d ^ e ^ f ^ h), I(a ^ b ^ c ^ d ^ e ^ f ^ h), I(g ^ h), I(a ^ g ^ h), I(b ^ g ^ h), I(a ^ b ^ g ^ h), I(c ^ g ^ h), I(a ^ c ^ g ^ h), I(b ^ c ^ g ^ h), I(a ^ b ^ c ^ g ^ h), I(d ^ g ^ h), I(a ^ d ^ g ^ h), I(b ^ d ^ g ^ h), I(a ^ b ^ d ^ g ^ h), I(c ^ d ^ g ^ h), I(a ^ c ^ d ^ g ^ h), I(b ^ c ^ d ^ g ^ h), I(a ^ b ^ c ^ d ^ g ^ h), I(e ^ g ^ h), I(a ^ e ^ g ^ h), I(b ^ e ^ g ^ h), I(a ^ b ^ e ^ g ^ h), I(c ^ e ^ g ^ h), I(a ^ c ^ e ^ g ^ h), I(b ^ c ^ e ^ g ^ h), I(a ^ b ^ c ^ e ^ g ^ h), I(d ^ e ^ g ^ h), I(a ^ d ^ e ^ g ^ h), I(b ^ d ^ e ^ g ^ h), I(a ^ b ^ d ^ e ^ g ^ h), I(c ^ d ^ e ^ g ^ h), I(a ^ c ^ d ^ e ^ g ^ h), I(b ^ c ^ d ^ e ^ g ^ h), I(a ^ b ^ c ^ d ^ e ^ g ^ h), I(f ^ g ^ h), I(a ^ f ^ g ^ h), I(b^ f ^ g ^ h), I(a ^ b ^ f ^ g ^ h), I(c^ f ^ g ^ h), I(a ^ c ^ f ^ g ^ h), I(b ^ c ^ f ^ g ^ h), I(a ^ b ^ c ^ f ^ g ^ h), I(d ^ f ^ g ^ h), I(a ^ d ^ f ^ g ^ h), I(b ^ d ^ f ^ g ^ h), I(a ^ b ^ d ^ f ^ g ^ h), I(c ^ d ^ f ^ g ^ h), I(a ^ c ^ d ^ f ^ g ^ h), I(b ^ c ^ d ^ f ^ g ^ h), I(a ^ b ^ c ^ d ^ f ^ g ^ h), I(e ^ f ^ g ^ h), I(a ^ e ^ f ^ g ^ h), I(b ^ e ^ f ^ g ^ h), I(a ^ b ^ e ^ f ^ g ^ h), I(c ^ e ^ f ^ g ^ h), I(a ^ c ^ e ^ f ^ g ^ h), I(b ^ c ^ e ^ f ^ g ^ h), I(a ^ b ^ c ^ e ^ f ^ g ^ h), I(d ^ e ^ f ^ g ^ h), I(a ^ d ^ e ^ f ^ g ^ h), I(b ^ d ^ e ^ f ^ g ^ h), I(a ^ b ^ d ^ e ^ f ^ g ^ h), I(c ^ d ^ e ^ f ^ g ^ h), I(a ^ c ^ d ^ e ^ f ^ g ^ h), I(b ^ c ^ d ^ e ^ f ^ g ^ h), I(a ^ b ^ c ^ d ^ e ^ f ^ g ^ h)} {} + + /* Construct a transformation over 3 to 8 bits, using a pointer to the bit's images. */ + constexpr LinTrans(const I* p, Num<2>) : LinTrans(I(p[0]), I(p[1])) {} + constexpr LinTrans(const I* p, Num<3>) : LinTrans(I(p[0]), I(p[1]), I(p[2])) {} + constexpr LinTrans(const I* p, Num<4>) : LinTrans(I(p[0]), I(p[1]), I(p[2]), I(p[3])) {} + constexpr LinTrans(const I* p, Num<5>) : LinTrans(I(p[0]), I(p[1]), I(p[2]), I(p[3]), I(p[4])) {} + constexpr LinTrans(const I* p, Num<6>) : LinTrans(I(p[0]), I(p[1]), I(p[2]), I(p[3]), I(p[4]), I(p[5])) {} + constexpr LinTrans(const I* p, Num<7>) : LinTrans(I(p[0]), I(p[1]), I(p[2]), I(p[3]), I(p[4]), I(p[5]), I(p[6])) {} + constexpr LinTrans(const I* p, Num<8>) : LinTrans(I(p[0]), I(p[1]), I(p[2]), I(p[3]), I(p[4]), I(p[5]), I(p[6]), I(p[7])) {} + + template + inline I Build(Num<1>, I a) + { + table[0] = I(); table[1] = a; + return a; + } + + template + inline I Build(Num<2>, I a) + { + I b = F(a); + table[0] = I(); table[1] = a; table[2] = b; table[3] = a ^ b; + return b; + } + + template + inline I Build(Num<3>, I a) + { + I b = F(a), c = F(b); + table[0] = I(); table[1] = a; table[2] = b; table[3] = a ^ b; table[4] = c; table[5] = a ^ c; table[6] = b ^ c; table[7] = a ^ b ^ c; + return c; + } + + template + inline I Build(Num<4>, I a) + { + I b = F(a), c = F(b), d = F(c); + table[0] = I(); table[1] = a; table[2] = b; table[3] = a ^ b; table[4] = c; table[5] = a ^ c; table[6] = b ^ c; table[7] = a ^ b ^ c; + table[8] = d; table[9] = a ^ d; table[10] = b ^ d; table[11] = a ^ b ^ d; table[12] = c ^ d; table[13] = a ^ c ^ d; table[14] = b ^ c ^ d; table[15] = a ^ b ^ c ^ d; + return d; + } + + template + inline I Build(Num<5>, I a) + { + I b = F(a), c = F(b), d = F(c), e = F(d); + table[0] = I(); table[1] = a; table[2] = b; table[3] = a ^ b; table[4] = c; table[5] = a ^ c; table[6] = b ^ c; table[7] = a ^ b ^ c; + table[8] = d; table[9] = a ^ d; table[10] = b ^ d; table[11] = a ^ b ^ d; table[12] = c ^ d; table[13] = a ^ c ^ d; table[14] = b ^ c ^ d; table[15] = a ^ b ^ c ^ d; + table[16] = e; table[17] = a ^ e; table[18] = b ^ e; table[19] = a ^ b ^ e; table[20] = c ^ e; table[21] = a ^ c ^ e; table[22] = b ^ c ^ e; table[23] = a ^ b ^ c ^ e; + table[24] = d ^ e; table[25] = a ^ d ^ e; table[26] = b ^ d ^ e; table[27] = a ^ b ^ d ^ e; table[28] = c ^ d ^ e; table[29] = a ^ c ^ d ^ e; table[30] = b ^ c ^ d ^ e; table[31] = a ^ b ^ c ^ d ^ e; + return e; + } + + template + inline I Build(Num<6>, I a) + { + I b = F(a), c = F(b), d = F(c), e = F(d), f = F(e); + table[0] = I(); table[1] = a; table[2] = b; table[3] = a ^ b; table[4] = c; table[5] = a ^ c; table[6] = b ^ c; table[7] = a ^ b ^ c; + table[8] = d; table[9] = a ^ d; table[10] = b ^ d; table[11] = a ^ b ^ d; table[12] = c ^ d; table[13] = a ^ c ^ d; table[14] = b ^ c ^ d; table[15] = a ^ b ^ c ^ d; + table[16] = e; table[17] = a ^ e; table[18] = b ^ e; table[19] = a ^ b ^ e; table[20] = c ^ e; table[21] = a ^ c ^ e; table[22] = b ^ c ^ e; table[23] = a ^ b ^ c ^ e; + table[24] = d ^ e; table[25] = a ^ d ^ e; table[26] = b ^ d ^ e; table[27] = a ^ b ^ d ^ e; table[28] = c ^ d ^ e; table[29] = a ^ c ^ d ^ e; table[30] = b ^ c ^ d ^ e; table[31] = a ^ b ^ c ^ d ^ e; + table[32] = f; table[33] = a ^ f; table[34] = b ^ f; table[35] = a ^ b ^ f; table[36] = c ^ f; table[37] = a ^ c ^ f; table[38] = b ^ c ^ f; table[39] = a ^ b ^ c ^ f; + table[40] = d ^ f; table[41] = a ^ d ^ f; table[42] = b ^ d ^ f; table[43] = a ^ b ^ d ^ f; table[44] = c ^ d ^ f; table[45] = a ^ c ^ d ^ f; table[46] = b ^ c ^ d ^ f; table[47] = a ^ b ^ c ^ d ^ f; + table[48] = e ^ f; table[49] = a ^ e ^ f; table[50] = b ^ e ^ f; table[51] = a ^ b ^ e ^ f; table[52] = c ^ e ^ f; table[53] = a ^ c ^ e ^ f; table[54] = b ^ c ^ e ^ f; table[55] = a ^ b ^ c ^ e ^ f; + table[56] = d ^ e ^ f; table[57] = a ^ d ^ e ^ f; table[58] = b ^ d ^ e ^ f; table[59] = a ^ b ^ d ^ e ^ f; table[60] = c ^ d ^ e ^ f; table[61] = a ^ c ^ d ^ e ^ f; table[62] = b ^ c ^ d ^ e ^ f; table[63] = a ^ b ^ c ^ d ^ e ^ f; + return f; + } + + template + inline I constexpr Map(I a) const { return table[O::template MidBits(a)]; } + + template + inline I constexpr TopMap(I a) const { static_assert(P + N == O::SIZE, "TopMap inconsistency"); return table[O::template TopBits(a)]; } +}; + + +/** A linear transformation constructed using LinTrans tables for sections of bits. */ +template class RecLinTrans; + +template class RecLinTrans { + LinTrans trans; +public: + static constexpr int BITS = N; + constexpr RecLinTrans(const I* p, Num) : trans(p, Num()) {} + constexpr RecLinTrans() = default; + constexpr RecLinTrans(const I (&init)[BITS]) : RecLinTrans(init, Num()) {} + + template + inline I constexpr Map(I a) const { return trans.template TopMap(a); } + + template + inline void Build(I a) { trans.template Build(Num(), a); } +}; + +template class RecLinTrans { + LinTrans trans; + RecLinTrans rec; +public: + static constexpr int BITS = RecLinTrans::BITS + N; + constexpr RecLinTrans(const I* p, Num) : trans(p, Num()), rec(p + N, Num()) {} + constexpr RecLinTrans() = default; + constexpr RecLinTrans(const I (&init)[BITS]) : RecLinTrans(init, Num()) {} + + template + inline I constexpr Map(I a) const { return trans.template Map(a) ^ rec.template Map(a); } + + template + inline void Build(I a) { I n = trans.template Build(Num(), a); rec.template Build(F(n)); } +}; + +/** The identity transformation. */ +class IdTrans { +public: + template + inline I constexpr Map(I a) const { return a; } +}; + +/** A singleton for the identity transformation. */ +constexpr IdTrans ID_TRANS{}; + +#endif diff --git a/src/minisketch.cpp b/src/minisketch.cpp new file mode 100644 index 0000000000..e9a322f139 --- /dev/null +++ b/src/minisketch.cpp @@ -0,0 +1,490 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#include + +#define MINISKETCH_BUILD +#ifdef _MINISKETCH_H_ +# error "minisketch.h cannot be included before minisketch.cpp" +#endif +#include "../include/minisketch.h" + +#include "false_positives.h" +#include "fielddefines.h" +#include "sketch.h" + +#ifdef HAVE_CLMUL +# ifdef _MSC_VER +# include +# else +# include +# endif +#endif + +Sketch* ConstructGeneric1Byte(int bits, int implementation); +Sketch* ConstructGeneric2Bytes(int bits, int implementation); +Sketch* ConstructGeneric3Bytes(int bits, int implementation); +Sketch* ConstructGeneric4Bytes(int bits, int implementation); +Sketch* ConstructGeneric5Bytes(int bits, int implementation); +Sketch* ConstructGeneric6Bytes(int bits, int implementation); +Sketch* ConstructGeneric7Bytes(int bits, int implementation); +Sketch* ConstructGeneric8Bytes(int bits, int implementation); + +#ifdef HAVE_CLMUL +Sketch* ConstructClMul1Byte(int bits, int implementation); +Sketch* ConstructClMul2Bytes(int bits, int implementation); +Sketch* ConstructClMul3Bytes(int bits, int implementation); +Sketch* ConstructClMul4Bytes(int bits, int implementation); +Sketch* ConstructClMul5Bytes(int bits, int implementation); +Sketch* ConstructClMul6Bytes(int bits, int implementation); +Sketch* ConstructClMul7Bytes(int bits, int implementation); +Sketch* ConstructClMul8Bytes(int bits, int implementation); +Sketch* ConstructClMulTri1Byte(int bits, int implementation); +Sketch* ConstructClMulTri2Bytes(int bits, int implementation); +Sketch* ConstructClMulTri3Bytes(int bits, int implementation); +Sketch* ConstructClMulTri4Bytes(int bits, int implementation); +Sketch* ConstructClMulTri5Bytes(int bits, int implementation); +Sketch* ConstructClMulTri6Bytes(int bits, int implementation); +Sketch* ConstructClMulTri7Bytes(int bits, int implementation); +Sketch* ConstructClMulTri8Bytes(int bits, int implementation); +#endif + +namespace { + +enum class FieldImpl { + GENERIC = 0, +#ifdef HAVE_CLMUL + CLMUL, + CLMUL_TRI, +#endif +}; + +static inline bool EnableClmul() +{ +#ifdef HAVE_CLMUL +#ifdef _MSC_VER + int regs[4]; + __cpuid(regs, 1); + return (regs[2] & 0x2); +#else + uint32_t eax, ebx, ecx, edx; + return (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & 0x2)); +#endif +#else + return false; +#endif +} + +Sketch* Construct(int bits, int impl) +{ + switch (FieldImpl(impl)) { + case FieldImpl::GENERIC: + switch ((bits + 7) / 8) { + case 1: + return ConstructGeneric1Byte(bits, impl); + case 2: + return ConstructGeneric2Bytes(bits, impl); + case 3: + return ConstructGeneric3Bytes(bits, impl); + case 4: + return ConstructGeneric4Bytes(bits, impl); + case 5: + return ConstructGeneric5Bytes(bits, impl); + case 6: + return ConstructGeneric6Bytes(bits, impl); + case 7: + return ConstructGeneric7Bytes(bits, impl); + case 8: + return ConstructGeneric8Bytes(bits, impl); + default: + return nullptr; + } + break; +#ifdef HAVE_CLMUL + case FieldImpl::CLMUL: + if (EnableClmul()) { + switch ((bits + 7) / 8) { + case 1: + return ConstructClMul1Byte(bits, impl); + case 2: + return ConstructClMul2Bytes(bits, impl); + case 3: + return ConstructClMul3Bytes(bits, impl); + case 4: + return ConstructClMul4Bytes(bits, impl); + case 5: + return ConstructClMul5Bytes(bits, impl); + case 6: + return ConstructClMul6Bytes(bits, impl); + case 7: + return ConstructClMul7Bytes(bits, impl); + case 8: + return ConstructClMul8Bytes(bits, impl); + default: + return nullptr; + } + } + break; + case FieldImpl::CLMUL_TRI: + if (EnableClmul()) { + switch ((bits + 7) / 8) { + case 1: + return ConstructClMulTri1Byte(bits, impl); + case 2: + return ConstructClMulTri2Bytes(bits, impl); + case 3: + return ConstructClMulTri3Bytes(bits, impl); + case 4: + return ConstructClMulTri4Bytes(bits, impl); + case 5: + return ConstructClMulTri5Bytes(bits, impl); + case 6: + return ConstructClMulTri6Bytes(bits, impl); + case 7: + return ConstructClMulTri7Bytes(bits, impl); + case 8: + return ConstructClMulTri8Bytes(bits, impl); + default: + return nullptr; + } + } + break; +#endif + } + return nullptr; +} + +} + +extern "C" { + +int minisketch_bits_supported(uint32_t bits) { +#ifdef ENABLE_FIELD_INT_2 + if (bits == 2) return true; +#endif +#ifdef ENABLE_FIELD_INT_3 + if (bits == 3) return true; +#endif +#ifdef ENABLE_FIELD_INT_4 + if (bits == 4) return true; +#endif +#ifdef ENABLE_FIELD_INT_5 + if (bits == 5) return true; +#endif +#ifdef ENABLE_FIELD_INT_6 + if (bits == 6) return true; +#endif +#ifdef ENABLE_FIELD_INT_7 + if (bits == 7) return true; +#endif +#ifdef ENABLE_FIELD_INT_8 + if (bits == 8) return true; +#endif +#ifdef ENABLE_FIELD_INT_9 + if (bits == 9) return true; +#endif +#ifdef ENABLE_FIELD_INT_10 + if (bits == 10) return true; +#endif +#ifdef ENABLE_FIELD_INT_11 + if (bits == 11) return true; +#endif +#ifdef ENABLE_FIELD_INT_12 + if (bits == 12) return true; +#endif +#ifdef ENABLE_FIELD_INT_13 + if (bits == 13) return true; +#endif +#ifdef ENABLE_FIELD_INT_14 + if (bits == 14) return true; +#endif +#ifdef ENABLE_FIELD_INT_15 + if (bits == 15) return true; +#endif +#ifdef ENABLE_FIELD_INT_16 + if (bits == 16) return true; +#endif +#ifdef ENABLE_FIELD_INT_17 + if (bits == 17) return true; +#endif +#ifdef ENABLE_FIELD_INT_18 + if (bits == 18) return true; +#endif +#ifdef ENABLE_FIELD_INT_19 + if (bits == 19) return true; +#endif +#ifdef ENABLE_FIELD_INT_20 + if (bits == 20) return true; +#endif +#ifdef ENABLE_FIELD_INT_21 + if (bits == 21) return true; +#endif +#ifdef ENABLE_FIELD_INT_22 + if (bits == 22) return true; +#endif +#ifdef ENABLE_FIELD_INT_23 + if (bits == 23) return true; +#endif +#ifdef ENABLE_FIELD_INT_24 + if (bits == 24) return true; +#endif +#ifdef ENABLE_FIELD_INT_25 + if (bits == 25) return true; +#endif +#ifdef ENABLE_FIELD_INT_26 + if (bits == 26) return true; +#endif +#ifdef ENABLE_FIELD_INT_27 + if (bits == 27) return true; +#endif +#ifdef ENABLE_FIELD_INT_28 + if (bits == 28) return true; +#endif +#ifdef ENABLE_FIELD_INT_29 + if (bits == 29) return true; +#endif +#ifdef ENABLE_FIELD_INT_30 + if (bits == 30) return true; +#endif +#ifdef ENABLE_FIELD_INT_31 + if (bits == 31) return true; +#endif +#ifdef ENABLE_FIELD_INT_32 + if (bits == 32) return true; +#endif +#ifdef ENABLE_FIELD_INT_33 + if (bits == 33) return true; +#endif +#ifdef ENABLE_FIELD_INT_34 + if (bits == 34) return true; +#endif +#ifdef ENABLE_FIELD_INT_35 + if (bits == 35) return true; +#endif +#ifdef ENABLE_FIELD_INT_36 + if (bits == 36) return true; +#endif +#ifdef ENABLE_FIELD_INT_37 + if (bits == 37) return true; +#endif +#ifdef ENABLE_FIELD_INT_38 + if (bits == 38) return true; +#endif +#ifdef ENABLE_FIELD_INT_39 + if (bits == 39) return true; +#endif +#ifdef ENABLE_FIELD_INT_40 + if (bits == 40) return true; +#endif +#ifdef ENABLE_FIELD_INT_41 + if (bits == 41) return true; +#endif +#ifdef ENABLE_FIELD_INT_42 + if (bits == 42) return true; +#endif +#ifdef ENABLE_FIELD_INT_43 + if (bits == 43) return true; +#endif +#ifdef ENABLE_FIELD_INT_44 + if (bits == 44) return true; +#endif +#ifdef ENABLE_FIELD_INT_45 + if (bits == 45) return true; +#endif +#ifdef ENABLE_FIELD_INT_46 + if (bits == 46) return true; +#endif +#ifdef ENABLE_FIELD_INT_47 + if (bits == 47) return true; +#endif +#ifdef ENABLE_FIELD_INT_48 + if (bits == 48) return true; +#endif +#ifdef ENABLE_FIELD_INT_49 + if (bits == 49) return true; +#endif +#ifdef ENABLE_FIELD_INT_50 + if (bits == 50) return true; +#endif +#ifdef ENABLE_FIELD_INT_51 + if (bits == 51) return true; +#endif +#ifdef ENABLE_FIELD_INT_52 + if (bits == 52) return true; +#endif +#ifdef ENABLE_FIELD_INT_53 + if (bits == 53) return true; +#endif +#ifdef ENABLE_FIELD_INT_54 + if (bits == 54) return true; +#endif +#ifdef ENABLE_FIELD_INT_55 + if (bits == 55) return true; +#endif +#ifdef ENABLE_FIELD_INT_56 + if (bits == 56) return true; +#endif +#ifdef ENABLE_FIELD_INT_57 + if (bits == 57) return true; +#endif +#ifdef ENABLE_FIELD_INT_58 + if (bits == 58) return true; +#endif +#ifdef ENABLE_FIELD_INT_59 + if (bits == 59) return true; +#endif +#ifdef ENABLE_FIELD_INT_60 + if (bits == 60) return true; +#endif +#ifdef ENABLE_FIELD_INT_61 + if (bits == 61) return true; +#endif +#ifdef ENABLE_FIELD_INT_62 + if (bits == 62) return true; +#endif +#ifdef ENABLE_FIELD_INT_63 + if (bits == 63) return true; +#endif +#ifdef ENABLE_FIELD_INT_64 + if (bits == 64) return true; +#endif + return false; +} + +uint32_t minisketch_implementation_max() { + uint32_t ret = 0; +#ifdef HAVE_CLMUL + ret += 2; +#endif + return ret; +} + +int minisketch_implementation_supported(uint32_t bits, uint32_t implementation) { + if (!minisketch_bits_supported(bits) || implementation > minisketch_implementation_max()) { + return 0; + } + try { + Sketch* sketch = Construct(bits, implementation); + if (sketch) { + delete sketch; + return 1; + } + } catch (const std::bad_alloc&) {} + return 0; +} + +minisketch* minisketch_create(uint32_t bits, uint32_t implementation, size_t capacity) { + try { + Sketch* sketch = Construct(bits, implementation); + if (sketch) { + try { + sketch->Init(capacity); + } catch (const std::bad_alloc&) { + delete sketch; + throw; + } + sketch->Ready(); + } + return (minisketch*)sketch; + } catch (const std::bad_alloc&) { + return nullptr; + } +} + +uint32_t minisketch_bits(const minisketch* sketch) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + return s->Bits(); +} + +size_t minisketch_capacity(const minisketch* sketch) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + return s->Syndromes(); +} + +uint32_t minisketch_implementation(const minisketch* sketch) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + return s->Implementation(); +} + +minisketch* minisketch_clone(const minisketch* sketch) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + Sketch* r = (Sketch*) minisketch_create(s->Bits(), s->Implementation(), s->Syndromes()); + if (r) { + r->Merge(s); + } + return (minisketch*) r; +} + +void minisketch_destroy(minisketch* sketch) { + if (sketch) { + Sketch* s = (Sketch*)sketch; + s->UnReady(); + delete s; + } +} + +size_t minisketch_serialized_size(const minisketch* sketch) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + size_t bits = s->Bits(); + size_t syndromes = s->Syndromes(); + return (bits * syndromes + 7) / 8; +} + +void minisketch_serialize(const minisketch* sketch, unsigned char* output) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + s->Serialize(output); +} + +void minisketch_deserialize(minisketch* sketch, const unsigned char* input) { + Sketch* s = (Sketch*)sketch; + s->Check(); + s->Deserialize(input); +} + +void minisketch_add_uint64(minisketch* sketch, uint64_t element) { + Sketch* s = (Sketch*)sketch; + s->Check(); + s->Add(element); +} + +size_t minisketch_merge(minisketch* sketch, const minisketch* other_sketch) { + Sketch* s1 = (Sketch*)sketch; + const Sketch* s2 = (const Sketch*)other_sketch; + s1->Check(); + s2->Check(); + if (s1->Bits() != s2->Bits()) return 0; + if (s1->Implementation() != s2->Implementation()) return 0; + return s1->Merge(s2); +} + +ssize_t minisketch_decode(const minisketch* sketch, size_t max_elements, uint64_t* output) { + const Sketch* s = (const Sketch*)sketch; + s->Check(); + return s->Decode(max_elements, output); +} + +void minisketch_set_seed(minisketch* sketch, uint64_t seed) { + Sketch* s = (Sketch*)sketch; + s->Check(); + s->SetSeed(seed); +} + +size_t minisketch_compute_capacity(uint32_t bits, size_t max_elements, uint32_t fpbits) { + return ComputeCapacity(bits, max_elements, fpbits); +} + +size_t minisketch_compute_max_elements(uint32_t bits, size_t capacity, uint32_t fpbits) { + return ComputeMaxElements(bits, capacity, fpbits); +} + +} diff --git a/src/sketch.h b/src/sketch.h new file mode 100644 index 0000000000..3e9bad793d --- /dev/null +++ b/src/sketch.h @@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_STATE_H_ +#define _MINISKETCH_STATE_H_ + +#include +#include + +/** Abstract class for internal representation of a minisketch object. */ +class Sketch +{ + uint64_t m_canary; + const int m_implementation; + const int m_bits; + +public: + Sketch(int implementation, int bits) : m_implementation(implementation), m_bits(bits) {} + + void Ready() { m_canary = 0x6d496e536b65LU; } + void Check() const { if (m_canary != 0x6d496e536b65LU) abort(); } + void UnReady() { m_canary = 1; } + int Implementation() const { return m_implementation; } + int Bits() const { return m_bits; } + + virtual ~Sketch() {} + virtual size_t Syndromes() const = 0; + + virtual void Init(int syndromes) = 0; + virtual void Add(uint64_t element) = 0; + virtual void Serialize(unsigned char*) const = 0; + virtual void Deserialize(const unsigned char*) = 0; + virtual size_t Merge(const Sketch* other_sketch) = 0; + virtual void SetSeed(uint64_t seed) = 0; + + virtual int Decode(int max_count, uint64_t* roots) const = 0; +}; + +#endif diff --git a/src/sketch_impl.h b/src/sketch_impl.h new file mode 100644 index 0000000000..4547b742f2 --- /dev/null +++ b/src/sketch_impl.h @@ -0,0 +1,433 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_SKETCH_IMPL_H_ +#define _MINISKETCH_SKETCH_IMPL_H_ + +#include + +#include "util.h" +#include "sketch.h" +#include "int_utils.h" + +/** Compute the remainder of a polynomial division of val by mod, putting the result in mod. */ +template +void PolyMod(const std::vector& mod, std::vector& val, const F& field) { + size_t modsize = mod.size(); + CHECK_SAFE(modsize > 0 && mod.back() == 1); + if (val.size() < modsize) return; + CHECK_SAFE(val.back() != 0); + while (val.size() >= modsize) { + auto term = val.back(); + val.pop_back(); + if (term != 0) { + typename F::Multiplier mul(field, term); + for (size_t x = 0; x < mod.size() - 1; ++x) { + val[val.size() - modsize + 1 + x] ^= mul(mod[x]); + } + } + } + while (val.size() > 0 && val.back() == 0) val.pop_back(); +} + +/** Compute the quotient of a polynomial division of val by mod, putting the quotient in div and the remainder in val. */ +template +void DivMod(const std::vector& mod, std::vector& val, std::vector& div, const F& field) { + size_t modsize = mod.size(); + CHECK_SAFE(mod.size() > 0 && mod.back() == 1); + if (val.size() < mod.size()) { + div.clear(); + return; + } + CHECK_SAFE(val.back() != 0); + div.resize(val.size() - mod.size() + 1); + while (val.size() >= modsize) { + auto term = val.back(); + div[val.size() - modsize] = term; + val.pop_back(); + if (term != 0) { + typename F::Multiplier mul(field, term); + for (size_t x = 0; x < mod.size() - 1; ++x) { + val[val.size() - modsize + 1 + x] ^= mul(mod[x]); + } + } + } +} + +/** Make a polynomial monic. */ +template +typename F::Elem MakeMonic(std::vector& a, const F& field) { + CHECK_SAFE(a.back() != 0); + if (a.back() == 1) return 0; + auto inv = field.Inv(a.back()); + typename F::Multiplier mul(field, inv); + a.back() = 1; + for (size_t i = 0; i < a.size() - 1; ++i) { + a[i] = mul(a[i]); + } + return inv; +} + +/** Compute the GCD of two polynomials, putting the result in a. b will be cleared. */ +template +void GCD(std::vector& a, std::vector& b, const F& field) { + if (a.size() < b.size()) std::swap(a, b); + while (b.size() > 0) { + if (b.size() == 1) { + a.resize(1); + a[0] = 1; + return; + } + MakeMonic(b, field); + PolyMod(b, a, field); + std::swap(a, b); + } +} + +/** Square a polynomial. */ +template +void Sqr(std::vector& poly, const F& field) { + if (poly.size() == 0) return; + poly.resize(poly.size() * 2 - 1); + for (int x = poly.size() - 1; x >= 0; --x) { + poly[x] = (x & 1) ? 0 : field.Sqr(poly[x / 2]); + } +} + +/** Compute the trace map of (param*x) modulo mod, putting the result in out. */ +template +void TraceMod(const std::vector& mod, std::vector& out, const typename F::Elem& param, const F& field) { + out.reserve(mod.size() * 2); + out.resize(2); + out[0] = 0; + out[1] = param; + + for (int i = 0; i < field.Bits() - 1; ++i) { + Sqr(out, field); + if (out.size() < 2) out.resize(2); + out[1] = param; + PolyMod(mod, out, field); + } +} + +/** One step of the root finding algorithm; finds roots of stack[pos] and adds them to roots. Stack elements >= pos are destroyed. + * + * It operates on a stack of polynomials. The polynomial operated on is `stack[pos]`, where elements of `stack` with index higher + * than `pos` are used as scratch space. + * + * `stack[pos]` is assumed to be square-free polynomial. If `fully_factorizable` is true, it is also assumed to have no irreducible + * factors of degree higher than 1. + + * This implements the Berlekamp trace algorithm, plus an efficient test to fail fast in + * case the polynomial cannot be fully factored. + */ +template +bool RecFindRoots(std::vector>& stack, size_t pos, std::vector& roots, bool fully_factorizable, int depth, typename F::Elem randv, const F& field) { + auto& ppoly = stack[pos]; + // We assert ppoly.size() > 1 (instead of just ppoly.size() > 0) to additionally exclude + // constants polynomials because + // - ppoly is not constant initially (this is ensured by FindRoots()), and + // - we never recurse on a constant polynomial. + CHECK_SAFE(ppoly.size() > 1 && ppoly.back() == 1); + /* 1st degree input: constant term is the root. */ + if (ppoly.size() == 2) { + roots.push_back(ppoly[0]); + return true; + } + /* 2nd degree input: use direct quadratic solver. */ + if (ppoly.size() == 3) { + CHECK_RETURN(ppoly[1] != 0, false); // Equations of the form (x^2 + a) have two identical solutions; contradicts square-free assumption. */ + auto input = field.Mul(ppoly[0], field.Sqr(field.Inv(ppoly[1]))); + auto root = field.Qrt(input); + if ((field.Sqr(root) ^ root) != input) { + CHECK_SAFE(!fully_factorizable); + return false; // No root found. + } + auto sol = field.Mul(root, ppoly[1]); + roots.push_back(sol); + roots.push_back(sol ^ ppoly[1]); + return true; + } + /* 3rd degree input and more: recurse further. */ + if (pos + 3 > stack.size()) { + // Allocate memory if necessary. + stack.resize((pos + 3) * 2); + } + auto& poly = stack[pos]; + auto& tmp = stack[pos + 1]; + auto& trace = stack[pos + 2]; + trace.clear(); + tmp.clear(); + for (int iter = 0;; ++iter) { + // Compute the polynomial (trace(x*randv) mod poly(x)) symbolically, + // and put the result in `trace`. + TraceMod(poly, trace, randv, field); + + if (iter >= 1 && !fully_factorizable) { + // If the polynomial cannot be factorized completely (it has an + // irreducible factor of degree higher than 1), we want to avoid + // the case where this is only detected after trying all BITS + // independent split attempts fail (see the assert below). + // + // Observe that if we call y = randv*x, it is true that: + // + // trace = y + y^2 + y^4 + y^8 + ... y^(FIELDSIZE/2) mod poly + // + // Due to the Frobenius endomorphism, this means: + // + // trace^2 = y^2 + y^4 + y^8 + ... + y^FIELDSIZE mod poly + // + // Or, adding them up: + // + // trace + trace^2 = y + y^FIELDSIZE mod poly. + // = randv*x + randv^FIELDSIZE*x^FIELDSIZE + // = randv*x + randv*x^FIELDSIZE + // = randv*(x + x^FIELDSIZE). + // (all mod poly) + // + // x + x^FIELDSIZE is the polynomial which has every field element + // as root once. Whenever x + x^FIELDSIZE is multiple of poly, + // this means it only has unique first degree factors. The same + // holds for its constant multiple randv*(x + x^FIELDSIZE) = + // trace + trace^2. + // + // We use this test to quickly verify whether the polynomial is + // fully factorizable after already having computed a trace. + // We don't invoke it immediately; only when splitting has failed + // at least once, which avoids it for most polynomials that are + // fully factorizable (or at least pushes the test down the + // recursion to factors which are smaller and thus faster). + tmp = trace; + Sqr(tmp, field); + for (size_t i = 0; i < trace.size(); ++i) { + tmp[i] ^= trace[i]; + } + while (tmp.size() && tmp.back() == 0) tmp.pop_back(); + PolyMod(poly, tmp, field); + + // Whenever the test fails, we can immediately abort the root + // finding. Whenever it succeeds, we can remember and pass down + // the information that it is in fact fully factorizable, avoiding + // the need to run the test again. + if (tmp.size() != 0) return false; + fully_factorizable = true; + } + + if (fully_factorizable) { + // Every succesful iteration of this algorithm splits the input + // polynomial further into buckets, each corresponding to a subset + // of 2^(BITS-depth) roots. If after depth splits the degree of + // the polynomial is >= 2^(BITS-depth), something is wrong. + CHECK_RETURN(field.Bits() - depth >= std::numeric_limits::digits || + (poly.size() - 2) >> (field.Bits() - depth) == 0, false); + } + + depth++; + // In every iteration we multiply randv by 2. As a result, the set + // of randv values forms a GF(2)-linearly independent basis of splits. + randv = field.Mul2(randv); + tmp = poly; + GCD(trace, tmp, field); + if (trace.size() != poly.size() && trace.size() > 1) break; + } + MakeMonic(trace, field); + DivMod(trace, poly, tmp, field); + // At this point, the stack looks like [... (poly) tmp trace], and we want to recursively + // find roots of trace and tmp (= poly/trace). As we don't care about poly anymore, move + // trace into its position first. + std::swap(poly, trace); + // Now the stack is [... (trace) tmp ...]. First we factor tmp (at pos = pos+1), and then + // we factor trace (at pos = pos). + if (!RecFindRoots(stack, pos + 1, roots, fully_factorizable, depth, randv, field)) return false; + // The stack position pos contains trace, the polynomial with all of poly's roots which (after + // multiplication with randv) have trace 0. This is never the case for irreducible factors + // (which always end up in tmp), so we can set fully_factorizable to true when recursing. + bool ret = RecFindRoots(stack, pos, roots, true, depth, randv, field); + // Because of the above, recursion can never fail here. + CHECK_SAFE(ret); + return ret; +} + +/** Returns the roots of a fully factorizable polynomial + * + * This function assumes that the input polynomial is square-free + * and not the zero polynomial (represented by an empty vector). + * + * In case the square-free polynomial is not fully factorizable, i.e., it + * has fewer roots than its degree, the empty vector is returned. + */ +template +std::vector FindRoots(const std::vector& poly, typename F::Elem basis, const F& field) { + std::vector roots; + CHECK_RETURN(poly.size() != 0, {}); + CHECK_RETURN(basis != 0, {}); + if (poly.size() == 1) return roots; // No roots when the polynomial is a constant. + roots.reserve(poly.size() - 1); + std::vector> stack = {poly}; + + // Invoke the recursive factorization algorithm. + if (!RecFindRoots(stack, 0, roots, false, 0, basis, field)) { + // Not fully factorizable. + return {}; + } + CHECK_RETURN(poly.size() - 1 == roots.size(), {}); + return roots; +} + +template +std::vector BerlekampMassey(const std::vector& syndromes, size_t max_degree, const F& field) { + std::vector table; + std::vector current, prev, tmp; + current.reserve(syndromes.size() / 2 + 1); + prev.reserve(syndromes.size() / 2 + 1); + tmp.reserve(syndromes.size() / 2 + 1); + current.resize(1); + current[0] = 1; + prev.resize(1); + prev[0] = 1; + typename F::Elem b = 1, b_inv = 1; + bool b_have_inv = true; + table.reserve(syndromes.size()); + + for (size_t n = 0; n != syndromes.size(); ++n) { + table.emplace_back(field, syndromes[n]); + auto discrepancy = syndromes[n]; + for (size_t i = 1; i < current.size(); ++i) discrepancy ^= table[n - i](current[i]); + if (discrepancy != 0) { + int x = n + 1 - (current.size() - 1) - (prev.size() - 1); + if (!b_have_inv) { + b_inv = field.Inv(b); + b_have_inv = true; + } + bool swap = 2 * (current.size() - 1) <= n; + if (swap) { + if (prev.size() + x - 1 > max_degree) return {}; // We'd exceed maximum degree + tmp = current; + current.resize(prev.size() + x); + } + typename F::Multiplier mul(field, field.Mul(discrepancy, b_inv)); + for (size_t i = 0; i < prev.size(); ++i) current[i + x] ^= mul(prev[i]); + if (swap) { + std::swap(prev, tmp); + b = discrepancy; + b_have_inv = false; + } + } + } + CHECK_RETURN(current.size() && current.back() != 0, {}); + return current; +} + +template +std::vector ReconstructAllSyndromes(const std::vector& odd_syndromes, const F& field) { + std::vector all_syndromes; + all_syndromes.resize(odd_syndromes.size() * 2); + for (size_t i = 0; i < odd_syndromes.size(); ++i) { + all_syndromes[i * 2] = odd_syndromes[i]; + all_syndromes[i * 2 + 1] = field.Sqr(all_syndromes[i]); + } + return all_syndromes; +} + +template +void AddToOddSyndromes(std::vector& osyndromes, typename F::Elem data, const F& field) { + auto sqr = field.Sqr(data); + typename F::Multiplier mul(field, sqr); + for (auto& osyndrome : osyndromes) { + osyndrome ^= data; + data = mul(data); + } +} + +template +std::vector FullDecode(const std::vector& osyndromes, const F& field) { + auto asyndromes = ReconstructAllSyndromes(osyndromes, field); + auto poly = BerlekampMassey(asyndromes, field); + std::reverse(poly.begin(), poly.end()); + return FindRoots(poly, field); +} + +template +class SketchImpl final : public Sketch +{ + const F m_field; + std::vector m_syndromes; + typename F::Elem m_basis; + +public: + template + SketchImpl(int implementation, int bits, const Args&... args) : Sketch(implementation, bits), m_field(args...) { + std::random_device rng; + std::uniform_int_distribution dist; + m_basis = m_field.FromSeed(dist(rng)); + } + + size_t Syndromes() const override { return m_syndromes.size(); } + void Init(int count) override { m_syndromes.assign(count, 0); } + + void Add(uint64_t val) override + { + auto elem = m_field.FromUint64(val); + AddToOddSyndromes(m_syndromes, elem, m_field); + } + + void Serialize(unsigned char* ptr) const override + { + BitWriter writer(ptr); + for (const auto& val : m_syndromes) { + m_field.Serialize(writer, val); + } + writer.Flush(); + } + + void Deserialize(const unsigned char* ptr) override + { + BitReader reader(ptr); + for (auto& val : m_syndromes) { + val = m_field.Deserialize(reader); + } + } + + int Decode(int max_count, uint64_t* out) const override + { + auto all_syndromes = ReconstructAllSyndromes(m_syndromes, m_field); + auto poly = BerlekampMassey(all_syndromes, max_count, m_field); + if (poly.size() == 0) return -1; + if (poly.size() == 1) return 0; + if ((int)poly.size() > 1 + max_count) return -1; + std::reverse(poly.begin(), poly.end()); + auto roots = FindRoots(poly, m_basis, m_field); + if (roots.size() == 0) return -1; + + for (const auto& root : roots) { + *(out++) = m_field.ToUint64(root); + } + return roots.size(); + } + + size_t Merge(const Sketch* other_sketch) override + { + // Sad cast. This is safe only because the caller code in minisketch.cpp checks + // that implementation and field size match. + const SketchImpl* other = static_cast(other_sketch); + m_syndromes.resize(std::min(m_syndromes.size(), other->m_syndromes.size())); + for (size_t i = 0; i < m_syndromes.size(); ++i) { + m_syndromes[i] ^= other->m_syndromes[i]; + } + return m_syndromes.size(); + } + + void SetSeed(uint64_t seed) override + { + if (seed == (uint64_t)-1) { + m_basis = 1; + } else { + m_basis = m_field.FromSeed(seed); + } + } +}; + +#endif diff --git a/src/test.cpp b/src/test.cpp new file mode 100644 index 0000000000..417937ea5f --- /dev/null +++ b/src/test.cpp @@ -0,0 +1,316 @@ +/********************************************************************** + * Copyright (c) 2018,2021 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "../include/minisketch.h" +#include "util.h" + +namespace { + +uint64_t Combination(uint64_t n, uint64_t k) { + if (n - k < k) k = n - k; + uint64_t ret = 1; + for (uint64_t i = 1; i <= k; ++i) { + ret = (ret * n) / i; + --n; + } + return ret; +} + +/** Create a vector with Minisketch objects, one for each implementation. */ +std::vector CreateSketches(uint32_t bits, size_t capacity) { + if (!Minisketch::BitsSupported(bits)) return {}; + std::vector ret; + for (uint32_t impl = 0; impl <= Minisketch::MaxImplementation(); ++impl) { + if (Minisketch::ImplementationSupported(bits, impl)) { + CHECK(Minisketch::BitsSupported(bits)); + ret.push_back(Minisketch(bits, impl, capacity)); + CHECK((bool)ret.back()); + } else { + // implementation 0 must always work unless field size is disabled + CHECK(impl != 0 || !Minisketch::BitsSupported(bits)); + } + } + return ret; +} + +/** Test properties by exhaustively decoding all 2**(bits*capacity) sketches + * with specified capacity and bits. */ +void TestExhaustive(uint32_t bits, size_t capacity) { + auto sketches = CreateSketches(bits, capacity); + if (sketches.empty()) return; + auto sketches_rebuild = CreateSketches(bits, capacity); + + std::vector serialized; + std::vector serialized_empty; + std::vector counts; //!< counts[i] = number of results with i elements + std::vector elements_0; //!< Result vector for elements for impl=0 + std::vector elements_other; //!< Result vector for elements for other impls + std::vector elements_too_small; //!< Result vector that's too small + + counts.resize(capacity + 1); + serialized.resize(sketches[0].GetSerializedSize()); + serialized_empty.resize(sketches[0].GetSerializedSize()); + + // Iterate over all (bits)-bit sketches with (capacity) syndromes. + for (uint64_t x = 0; (x >> (bits * capacity)) == 0; ++x) { + // Construct the serialization. + for (size_t i = 0; i < serialized.size(); ++i) { + serialized[i] = (x >> (i * 8)) & 0xFF; + } + + // Compute all the solutions + sketches[0].Deserialize(serialized); + elements_0.resize(64); + bool decodable_0 = sketches[0].Decode(elements_0); + std::sort(elements_0.begin(), elements_0.end()); + + // Verify that decoding with other implementations agrees. + for (size_t impl = 1; impl < sketches.size(); ++impl) { + sketches[impl].Deserialize(serialized); + elements_other.resize(64); + bool decodable_other = sketches[impl].Decode(elements_other); + CHECK(decodable_other == decodable_0); + std::sort(elements_other.begin(), elements_other.end()); + CHECK(elements_other == elements_0); + } + + // If there are solutions: + if (decodable_0) { + if (!elements_0.empty()) { + // Decoding with limit one less than the number of elements should fail. + elements_too_small.resize(elements_0.size() - 1); + for (size_t impl = 0; impl < sketches.size(); ++impl) { + CHECK(!sketches[impl].Decode(elements_too_small)); + } + } + + // Reconstruct the sketch from the solutions. + for (size_t impl = 0; impl < sketches.size(); ++impl) { + // Clear the sketch. + sketches_rebuild[impl].Deserialize(serialized_empty); + // Load all decoded elements into it. + for (uint64_t elem : elements_0) { + CHECK(elem != 0); + CHECK(elem >> bits == 0); + sketches_rebuild[impl].Add(elem); + } + // Reserialize the result + auto serialized_rebuild = sketches_rebuild[impl].Serialize(); + // Compare + CHECK(serialized == serialized_rebuild); + // Count it + if (impl == 0 && elements_0.size() <= capacity) ++counts[elements_0.size()]; + } + } + } + + // Verify that the number of decodable sketches with given elements is expected. + uint64_t mask = bits == 64 ? UINT64_MAX : (uint64_t{1} << bits) - 1; + for (uint64_t i = 0; i <= capacity && (i & mask) == i; ++i) { + CHECK(counts[i] == Combination(mask, i)); + } +} + +/** Test properties of sketches with random elements put in. */ +void TestRandomized(uint32_t bits, size_t max_capacity, size_t iter) { + std::random_device rnd; + std::uniform_int_distribution capacity_dist(0, std::min(std::numeric_limits::max() >> (64 - bits), max_capacity)); + std::uniform_int_distribution element_dist(1, std::numeric_limits::max() >> (64 - bits)); + std::uniform_int_distribution rand64(0, std::numeric_limits::max()); + std::uniform_int_distribution size_offset_dist(-3, 3); + + std::vector decode_0; + std::vector decode_other; + std::vector decode_temp; + std::vector elements; + + for (size_t i = 0; i < iter; ++i) { + // Determine capacity, and construct Minisketch objects for all implementations. + uint64_t capacity = capacity_dist(rnd); + auto sketches = CreateSketches(bits, capacity); + // Sanity checks + if (sketches.empty()) return; + for (size_t impl = 0; impl < sketches.size(); ++impl) { + CHECK(sketches[impl].GetBits() == bits); + CHECK(sketches[impl].GetCapacity() == capacity); + CHECK(sketches[impl].GetSerializedSize() == sketches[0].GetSerializedSize()); + } + // Determine the number of elements, and create a vector to store them in. + size_t element_count = std::max(0, std::max(0, capacity + size_offset_dist(rnd))); + elements.resize(element_count); + // Add the elements to all sketches + for (size_t j = 0; j < element_count; ++j) { + uint64_t elem = element_dist(rnd); + CHECK(elem != 0); + elements[j] = elem; + for (auto& sketch : sketches) sketch.Add(elem); + } + // Remove pairs of duplicates in elements, as they cancel out. + std::sort(elements.begin(), elements.end()); + size_t real_element_count = element_count; + for (size_t pos = 0; pos + 1 < elements.size(); ++pos) { + if (elements[pos] == elements[pos + 1]) { + real_element_count -= 2; + // Set both elements to 0; afterwards we will move these to the end. + elements[pos] = 0; + elements[pos + 1] = 0; + ++pos; + } + } + if (real_element_count < element_count) { + // Move all introduced zeroes (masking duplicates) to the end. + std::sort(elements.begin(), elements.end(), [](uint64_t a, uint64_t b) { return a != b && (b == 0 || (a != 0 && a < b)); }); + CHECK(elements[real_element_count] == 0); + elements.resize(real_element_count); + } + // Create and compare serializations + auto serialized_0 = sketches[0].Serialize(); + for (size_t impl = 1; impl < sketches.size(); ++impl) { + auto serialized_other = sketches[impl].Serialize(); + CHECK(serialized_other == serialized_0); + } + // Deserialize and reserialize them + for (size_t impl = 0; impl < sketches.size(); ++impl) { + sketches[impl].Deserialize(serialized_0); + auto reserialized = sketches[impl].Serialize(); + CHECK(reserialized == serialized_0); + } + // Decode with limit set to the capacity, and compare results + decode_0.resize(capacity); + bool decodable_0 = sketches[0].Decode(decode_0); + std::sort(decode_0.begin(), decode_0.end()); + for (size_t impl = 1; impl < sketches.size(); ++impl) { + decode_other.resize(capacity); + bool decodable_other = sketches[impl].Decode(decode_other); + CHECK(decodable_other == decodable_0); + std::sort(decode_other.begin(), decode_other.end()); + CHECK(decode_other == decode_0); + } + // If the result is decodable, it should also be decodable with limit + // set to the actual number of elements, and not with one less. + if (decodable_0) { + for (auto& sketch : sketches) { + decode_temp.resize(decode_0.size()); + bool decodable = sketch.Decode(decode_temp); + CHECK(decodable); + std::sort(decode_temp.begin(), decode_temp.end()); + CHECK(decode_temp == decode_0); + if (!decode_0.empty()) { + decode_temp.resize(decode_0.size() - 1); + decodable = sketch.Decode(decode_temp); + CHECK(!decodable); + } + } + } + // If the actual number of elements is not higher than the capacity, the + // result should be decodable, and the result should match what we put in. + if (real_element_count <= capacity) { + CHECK(decodable_0); + CHECK(decode_0 == elements); + } + } +} + +void TestComputeFunctions() { + for (uint32_t bits = 0; bits <= 256; ++bits) { + for (uint32_t fpbits = 0; fpbits <= 512; ++fpbits) { + std::vector table_max_elements(1025); + for (size_t capacity = 0; capacity <= 1024; ++capacity) { + table_max_elements[capacity] = minisketch_compute_max_elements(bits, capacity, fpbits); + // Exception for bits==0 + if (bits == 0) CHECK(table_max_elements[capacity] == 0); + // A sketch with capacity N cannot guarantee decoding more than N elements. + CHECK(table_max_elements[capacity] <= capacity); + // When asking for N bits of false positive protection, either no solution exists, or no more than ceil(N / bits) excess capacity should be needed. + if (bits > 0) CHECK(table_max_elements[capacity] == 0 || capacity - table_max_elements[capacity] <= (fpbits + bits - 1) / bits); + // Increasing capacity by one, if there is a solution, should always increment the max_elements by at least one as well. + if (capacity > 0) CHECK(table_max_elements[capacity] == 0 || table_max_elements[capacity] > table_max_elements[capacity - 1]); + } + + std::vector table_capacity(513); + for (size_t max_elements = 0; max_elements <= 512; ++max_elements) { + table_capacity[max_elements] = minisketch_compute_capacity(bits, max_elements, fpbits); + // Exception for bits==0 + if (bits == 0) CHECK(table_capacity[max_elements] == 0); + // To be able to decode N elements, capacity needs to be at least N. + if (bits > 0) CHECK(table_capacity[max_elements] >= max_elements); + // A sketch of N bits in total cannot have more than N bits of false positive protection; + if (bits > 0) CHECK(bits * table_capacity[max_elements] >= fpbits); + // When asking for N bits of false positive protection, no more than ceil(N / bits) excess capacity should be needed. + if (bits > 0) CHECK(table_capacity[max_elements] - max_elements <= (fpbits + bits - 1) / bits); + // Increasing max_elements by one can only increment the capacity by 0 or 1. + if (max_elements > 0 && fpbits < 256) CHECK(table_capacity[max_elements] == table_capacity[max_elements - 1] || table_capacity[max_elements] == table_capacity[max_elements - 1] + 1); + // Check round-tripping max_elements->capacity->max_elements (only a lower bound) + CHECK(table_capacity[max_elements] <= 1024); + CHECK(table_max_elements[table_capacity[max_elements]] == 0 || table_max_elements[table_capacity[max_elements]] >= max_elements); + } + + for (size_t capacity = 0; capacity <= 512; ++capacity) { + // Check round-tripping capacity->max_elements->capacity (exact, if it exists) + CHECK(table_max_elements[capacity] <= 512); + CHECK(table_max_elements[capacity] == 0 || table_capacity[table_max_elements[capacity]] == capacity); + } + } + } +} + +} // namespace + +int main(int argc, char** argv) { + uint64_t test_complexity = 4; + if (argc > 1) { + size_t len = 0; + std::string arg{argv[1]}; + try { + test_complexity = 0; + long long complexity = std::stoll(arg, &len); + if (complexity >= 1 && len == arg.size() && ((uint64_t)complexity <= std::numeric_limits::max() >> 10)) { + test_complexity = complexity; + } + } catch (const std::logic_error&) {} + if (test_complexity == 0) { + fprintf(stderr, "Invalid complexity specified: '%s'\n", arg.c_str()); + return 1; + } + } + +#ifdef MINISKETCH_VERIFY + const char* mode = " in verify mode"; +#else + const char* mode = ""; +#endif + printf("Running libminisketch tests%s with complexity=%llu\n", mode, (unsigned long long)test_complexity); + + TestComputeFunctions(); + + for (unsigned j = 2; j <= 64; ++j) { + TestRandomized(j, 8, (test_complexity << 10) / j); + TestRandomized(j, 128, (test_complexity << 7) / j); + TestRandomized(j, 4096, test_complexity / j); + } + + // Test capacity==0 together with all field sizes, and then + // all combinations of bits and capacity up to a certain bits*capacity, + // depending on test_complexity. + for (int weight = 0; weight <= 40; ++weight) { + for (int bits = 2; weight == 0 ? bits <= 64 : (bits <= 32 && bits <= weight); ++bits) { + int capacity = weight / bits; + if (capacity * bits != weight) continue; + TestExhaustive(bits, capacity); + } + if (weight >= 16 && test_complexity >> (weight - 16) == 0) break; + } + + printf("All tests successful.\n"); + return 0; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000000..fdb3f3a231 --- /dev/null +++ b/src/util.h @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko * + * Distributed under the MIT software license, see the accompanying * + * file LICENSE or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _MINISKETCH_UTIL_H_ +#define _MINISKETCH_UTIL_H_ + +#ifdef MINISKETCH_VERIFY +#include +#endif + +#if !defined(__GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define __GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define __GNUC_PREREQ(_maj,_min) 0 +# endif +#endif + +#if __GNUC_PREREQ(3, 0) +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +/* Assertion macros */ + +/** + * Unconditional failure on condition failure. + * Primarily used in testing harnesses. + */ +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \ + abort(); \ + } \ +} while(0) + +/** + * Check macro that does nothing in normal non-verify builds but crashes in verify builds. + * This is used to test conditions at runtime that should always be true, but are either + * expensive to test or in locations where returning on failure would be messy. + */ +#ifdef MINISKETCH_VERIFY +#define CHECK_SAFE(cond) CHECK(cond) +#else +#define CHECK_SAFE(cond) +#endif + +/** + * Check a condition and return on failure in non-verify builds, crash in verify builds. + * Used for inexpensive conditions which believed to be always true in locations where + * a graceful exit is possible. + */ +#ifdef MINISKETCH_VERIFY +#define CHECK_RETURN(cond, rvar) do { \ + if (EXPECT(!(cond), 0)) { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \ + abort(); \ + return rvar; /* Does nothing, but causes compile to warn on incorrect return types. */ \ + } \ +} while(0) +#else +#define CHECK_RETURN(cond, rvar) do { \ + if (EXPECT(!(cond), 0)) { \ + return rvar; \ + } \ +} while(0) +#endif + +#endif diff --git a/tests/pyminisketch.py b/tests/pyminisketch.py new file mode 100755 index 0000000000..7a9ea9c1f1 --- /dev/null +++ b/tests/pyminisketch.py @@ -0,0 +1,507 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +"""Native Python (slow) reimplementation of libminisketch' algorithms.""" + +import random +import unittest + +# Irreducible polynomials over GF(2) to use (represented as integers). +# +# Most fields can be defined by multiple such polynomials. Minisketch uses the one with the minimal +# number of nonzero coefficients, and tie-breaking by picking the lexicographically first among +# those. +# +# All polynomials for degrees 2 through 64 (inclusive) are given. +GF2_MODULI = [ + None, None, + 2**2 + 2**1 + 1, + 2**3 + 2**1 + 1, + 2**4 + 2**1 + 1, + 2**5 + 2**2 + 1, + 2**6 + 2**1 + 1, + 2**7 + 2**1 + 1, + 2**8 + 2**4 + 2**3 + 2**1 + 1, + 2**9 + 2**1 + 1, + 2**10 + 2**3 + 1, + 2**11 + 2**2 + 1, + 2**12 + 2**3 + 1, + 2**13 + 2**4 + 2**3 + 2**1 + 1, + 2**14 + 2**5 + 1, + 2**15 + 2**1 + 1, + 2**16 + 2**5 + 2**3 + 2**1 + 1, + 2**17 + 2**3 + 1, + 2**18 + 2**3 + 1, + 2**19 + 2**5 + 2**2 + 2**1 + 1, + 2**20 + 2**3 + 1, + 2**21 + 2**2 + 1, + 2**22 + 2**1 + 1, + 2**23 + 2**5 + 1, + 2**24 + 2**4 + 2**3 + 2**1 + 1, + 2**25 + 2**3 + 1, + 2**26 + 2**4 + 2**3 + 2**1 + 1, + 2**27 + 2**5 + 2**2 + 2**1 + 1, + 2**28 + 2**1 + 1, + 2**29 + 2**2 + 1, + 2**30 + 2**1 + 1, + 2**31 + 2**3 + 1, + 2**32 + 2**7 + 2**3 + 2**2 + 1, + 2**33 + 2**10 + 1, + 2**34 + 2**7 + 1, + 2**35 + 2**2 + 1, + 2**36 + 2**9 + 1, + 2**37 + 2**6 + 2**4 + 2**1 + 1, + 2**38 + 2**6 + 2**5 + 2**1 + 1, + 2**39 + 2**4 + 1, + 2**40 + 2**5 + 2**4 + 2**3 + 1, + 2**41 + 2**3 + 1, + 2**42 + 2**7 + 1, + 2**43 + 2**6 + 2**4 + 2**3 + 1, + 2**44 + 2**5 + 1, + 2**45 + 2**4 + 2**3 + 2**1 + 1, + 2**46 + 2**1 + 1, + 2**47 + 2**5 + 1, + 2**48 + 2**5 + 2**3 + 2**2 + 1, + 2**49 + 2**9 + 1, + 2**50 + 2**4 + 2**3 + 2**2 + 1, + 2**51 + 2**6 + 2**3 + 2**1 + 1, + 2**52 + 2**3 + 1, + 2**53 + 2**6 + 2**2 + 2**1 + 1, + 2**54 + 2**9 + 1, + 2**55 + 2**7 + 1, + 2**56 + 2**7 + 2**4 + 2**2 + 1, + 2**57 + 2**4 + 1, + 2**58 + 2**19 + 1, + 2**59 + 2**7 + 2**4 + 2**2 + 1, + 2**60 + 2**1 + 1, + 2**61 + 2**5 + 2**2 + 2**1 + 1, + 2**62 + 2**29 + 1, + 2**63 + 2**1 + 1, + 2**64 + 2**4 + 2**3 + 2**1 + 1 +] + +class GF2Ops: + """Class to perform GF(2^field_size) operations on elements represented as integers. + + Given that elements are represented as integers, addition is simply xor, and not + exposed here. + """ + + def __init__(self, field_size): + """Construct a GF2Ops object for the specified field size.""" + self.field_size = field_size + self._modulus = GF2_MODULI[field_size] + assert self._modulus is not None + + def mul2(self, x): + """Multiply x by 2 in GF(2^field_size).""" + x <<= 1 + if x >> self.field_size: + x ^= self._modulus + return x + + def mul(self, x, y): + """Multiply x by y in GF(2^field_size).""" + ret = 0 + while y: + if y & 1: + ret ^= x + y >>= 1 + x = self.mul2(x) + return ret + + def sqr(self, x): + """Square x in GF(2^field_size).""" + return self.mul(x, x) + + def inv(self, x): + """Compute the inverse of x in GF(2^field_size).""" + assert x != 0 + # Use the extended polynomial Euclidean GCD algorithm on (modulus, x), over GF(2). + # See https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor. + t1, t2 = 0, 1 + r1, r2 = self._modulus, x + r1l, r2l = self.field_size + 1, r2.bit_length() + while r2: + q = r1l - r2l + r1 ^= r2 << q + t1 ^= t2 << q + r1l = r1.bit_length() + if r1 < r2: + t1, t2 = t2, t1 + r1, r2 = r2, r1 + r1l, r2l = r2l, r1l + assert r1 == 1 + return t1 + +class TestGF2Ops(unittest.TestCase): + """Test class for basic arithmetic properties of GF2Ops.""" + + def field_size_test(self, field_size): + """Test operations for given field_size.""" + + gf = GF2Ops(field_size) + for i in range(100): + x = random.randrange(1 << field_size) + y = random.randrange(1 << field_size) + x2 = gf.mul2(x) + xy = gf.mul(x, y) + self.assertEqual(x2, gf.mul(x, 2)) # mul2(x) == x*2 + self.assertEqual(x2, gf.mul(2, x)) # mul2(x) == 2*x + self.assertEqual(xy == 0, x == 0 or y == 0) + self.assertEqual(xy == x, y == 1 or x == 0) + self.assertEqual(xy == y, x == 1 or y == 0) + self.assertEqual(xy, gf.mul(y, x)) # x*y == y*x + if i < 10: + xp = x + for _ in range(field_size): + xp = gf.sqr(xp) + self.assertEqual(xp, x) # x^(2^field_size) == x + if y != 0: + yi = gf.inv(y) + self.assertEqual(y == yi, y == 1) # y==1/x iff y==1 + self.assertEqual(gf.mul(y, yi), 1) # y*(1/y) == 1 + yii = gf.inv(yi) + self.assertEqual(y, yii) # 1/(1/y) == y + if x != 0: + xi = gf.inv(x) + xyi = gf.inv(xy) + self.assertEqual(xyi, gf.mul(xi, yi)) # (1/x)*(1/y) == 1/(x*y) + + def test(self): + """Run tests.""" + for field_size in range(2, 65): + self.field_size_test(field_size) + +# The operations below operate on polynomials over GF(2^field_size), represented as lists of +# integers: +# +# [a, b, c, ...] = a + b*x + c*x^2 + ... +# +# As an invariant, there are never any trailing zeroes in the list representation. +# +# Examples: +# * [] = 0 +# * [3] = 3 +# * [0, 1] = x +# * [2, 0, 5] = 5*x^2 + 2 + +def poly_monic(poly, gf): + """Return a monic version of the polynomial poly.""" + # Multiply every coefficient with the inverse of the top coefficient. + inv = gf.inv(poly[-1]) + return [gf.mul(inv, v) for v in poly] + +def poly_divmod(poly, mod, gf): + """Return the polynomial (quotient, remainder) of poly divided by mod.""" + assert len(mod) > 0 and mod[-1] == 1 # Require monic mod. + if len(poly) < len(mod): + return ([], poly) + val = list(poly) + div = [0 for _ in range(len(val) - len(mod) + 1)] + while len(val) >= len(mod): + term = val[-1] + div[len(val) - len(mod)] = term + # If the highest coefficient in val is nonzero, subtract a multiple of mod from it. + val.pop() + if term != 0: + for x in range(len(mod) - 1): + val[1 + x - len(mod)] ^= gf.mul(term, mod[x]) + # Prune trailing zero coefficients. + while len(val) > 0 and val[-1] == 0: + val.pop() + return div, val + +def poly_gcd(a, b, gf): + """Return the polynomial GCD of a and b.""" + if len(a) < len(b): + a, b = b, a + # Use Euclid's algorithm to find the GCD of a and b. + # see https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclid's_algorithm. + while len(b) > 0: + b = poly_monic(b, gf) + (_, b), a = poly_divmod(a, b, gf), b + return a + +def poly_sqr(poly, gf): + """Return the square of polynomial poly.""" + if len(poly) == 0: + return [] + # In characteristic-2 fields, thanks to Frobenius' endomorphism ((a + b)^2 = a^2 + b^2), + # squaring a polynomial is easy: square all the coefficients and interleave with zeroes. + # E.g., (3 + 5*x + 17*x^2)^2 = 3^2 + (5*x)^2 + (17*x^2)^2. + # See https://en.wikipedia.org/wiki/Frobenius_endomorphism. + return [0 if i & 1 else gf.sqr(poly[i // 2]) for i in range(2 * len(poly) - 1)] + +def poly_tracemod(poly, param, gf): + """Compute y + y^2 + y^4 + ... + y^(2^(field_size-1)) mod poly, where y = param*x.""" + out = [0, param] + for _ in range(gf.field_size - 1): + # In each loop iteration i, we start with out = y + y^2 + ... + y^(2^i). By squaring that we + # transform it into out = y^2 + y^4 + ... + y^(2^(i+1)). + out = poly_sqr(out, gf) + # Thus, we just need to add y again to it to get out = y + ... + y^(2^(i+1)). + while len(out) < 2: + out.append(0) + out[1] = param + # Finally take a modulus to keep the intermediary polynomials small. + _, out = poly_divmod(out, poly, gf) + return out + +def poly_frobeniusmod(poly, gf): + """Compute x^(2^field_size) mod poly.""" + out = [0, 1] + for _ in range(gf.field_size): + _, out = poly_divmod(poly_sqr(out, gf), poly, gf) + return out + +def poly_find_roots(poly, gf): + """Find the roots of poly if fully factorizable with unique roots, [] otherwise.""" + assert len(poly) > 0 + # If the polynomial is constant (and nonzero), it has no roots. + if len(poly) == 1: + return [] + # Make the polynomial monic (which doesn't change its roots). + poly = poly_monic(poly, gf) + # If the polynomial is of the form x+a, return a. + if len(poly) == 2: + return [poly[0]] + # Otherwise, first test that poly can be completely factored into unique roots. The polynomial + # x^(2^fieldsize)-x has every field element once as root. Thus we want to know that that is a + # multiple of poly. Compute x^(field_size) mod poly, which needs to equal x if that is the case + # (unless poly has degree <= 1, but that case is handled above). + if poly_frobeniusmod(poly, gf) != [0, 1]: + return [] + + def rec_split(poly, randv): + """Recursively split poly using the Berlekamp trace algorithm.""" + # See https://hal.archives-ouvertes.fr/hal-00626997/document. + assert len(poly) > 1 and poly[-1] == 1 # Require a monic poly. + # If poly is of the form x+a, its root is a. + if len(poly) == 2: + return [poly[0]] + # Try consecutive randomization factors randv, until one is found that factors poly. + while True: + # Compute the trace of (randv*x) mod poly. This is a polynomial that maps half of the + # domain to 0, and the other half to 1. Which half that is is controlled by randv. + # By taking it modulo poly, we only add a multiple of poly. Thus the result has at least + # the shared roots of the trace polynomial and poly still, but may have others. + trace = poly_tracemod(poly, randv, gf) + # Using the set {2^i*a for i=0..fieldsize-1} gives optimally independent randv values + # (no more than fieldsize are ever needed). + randv = gf.mul2(randv) + # Now take the GCD of this trace polynomial with poly. The result is a polynomial + # that only has the shared roots of the trace polynomial and poly as roots. + gcd = poly_gcd(trace, poly, gf) + # If the result has a degree higher than 1, and lower than that of poly, we found a + # useful factorization. + if len(gcd) != len(poly) and len(gcd) > 1: + break + # Otherwise, continue with another randv. + # Find the actual factors: the monic version of the GCD above, and poly divided by it. + factor1 = poly_monic(gcd, gf) + factor2, _ = poly_divmod(poly, gcd, gf) + # Recurse. + return rec_split(factor1, randv) + rec_split(factor2, randv) + + # Invoke the recursive splitting with a random initial factor, and sort the results. + return sorted(rec_split(poly, random.randrange(1, 1 << gf.field_size))) + +class TestPolyFindRoots(unittest.TestCase): + """Test class for poly_find_roots.""" + + def field_size_test(self, field_size): + """Run tests for given field_size.""" + gf = GF2Ops(field_size) + for test_size in [0, 1, 2, 3, 10]: + roots = [random.randrange(1 << field_size) for _ in range(test_size)] + roots_set = set(roots) + # Construct a polynomial with all elements of roots as roots (with multiplicity). + poly = [1] + for root in roots: + new_poly = [0] + poly + for n, c in enumerate(poly): + new_poly[n] ^= gf.mul(c, root) + poly = new_poly + # Invoke the root finding algorithm. + found_roots = poly_find_roots(poly, gf) + # The result must match the input, unless any roots were repeated. + if len(roots) == len(roots_set): + self.assertEqual(found_roots, sorted(roots)) + else: + self.assertEqual(found_roots, []) + + def test(self): + """Run tests.""" + for field_size in range(2, 65): + self.field_size_test(field_size) + +def berlekamp_massey(syndromes, gf): + """Implement the Berlekamp-Massey algorithm. + + Takes as input a sequence of GF(2^field_size) elements, and returns the shortest LSFR + that generates it, represented as a polynomial. + """ + # See https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm. + current = [1] + prev = [1] + b_inv = 1 + for n, discrepancy in enumerate(syndromes): + # Compute discrepancy + for i in range(1, len(current)): + discrepancy ^= gf.mul(syndromes[n - i], current[i]) + + # Correct if discrepancy is nonzero. + if discrepancy: + x = n + 1 - (len(current) - 1) - (len(prev) - 1) + if 2 * (len(current) - 1) <= n: + tmp = list(current) + current.extend(0 for _ in range(len(prev) + x - len(current))) + mul = gf.mul(discrepancy, b_inv) + for i, v in enumerate(prev): + current[i + x] ^= gf.mul(mul, v) + prev = tmp + b_inv = gf.inv(discrepancy) + else: + mul = gf.mul(discrepancy, b_inv) + for i, v in enumerate(prev): + current[i + x] ^= gf.mul(mul, v) + return current + +class Minisketch: + """A Minisketch sketch. + + This represents a sketch of a certain capacity, with elements of a certain bit size. + """ + + def __init__(self, field_size, capacity): + """Initialize an empty sketch with the specified field_size size and capacity.""" + self.field_size = field_size + self.capacity = capacity + self.odd_syndromes = [0] * capacity + self.gf = GF2Ops(field_size) + + def add(self, element): + """Add an element to this sketch. 1 <= element < 2**field_size.""" + sqr = self.gf.sqr(element) + for pos in range(self.capacity): + self.odd_syndromes[pos] ^= element + element = self.gf.mul(sqr, element) + + def serialized_size(self): + """Compute how many bytes a serialization of this sketch will be in size.""" + return (self.capacity * self.field_size + 7) // 8 + + def serialize(self): + """Serialize this sketch to bytes.""" + val = 0 + for i in range(self.capacity): + val |= self.odd_syndromes[i] << (self.field_size * i) + return val.to_bytes(self.serialized_size(), 'little') + + def deserialize(self, byte_data): + """Deserialize a byte array into this sketch, overwriting its contents.""" + assert len(byte_data) == self.serialized_size() + val = int.from_bytes(byte_data, 'little') + for i in range(self.capacity): + self.odd_syndromes[i] = (val >> (self.field_size * i)) & ((1 << self.field_size) - 1) + + def clone(self): + """Return a clone of this sketch.""" + ret = Minisketch(self.field_size, self.capacity) + ret.odd_syndromes = list(self.odd_syndromes) + ret.gf = self.gf + return ret + + def merge(self, other): + """Merge a sketch with another sketch. Corresponds to XOR'ing their serializations.""" + assert self.capacity == other.capacity + assert self.field_size == other.field_size + for i in range(self.capacity): + self.odd_syndromes[i] ^= other.odd_syndromes[i] + + def decode(self, max_count=None): + """Decode the contents of this sketch. + + Returns either a list of elements or None if undecodable. + """ + # We know the odd syndromes s1=x+y+..., s3=x^3+y^3+..., s5=..., and reconstruct the even + # syndromes from this: + # * s2 = x^2+y^2+.... = (x+y+...)^2 = s1^2 + # * s4 = x^4+y^4+.... = (x^2+y^2+...)^2 = s2^2 + # * s6 = x^6+y^6+.... = (x^3+y^3+...)^2 = s3^2 + all_syndromes = [0 for _ in range(2 * len(self.odd_syndromes))] + for i in range(len(self.odd_syndromes)): + all_syndromes[i * 2] = self.odd_syndromes[i] + all_syndromes[i * 2 + 1] = self.gf.sqr(all_syndromes[i]) + # Given the syndromes, find the polynomial that generates them. + poly = berlekamp_massey(all_syndromes, self.gf) + # Deal with failure and trivial cases. + if len(poly) == 0: + return None + if len(poly) == 1: + return [] + if max_count is not None and len(poly) > 1 + max_count: + return None + # If the polynomial can be factored into (1-m1*x)*(1-m2*x)*...*(1-mn*x), then {m1,m2,...,mn} + # is our set. As each factor (1-m*x) has 1/m as root, we're really just looking for the + # inverses of the roots. We find these by reversing the order of the coefficients, and + # finding the roots. + roots = poly_find_roots(list(reversed(poly)), self.gf) + if len(roots) == 0: + return None + return roots + +class TestMinisketch(unittest.TestCase): + """Test class for Minisketch.""" + + @classmethod + def construct_data(cls, field_size, num_a_only, num_b_only, num_both): + """Construct two random lists of elements in [1..2**field_size-1]. + + Each list will have unique elements that don't appear in the other (num_a_only in the first + and num_b_only in the second), and num_both elements will appear in both.""" + sample = [] + # Simulate random.sample here (which doesn't work with ranges over 2**63). + for _ in range(num_a_only + num_b_only + num_both): + while True: + r = random.randrange(1, 1 << field_size) + if r not in sample: + sample.append(r) + break + full_a = sample[:num_a_only + num_both] + full_b = sample[num_a_only:] + random.shuffle(full_a) + random.shuffle(full_b) + return full_a, full_b + + def field_size_capacity_test(self, field_size, capacity): + """Test Minisketch methods for a specific field and capacity.""" + used_capacity = random.randrange(capacity + 1) + num_a = random.randrange(used_capacity + 1) + num_both = random.randrange(min(2 * capacity, (1 << field_size) - 1 - used_capacity) + 1) + full_a, full_b = self.construct_data(field_size, num_a, used_capacity - num_a, num_both) + sketch_a = Minisketch(field_size, capacity) + sketch_b = Minisketch(field_size, capacity) + for v in full_a: + sketch_a.add(v) + for v in full_b: + sketch_b.add(v) + sketch_combined = sketch_a.clone() + sketch_b_ser = sketch_b.serialize() + sketch_b_received = Minisketch(field_size, capacity) + sketch_b_received.deserialize(sketch_b_ser) + sketch_combined.merge(sketch_b_received) + decode = sketch_combined.decode() + self.assertEqual(decode, sorted(set(full_a) ^ set(full_b))) + + def test(self): + """Run tests.""" + for field_size in range(2, 65): + for capacity in [0, 1, 2, 5, 10, field_size]: + self.field_size_capacity_test(field_size, min(capacity, (1 << field_size) - 1)) + +if __name__ == '__main__': + unittest.main()