Annotate noexcept and constexpr with HEDLEY, and add interval containment, ChaCha, and the dyadic range tables.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
875f09fec1
commit
0d8a5a8131
97 changed files with 9212 additions and 1159 deletions
|
|
@ -1037,7 +1037,7 @@ EXAMPLE_PATTERNS = *
|
||||||
# irrespective of the value of the RECURSIVE tag.
|
# irrespective of the value of the RECURSIVE tag.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
EXAMPLE_RECURSIVE = NO
|
EXAMPLE_RECURSIVE = YES
|
||||||
|
|
||||||
# The IMAGE_PATH tag can be used to specify one or more files or directories
|
# The IMAGE_PATH tag can be used to specify one or more files or directories
|
||||||
# that contain images that are to be included in the documentation (see the
|
# that contain images that are to be included in the documentation (see the
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
/// @brief an example of `dpf::keyword` in use
|
/// @brief an example of `dpf::keyword` in use
|
||||||
|
|
||||||
/// @example input_types/xor_wrapper.cpp xor_wrapper.cpp
|
/// @example input_types/xor_wrapper.cpp xor_wrapper.cpp
|
||||||
/// @brief an example of `dpf::memoizers` in use
|
/// @brief an example of `dpf::xor_wrapper` as an input
|
||||||
|
|
||||||
/// @example input_types/custom.cpp custom.cpp
|
/// @example input_types/custom.cpp custom.cpp
|
||||||
/// @brief an example of `dpf::output_buffers` in use
|
/// @brief an example of a custom input type
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
@ -30,25 +30,25 @@
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// @example output_types/integral_types.cpp integral_types.cpp
|
/// @example output_types/integral_types.cpp integral_types.cpp
|
||||||
/// @brief an example of `dpf::eval_point` in use
|
/// @brief an example of an integer output
|
||||||
|
|
||||||
/// @example output_types/extended_types.cpp extended_types.cpp
|
/// @example output_types/extended_types.cpp extended_types.cpp
|
||||||
/// @brief an example of `dpf::eval_interval` in use
|
/// @brief an example of a 128-bit integer output
|
||||||
|
|
||||||
/// @example output_types/bit.cpp bit.cpp
|
/// @example output_types/bit.cpp bit.cpp
|
||||||
/// @brief an example of `dpf::eval_full` in use
|
/// @brief an example of `dpf::bit` as an output
|
||||||
|
|
||||||
/// @example output_types/bitstring.cpp bitstring.cpp
|
/// @example output_types/bitstring.cpp bitstring.cpp
|
||||||
/// @brief an example of `dpf::eval_sequence` in use
|
/// @brief an example of `dpf::bitstring` as an output
|
||||||
|
|
||||||
/// @example output_types/wildcard.cpp wildcard.cpp
|
/// @example output_types/wildcard.cpp wildcard.cpp
|
||||||
/// @brief an example of `dpf::eval_sequence` in use
|
/// @brief an example of `dpf::wildcard` as an output
|
||||||
|
|
||||||
/// @example output_types/xor_wrapper.cpp xor_wrapper.cpp
|
/// @example output_types/xor_wrapper.cpp xor_wrapper.cpp
|
||||||
/// @brief an example of `dpf::memoizers` in use
|
/// @brief an example of `dpf::xor_wrapper` as an output
|
||||||
|
|
||||||
/// @example output_types/custom.cpp custom.cpp
|
/// @example output_types/custom.cpp custom.cpp
|
||||||
/// @brief an example of `dpf::output_buffers` in use
|
/// @brief an example of a custom output type
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
@ -73,7 +73,10 @@
|
||||||
/// @brief an example of `dpf::memoizers` in use
|
/// @brief an example of `dpf::memoizers` in use
|
||||||
|
|
||||||
/// @example evaluation/output_buffers.cpp output_buffers.cpp
|
/// @example evaluation/output_buffers.cpp output_buffers.cpp
|
||||||
/// @brief an example of `dpf::output_buffers` in use
|
/// @brief an example of `dpf::output_buffer` in use
|
||||||
|
|
||||||
|
/// @example evaluation/buffered_prg.cpp buffered_prg.cpp
|
||||||
|
/// @brief an example of `dpf::randomness::buffered_prg` and `lane_table`
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,126 @@
|
||||||
<!-- # Evaluating DPFs {#evalaution} -->
|
<!-- # Evaluating DPFs {#evaluation} -->
|
||||||
Once a `DPF` generated, the *eval_* * functions are used to evaluate differents inputs.
|
|
||||||
The appropriate function depends on your specific needs. If you only need the DPF's output
|
|
||||||
for a single input value, use `eval_point`. For evaluating a continuous range of inputs,
|
|
||||||
`eval_interval` is suitable. To analyze the DPF's behavior across its entire domain, use `eval_full`.
|
|
||||||
The code likely offers different implementations of memoization and output buffers, allowing you to
|
|
||||||
optimize for memory usage or execution speed depending on your needs.\n
|
|
||||||
Using a PRG while making a `DPF` allows the user to check how much it cost to manipulate the `DPF`s.
|
|
||||||
|
|
||||||
# Memoizers
|
`make_dpf(x, y)` returns one key per party. Evaluation of a key yields that
|
||||||
The `memoizers` remembers the most used path while the DPF is being created. These are usefull functions
|
party's share. Leaf outputs are subtractive shares: open them with
|
||||||
to improve the speed and the cost of execution.
|
`dpf::reconstruct`, which computes `share0 - share1`. Comparison outputs are
|
||||||
|
additive shares: `reconstruct` computes `share0 + share1`. A single-output
|
||||||
|
`eval_point` returns a small handle; `*handle` is the share. An unassigned
|
||||||
|
`dpf::wildcard` output throws `std::runtime_error`.
|
||||||
|
|
||||||
|
`[from, to]` is inclusive. The points passed to `eval_sequence` are a
|
||||||
|
nondecreasing range; an unsorted range throws `std::runtime_error`.
|
||||||
|
|
||||||
|
Memoizers hold interior nodes between calls. Output buffers hold the shares
|
||||||
|
a multi-point evaluation writes. Pass both as mutable named objects when a
|
||||||
|
later call should reuse them. The factories
|
||||||
|
`make_basic_path_memoizer`, `make_basic_interval_memoizer`, and
|
||||||
|
`make_*_sequence_memoizer` unwrap `party_key`, so a workspace built from
|
||||||
|
either party's type accepts both parties. Name that type with
|
||||||
|
`dpf::unwrap_party_key_t<std::decay_t<decltype(key)>>`.
|
||||||
|
|
||||||
|
# Memoizers {#memoizers}
|
||||||
|
|
||||||
|
## Path memoizers {#path_memoizers}
|
||||||
|
|
||||||
|
`eval_point` walks one root-to-leaf path. `make_basic_path_memoizer<Key>()`
|
||||||
|
keeps every node of the previous point, and the next point recomputes only
|
||||||
|
the suffix after the common prefix. `make_nonmemoizing_path_memoizer<Key>()`
|
||||||
|
keeps one node and starts from the root on every call. A one-off
|
||||||
|
`eval_point(key, x)` uses the nonmemoizing memoizer.
|
||||||
|
|
||||||
|
Pass the memoizer as a mutable lvalue. The default argument is a new
|
||||||
|
temporary, so it has no previous point to resume from. Keep a separate
|
||||||
|
memoizer for each key you are in the middle of evaluating. A different root
|
||||||
|
restarts the path.
|
||||||
|
|
||||||
**Code samples**\n
|
**Code samples**\n
|
||||||
For instance, in the code below the utilization of `dpf::make_basic_path_memoizer` reduced by 10 the time of execution
|
|
||||||
compare to the code that is commented that doesn't use the `memoizers`.
|
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
|
||||||
- <b class="tab-title">memoizers.cpp</b> \include{cpp} evaluation/memoizers.cpp
|
- <b class="tab-title">memoizers.cpp</b> \include{cpp} evaluation/memoizers.cpp
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Interval memoizers {#interval_memoizers}
|
||||||
|
|
||||||
# dpf::eval_point
|
`eval_interval` and `eval_full` expand every leaf in a range.
|
||||||
This function evaluate a single input of a DPF. The XOR result of the `eval_point` for both shares will only be equal to 1 if it represents the correct input in both evaluations. As input arguments it uses the `share` and the input to evaluate (note: it can't be a wildcard, otherwise it will throw an error).\n
|
`make_basic_interval_memoizer<Key>(from, to)` stores two levels of that
|
||||||
|
range. That is the workspace the convenience overloads allocate.
|
||||||
|
`make_full_tree_interval_memoizer<Key>(from, to)` keeps every level.
|
||||||
|
`make_basic_full_memoizer<Key>()` and `make_full_tree_full_memoizer<Key>()`
|
||||||
|
are the same workspaces sized for the whole domain.
|
||||||
|
|
||||||
**See also**\n
|
Size the memoizer for the widest interval you will pass to it. A wider
|
||||||
PIR
|
interval throws `std::length_error`. The same key and the same endpoints
|
||||||
|
leave the final interior level in place. A different key or a different
|
||||||
|
interval rebuilds into the same allocation.
|
||||||
|
|
||||||
**Pro tip**\n
|
Passing only the memoizer still allocates a fresh output buffer and returns
|
||||||
Use the `dpf::pathmemoizer` for a faster execution.\n
|
`std::pair(buffer, iterable)`.
|
||||||
|
|
||||||
|
## Sequence memoizers {#sequence_memoizers}
|
||||||
|
|
||||||
|
`make_sequence_recipe<Key>(begin, end)` compiles a sorted point list into a
|
||||||
|
traversal. The recipe depends on the input type, and one recipe serves every
|
||||||
|
key of that type.
|
||||||
|
|
||||||
|
A sequence memoizer stores a reference to the recipe object it was built
|
||||||
|
from and checks later calls by address. Pass that same object, and keep the
|
||||||
|
recipe alive for as long as the memoizer is used. A copy of the recipe
|
||||||
|
throws `std::logic_error`.
|
||||||
|
|
||||||
|
`make_double_space_sequence_memoizer<Key>(recipe)` keeps two levels. It is
|
||||||
|
what `eval_sequence(key, recipe, buffer)` allocates when you omit the
|
||||||
|
memoizer. `make_inplace_reversing_sequence_memoizer<Key>(recipe)` keeps one
|
||||||
|
level and reverses direction as it descends.
|
||||||
|
`make_full_tree_sequence_memoizer<Key>(recipe)` retains every level. A key
|
||||||
|
whose depth differs from the recipe throws `std::logic_error`.
|
||||||
|
|
||||||
|
# Output buffers {#output_buffers}
|
||||||
|
|
||||||
|
`output_buffer<T>` is move-only storage with `size`, iterators, `data`, and
|
||||||
|
`operator[]`. Build it with the factory that matches the evaluation:
|
||||||
|
|
||||||
|
- `make_output_buffer_for_interval(key, from, to)`
|
||||||
|
- `make_output_buffer_for_full(key)`
|
||||||
|
- `make_output_buffer_for_subsequence(key, begin, end, tag)`
|
||||||
|
- `make_output_buffer_for_recipe_subsequence(key, recipe, tag)`
|
||||||
|
|
||||||
|
On a `party_key`, leaf slots are `subtractive_share`s and comparison slots
|
||||||
|
are `additive_share`s. `dpf::bit`, `dpf::twobit`, and `dpf::nyble` slots are
|
||||||
|
packed. Trivially default-constructible slot types are left uninitialized;
|
||||||
|
the evaluation overwrites every slot it is responsible for.
|
||||||
|
|
||||||
|
`eval_interval` and recipe `eval_sequence` take the buffer as a non-const
|
||||||
|
reference, so the argument is a named object. The returned iterable refers
|
||||||
|
into that buffer. Read it while the buffer is alive, and only over the
|
||||||
|
points the iterable covers. The next evaluation overwrites those slots.
|
||||||
|
|
||||||
|
For one output, the convenience overload returns the buffer itself as the
|
||||||
|
first element of the pair. For several output indices it returns a tuple of
|
||||||
|
buffers.
|
||||||
|
|
||||||
|
`make_output_buffer(dpf::out<I>, key, from, to)` and
|
||||||
|
`make_output_buffer(dpf::cmp, key, n)` size a buffer for one channel of a
|
||||||
|
multi-output or comparison key. The slot types follow the same party-share
|
||||||
|
rule.
|
||||||
|
|
||||||
|
**Code samples**\n
|
||||||
|
<div class="tabbed">
|
||||||
|
|
||||||
|
- <b class="tab-title">output_buffers.cpp</b> \include{cpp} evaluation/output_buffers.cpp
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
# dpf::eval_point {#eval_point}
|
||||||
|
|
||||||
|
`eval_point(key, x)` evaluates output 0 at one input.
|
||||||
|
`eval_point<I>(key, x)` selects another output. Two or more indices,
|
||||||
|
`eval_point<0, 1>(key, x)`, return a tuple of shares rather than handles.
|
||||||
|
`eval_point(key, x, path)` continues a path memoizer.
|
||||||
|
|
||||||
|
`eval_point(dpf::out<I>, key, x, path)` and `eval_point(dpf::cmp, key, x, path)`
|
||||||
|
are the same walk with an explicit channel. Comparison results are additive
|
||||||
|
shares.
|
||||||
|
|
||||||
**Code samples**\n
|
**Code samples**\n
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
|
@ -37,12 +129,13 @@ Use the `dpf::pathmemoizer` for a faster execution.\n
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# dpf::eval_interval
|
# dpf::eval_interval {#eval_interval}
|
||||||
This function evaluates a contiguous range of inputs. As input arguments it uses the `share` generated by `make_dpf`,
|
|
||||||
`from` and `to` for the range of inputs to evaluate.\n
|
|
||||||
|
|
||||||
**See also**\n
|
`eval_interval(key, from, to)` evaluates every input from `from` through
|
||||||
PIR
|
`to`. The iterable yields one share per input, in that order. Optional
|
||||||
|
arguments are an output buffer and then an interval memoizer. An output
|
||||||
|
index pack, `eval_interval<0, 1>(key, from, to, buffers, memo)`, writes each
|
||||||
|
selected output.
|
||||||
|
|
||||||
**Code samples**\n
|
**Code samples**\n
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
|
@ -51,8 +144,13 @@ PIR
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# dpf::eval_full
|
# dpf::eval_full {#eval_full}
|
||||||
This function evaluate all the passible inputs it only uses as argument the `share` of the `DPF` to evaluate.
|
|
||||||
|
`eval_full(key)` is the closed interval from
|
||||||
|
`std::numeric_limits<Input>::min()` through `max()`. The buffer and
|
||||||
|
full-domain memoizer overloads match `eval_interval`.
|
||||||
|
`make_output_buffer_for_full(key)` and `make_basic_full_memoizer<Key>()`
|
||||||
|
size both for that domain.
|
||||||
|
|
||||||
**Code samples**\n
|
**Code samples**\n
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
|
@ -61,14 +159,16 @@ This function evaluate all the passible inputs it only uses as argument the `sha
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
# dpf::eval_sequence {#eval_sequence}
|
||||||
|
|
||||||
# dpf::eval_sequence
|
`eval_sequence(key, begin, end, tag)` evaluates a sorted list.
|
||||||
This function evaluate a subset of inputs that is not contiguous (useful for a `DPF` made with `keyword`),
|
`dpf::return_output_only_tag_` stores one share per listed point.
|
||||||
it uses as arguments the `share` generated by `make_dpf`, `from` and `to` for the subset of inputs to evaluate.\n
|
`dpf::return_entire_node_tag_` stores whole leaves; it is the default.
|
||||||
For a better utilization, you can use the `make_sequence_recipe` it takes as input a sorted list and returns a `recipe`.
|
The iterable still yields one share per listed point, in list order.
|
||||||
The cost of creating is a little bit worse than just calling `eval_sequence`, however once the `recipe` created
|
|
||||||
`eval_sequence` is faster and has a better cost.
|
|
||||||
|
|
||||||
|
`eval_sequence(key, recipe, buffer, memo, tag)` repeats that list.
|
||||||
|
`memo` is a sequence memoizer bound to `recipe`. Omit `memo` to allocate a
|
||||||
|
`double_space` workspace for that call.
|
||||||
|
|
||||||
**Code samples**\n
|
**Code samples**\n
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
|
@ -77,4 +177,22 @@ The cost of creating is a little bit worse than just calling `eval_sequence`, ho
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Output buffers
|
# Buffered PRG {#buffered_prg}
|
||||||
|
|
||||||
|
`dpf::randomness::buffered_prg<PRG, Ts...>` (alias
|
||||||
|
`dpf::randomness::aes_buffered_prg<Ts...>`) is a forward cursor with one
|
||||||
|
PRG stream per value type. `get<I>()` and `fill<I>(out, n)` consume the
|
||||||
|
cursor. `at<I>(index)` reads an absolute index and leaves the cursor where
|
||||||
|
it is. `sampled<I>()` is how far `get` and `fill` have advanced.
|
||||||
|
`per_stream_buffer_elems` is at least 1.
|
||||||
|
|
||||||
|
`dpf::randomness::lane_table<T>` is the seekable form for a runtime set of
|
||||||
|
roles. `value_at(role, index)` and `mask_at(role, index)` are independent
|
||||||
|
streams, and a repeated index returns the same element.
|
||||||
|
|
||||||
|
**Code samples**\n
|
||||||
|
<div class="tabbed">
|
||||||
|
|
||||||
|
- <b class="tab-title">buffered_prg.cpp</b> \include{cpp} evaluation/buffered_prg.cpp
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
||||||
62
examples/evaluation/buffered_prg.cpp
Normal file
62
examples/evaluation/buffered_prg.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
|
||||||
|
/// `buffered_prg` is a forward cursor, one stream per value type.
|
||||||
|
/// `at<I>(index)` reads by absolute index and does not move the cursor.
|
||||||
|
/// `lane_table` is the seekable form: value and mask streams per role.
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//! [buffered-prg]
|
||||||
|
dpf::randomness::aes_buffered_prg<std::uint64_t, std::uint32_t> prg(
|
||||||
|
/*per stream*/ 64);
|
||||||
|
|
||||||
|
std::uint64_t first = prg.get<0>();
|
||||||
|
std::uint64_t second = prg.get<0>();
|
||||||
|
// Absolute index 0 is `first` again. The cursor stays at 2.
|
||||||
|
std::uint64_t replay = prg.at<0>(0);
|
||||||
|
std::uint32_t other_stream = prg.get<1>();
|
||||||
|
|
||||||
|
std::uint64_t batch[4];
|
||||||
|
prg.fill<0>(batch, 4);
|
||||||
|
//! [buffered-prg]
|
||||||
|
|
||||||
|
if (std::memcmp(&replay, &first, sizeof(first)) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "buffered_prg at(0)\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (prg.sampled<0>() != 6)
|
||||||
|
{
|
||||||
|
std::cerr << "buffered_prg cursor\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Stream 1 has its own cursor.
|
||||||
|
if (prg.sampled<1>() != 1)
|
||||||
|
{
|
||||||
|
std::cerr << "buffered_prg stream 1\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
(void)second;
|
||||||
|
(void)other_stream;
|
||||||
|
(void)batch;
|
||||||
|
|
||||||
|
//! [lane-table]
|
||||||
|
dpf::randomness::lane_table<std::uint64_t> lanes(/*window*/ 32);
|
||||||
|
// Order does not matter. Masks are a separate stream from values.
|
||||||
|
auto v_late = lanes.value_at(/*role*/ 3, /*index*/ 10);
|
||||||
|
auto v_early = lanes.value_at(3, 10);
|
||||||
|
auto mask = lanes.mask_at(3, 10);
|
||||||
|
//! [lane-table]
|
||||||
|
if (std::memcmp(&v_late, &v_early, sizeof(v_late)) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "lane_table replay\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
(void)mask;
|
||||||
|
|
||||||
|
std::cout << "ok\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,44 @@
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
int main(int arc, char * argv[])
|
/// Every input of the domain, from `numeric_limits<Input>::min()` through
|
||||||
|
/// `max()`. Same shape as `eval_interval`.
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
uint16_t x = 42; // Input value
|
const std::uint8_t alpha = 42;
|
||||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>; // This is just to count the number of PRG invocations
|
const std::uint64_t beta = 7;
|
||||||
auto before = prg::count(); // In order to show how much this program cost
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
|
||||||
auto after = prg::count();
|
|
||||||
std::cout << "dpf::make_dpf used " << (after-before) << "\n";
|
|
||||||
|
|
||||||
before = prg::count();
|
//! [eval-full]
|
||||||
auto [buf0, iter0] = dpf::eval_full(dpf0);
|
auto [buf0, iter0] = dpf::eval_full(k0);
|
||||||
after = prg::count();
|
auto [buf1, iter1] = dpf::eval_full(k1);
|
||||||
std::cout << "dpf::eval_full(dpf0) used " << (after-before) << "\n";
|
|
||||||
before = prg::count();
|
auto it0 = std::begin(iter0);
|
||||||
auto [buf1, iter1] = dpf::eval_full(dpf1);
|
auto it1 = std::begin(iter1);
|
||||||
after = prg::count();
|
for (int x = std::numeric_limits<std::uint8_t>::min();
|
||||||
std::cout << "dpf::eval_full(dpf1) used " << (after-before) << "\n";
|
x <= std::numeric_limits<std::uint8_t>::max();
|
||||||
// Retrieve the original input by iterating over the two buffers
|
++x, ++it0, ++it1)
|
||||||
for (size_t i = 0; i < buf0.size(); ++i) {
|
{
|
||||||
bool item1 = buf0[i];
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
bool item2 = buf1[i];
|
std::uint64_t expect = (static_cast<std::uint8_t>(x) == alpha) ? beta : 0;
|
||||||
if (item1 ^ item2) std::cout << "The original input is: " << i << std::endl;
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "eval_full\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [eval-full]
|
||||||
|
if (it0 != std::end(iter0) || it1 != std::end(iter1))
|
||||||
|
{
|
||||||
|
std::cerr << "eval_full length\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Total PRG invocation: " << prg::count() << "\n";
|
std::cout << beta << "\n";
|
||||||
|
(void)buf0;
|
||||||
|
(void)buf1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,42 +1,43 @@
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
int main(int arc, char * argv[])
|
/// Inclusive range. The returned iterable yields one share per input in
|
||||||
|
/// `[from, to]`, in that order.
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
uint16_t x = 42, y;
|
const std::uint8_t alpha = 42;
|
||||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
const std::uint64_t beta = 7;
|
||||||
|
const std::uint8_t from = 40;
|
||||||
|
const std::uint8_t to = 50;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
|
|
||||||
// Make the DPF
|
//! [eval-interval]
|
||||||
auto before = prg::count();
|
auto [buf0, iter0] = dpf::eval_interval(k0, from, to);
|
||||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
auto [buf1, iter1] = dpf::eval_interval(k1, from, to);
|
||||||
auto after = prg::count();
|
|
||||||
std::cout << "dpf::make_dpf prg invocation: " << (after-before) << "\n";
|
|
||||||
|
|
||||||
// Evaluate the DPF by interval
|
auto it0 = std::begin(iter0);
|
||||||
before = prg::count();
|
auto it1 = std::begin(iter1);
|
||||||
int from = 0, to = 49;
|
for (std::uint8_t x = from; x <= to; ++x, ++it0, ++it1)
|
||||||
auto [buf0, iter0] = dpf::eval_interval(dpf0, from, to);
|
{
|
||||||
auto [buf1, iter1] = dpf::eval_interval(dpf1, from, to);
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
after = prg::count();
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
std::cout << "dpf::eval_interval prg invocation: " << (after-before) << "\n";
|
if (got != expect)
|
||||||
|
{
|
||||||
// Retrieve the original input by iterating over the two buffers
|
std::cerr << "eval_interval\n";
|
||||||
std::vector<bool> result;
|
return 1;
|
||||||
for (size_t i = from; i < to+1; ++i) {
|
|
||||||
bool item1 = buf0[i];
|
|
||||||
bool item2 = buf1[i];
|
|
||||||
result.push_back(item1 ^ item2);
|
|
||||||
if (item1 ^ item2) y=i;
|
|
||||||
}
|
}
|
||||||
// Print out the XOR interval
|
|
||||||
for (const auto& item : result) {
|
|
||||||
std::cout << static_cast<bool>(item);
|
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
//! [eval-interval]
|
||||||
if (y == x) std::cout << "The orginal value is: " << x << std::endl;
|
if (it0 != std::end(iter0) || it1 != std::end(iter1))
|
||||||
else std::cout << "The evaluated inputs did not match the original value." << std::endl;
|
{
|
||||||
|
std::cerr << "eval_interval length\n";
|
||||||
std::cout << "Total PRG invocation: " << prg::count() << std::endl;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << dpf::reconstruct(*std::begin(iter0), *std::begin(iter1)) << "\n";
|
||||||
|
(void)buf0;
|
||||||
|
(void)buf1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,54 @@
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
int main(int arc, char * argv[])
|
/// One input. `*eval_point` is that party's share of output 0.
|
||||||
|
/// Reconstruct with `dpf::reconstruct` (leaf outputs are subtractive).
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
uint16_t x = 42;
|
const std::uint8_t alpha = 42;
|
||||||
auto [dpf0, dpf1] = dpf::make_dpf(x);
|
const std::uint64_t beta = 7;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
|
using key_t = dpf::unwrap_party_key_t<std::decay_t<decltype(k0)>>;
|
||||||
|
|
||||||
auto res = dpf::eval_point(dpf0, x);
|
//! [eval-point]
|
||||||
|
auto y0 = *dpf::eval_point(k0, alpha);
|
||||||
|
auto y1 = *dpf::eval_point(k1, alpha);
|
||||||
|
std::uint64_t opened = dpf::reconstruct(y0, y1);
|
||||||
|
//! [eval-point]
|
||||||
|
if (opened != beta)
|
||||||
|
{
|
||||||
|
std::cerr << "eval_point at the programmed input\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << *dpf::eval_point(dpf0, 41) << " ^ " << *dpf::eval_point(dpf1, 41) << " = " << (*dpf::eval_point(dpf0, 41) ^ *dpf::eval_point(dpf1, 41)) << "\n"; // = 0
|
auto off0 = *dpf::eval_point(k0, std::uint8_t{41});
|
||||||
std::cout << *dpf::eval_point(dpf0, x) << " ^ " << *dpf::eval_point(dpf1, x) << " = " << (*dpf::eval_point(dpf0, x) ^ *dpf::eval_point(dpf1, x)) << "\n"; // = 1
|
auto off1 = *dpf::eval_point(k1, std::uint8_t{41});
|
||||||
std::cout << *dpf::eval_point(dpf0, 43) << " ^ " << *dpf::eval_point(dpf1, 43) << " = " << (*dpf::eval_point(dpf0, 43) ^ *dpf::eval_point(dpf1, 43)) << "\n"; // = 0
|
if (dpf::reconstruct(off0, off1) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "eval_point off the programmed input\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [eval-point-memo]
|
||||||
|
// One mutable memoizer per key. Nearby points reuse the common prefix.
|
||||||
|
auto path0 = dpf::make_basic_path_memoizer<key_t>();
|
||||||
|
auto path1 = dpf::make_basic_path_memoizer<key_t>();
|
||||||
|
for (int x = 40; x <= 44; ++x)
|
||||||
|
{
|
||||||
|
auto s0 = *dpf::eval_point(k0, static_cast<std::uint8_t>(x), path0);
|
||||||
|
auto s1 = *dpf::eval_point(k1, static_cast<std::uint8_t>(x), path1);
|
||||||
|
std::uint64_t got = dpf::reconstruct(s0, s1);
|
||||||
|
std::uint64_t expect = (static_cast<std::uint8_t>(x) == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "path memoizer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [eval-point-memo]
|
||||||
|
|
||||||
|
std::cout << opened << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,54 +1,114 @@
|
||||||
#include <chrono>
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
using std::chrono::high_resolution_clock;
|
/// A sorted point list. `return_output_only_tag_` stores one share per point.
|
||||||
using std::chrono::duration_cast;
|
/// A `sequence_recipe` compiled from that list is reusable across keys.
|
||||||
using std::chrono::duration;
|
int main()
|
||||||
using std::chrono::milliseconds;
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
|
||||||
{
|
{
|
||||||
using input_type = uint8_t;
|
const std::uint8_t alpha = 42;
|
||||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
const std::uint64_t beta = 7;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
|
using key_t = dpf::unwrap_party_key_t<std::decay_t<decltype(k0)>>;
|
||||||
|
|
||||||
constexpr int N = 50;
|
//! [eval-sequence]
|
||||||
std::array<input_type, N> keys{};
|
// Nondecreasing. An unsorted range throws std::runtime_error.
|
||||||
for(int i=0; i<N; i++) keys[i] = i; // Create an array of keys
|
std::array<std::uint8_t, 5> points{1, 7, 42, 100, 200};
|
||||||
|
|
||||||
// eval_sequence with recipe
|
auto [buf0, iter0] = dpf::eval_sequence(k0, points.begin(), points.end(),
|
||||||
input_type x = 42;
|
dpf::return_output_only_tag_{});
|
||||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x); // First DPF to be able to create the recipe
|
auto [buf1, iter1] = dpf::eval_sequence(k1, points.begin(), points.end(),
|
||||||
auto t1 = high_resolution_clock::now(); // To measure the time of execution
|
dpf::return_output_only_tag_{});
|
||||||
auto before = prg::count(); // To count the number of PRG invocations
|
|
||||||
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(keys), std::end(keys)); // Create a recipe
|
auto it0 = std::begin(iter0);
|
||||||
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(keys), std::end(keys)); // Create a recipe
|
auto it1 = std::begin(iter1);
|
||||||
for (int i=0; i<N; i++)
|
for (std::uint8_t x : points)
|
||||||
{
|
{
|
||||||
auto [dpf00, dpf11] = dpf::make_dpf<prg>(i); // Make 50 DPFs
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
dpf::eval_sequence(dpf0, recipe0); // Evaluate the DPFs with the recipe
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
dpf::eval_sequence(dpf1, recipe0); // Evaluate the DPFs with the recipe
|
if (got != expect)
|
||||||
}
|
|
||||||
auto after = prg::count(); // Count the number of PRG invocations
|
|
||||||
std::cout << "dpf::eval_sequence with recipe " << (after-before) << "\n";
|
|
||||||
|
|
||||||
// eval_sequence without the recipe
|
|
||||||
auto t2 = high_resolution_clock::now();
|
|
||||||
duration<double, std::milli> ms_double = t2 - t1;
|
|
||||||
std::cout << "Time of execution: " << ms_double.count() << "ms\n";
|
|
||||||
auto t3 = high_resolution_clock::now();
|
|
||||||
before = prg::count();
|
|
||||||
for (int i=0; i<N; i++)
|
|
||||||
{
|
{
|
||||||
auto [dpf00, dpf11] = dpf::make_dpf<prg>(i);
|
std::cerr << "eval_sequence\n";
|
||||||
dpf::eval_sequence(dpf00, std::begin(keys), std::end(keys));
|
return 1;
|
||||||
dpf::eval_sequence(dpf11, std::begin(keys), std::end(keys));
|
|
||||||
}
|
}
|
||||||
after = prg::count();
|
++it0;
|
||||||
std::cout << "dpf::eval_sequence used " << (after-before) << "\n";
|
++it1;
|
||||||
auto t4 = high_resolution_clock::now();
|
}
|
||||||
duration<double, std::milli> ms_double2 = t4 - t3;
|
//! [eval-sequence]
|
||||||
std::cout << "Time of execution with the memoizers: " << ms_double2.count() << "ms\n";
|
|
||||||
|
|
||||||
|
//! [eval-sequence-recipe]
|
||||||
|
// The recipe is a property of the point list and the key's input type.
|
||||||
|
// Bind the memoizer to this recipe object and pass that same object back.
|
||||||
|
auto recipe = dpf::make_sequence_recipe<key_t>(points.begin(), points.end());
|
||||||
|
auto memo0 = dpf::make_double_space_sequence_memoizer<key_t>(recipe);
|
||||||
|
auto memo1 = dpf::make_double_space_sequence_memoizer<key_t>(recipe);
|
||||||
|
auto sbuf0 = dpf::make_output_buffer_for_recipe_subsequence(k0, recipe,
|
||||||
|
dpf::return_output_only_tag_{});
|
||||||
|
auto sbuf1 = dpf::make_output_buffer_for_recipe_subsequence(k1, recipe,
|
||||||
|
dpf::return_output_only_tag_{});
|
||||||
|
auto seq0 = dpf::eval_sequence(k0, recipe, sbuf0, memo0,
|
||||||
|
dpf::return_output_only_tag_{});
|
||||||
|
auto seq1 = dpf::eval_sequence(k1, recipe, sbuf1, memo1,
|
||||||
|
dpf::return_output_only_tag_{});
|
||||||
|
//! [eval-sequence-recipe]
|
||||||
|
|
||||||
|
// Omitting the memoizer allocates a double-space workspace for that call.
|
||||||
|
seq0 = dpf::eval_sequence(k0, recipe, sbuf0, dpf::return_output_only_tag_{});
|
||||||
|
seq1 = dpf::eval_sequence(k1, recipe, sbuf1, dpf::return_output_only_tag_{});
|
||||||
|
it0 = std::begin(seq0);
|
||||||
|
it1 = std::begin(seq1);
|
||||||
|
for (std::uint8_t x : points)
|
||||||
|
{
|
||||||
|
if (dpf::reconstruct(*it0, *it1) != ((x == alpha) ? beta : 0))
|
||||||
|
{
|
||||||
|
std::cerr << "eval_sequence recipe, default memoizer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
++it0;
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
seq0 = dpf::eval_sequence(k0, recipe, sbuf0, memo0, dpf::return_output_only_tag_{});
|
||||||
|
seq1 = dpf::eval_sequence(k1, recipe, sbuf1, memo1, dpf::return_output_only_tag_{});
|
||||||
|
|
||||||
|
it0 = std::begin(seq0);
|
||||||
|
it1 = std::begin(seq1);
|
||||||
|
for (std::uint8_t x : points)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "eval_sequence recipe\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
++it0;
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same recipe, same memoizers, same buffers: a second key overwrites them.
|
||||||
|
auto [k0b, k1b] = dpf::make_dpf(std::uint8_t{100}, std::uint64_t{9});
|
||||||
|
seq0 = dpf::eval_sequence(k0b, recipe, sbuf0, memo0, dpf::return_output_only_tag_{});
|
||||||
|
seq1 = dpf::eval_sequence(k1b, recipe, sbuf1, memo1, dpf::return_output_only_tag_{});
|
||||||
|
it0 = std::begin(seq0);
|
||||||
|
it1 = std::begin(seq1);
|
||||||
|
for (std::uint8_t x : points)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == 100) ? 9 : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "eval_sequence recipe reuse\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
++it0;
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << beta << "\n";
|
||||||
|
(void)buf0;
|
||||||
|
(void)buf1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,50 +1,128 @@
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
using std::chrono::high_resolution_clock;
|
/// Memoizers are workspaces of interior nodes. Pass a mutable lvalue.
|
||||||
using std::chrono::duration_cast;
|
/// A temporary (including the default argument) cannot remember a prefix.
|
||||||
using std::chrono::duration;
|
int main()
|
||||||
using std::chrono::milliseconds;
|
|
||||||
|
|
||||||
int main(int arc, char * argv[])
|
|
||||||
{
|
{
|
||||||
// Making the DPF with an integer value
|
const std::uint8_t alpha = 42;
|
||||||
uint16_t x = 42;
|
const std::uint64_t beta = 7;
|
||||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
using key_t = dpf::unwrap_party_key_t<std::decay_t<decltype(k0)>>;
|
||||||
|
|
||||||
// Evaluating the DPF and counting how much it cost without memoizers
|
//! [path-memoizer]
|
||||||
auto t1 = high_resolution_clock::now();
|
auto path0 = dpf::make_basic_path_memoizer<key_t>();
|
||||||
auto before = prg::count();
|
auto path1 = dpf::make_basic_path_memoizer<key_t>();
|
||||||
for (int i = 0; i<1024*1024; i++)
|
for (int x = 0; x < 256; ++x)
|
||||||
{
|
{
|
||||||
dpf::eval_point(dpf0, i);
|
auto y0 = *dpf::eval_point(k0, static_cast<std::uint8_t>(x), path0);
|
||||||
|
auto y1 = *dpf::eval_point(k1, static_cast<std::uint8_t>(x), path1);
|
||||||
}
|
std::uint64_t got = dpf::reconstruct(y0, y1);
|
||||||
// Printing out the results
|
std::uint64_t expect = (static_cast<std::uint8_t>(x) == alpha) ? beta : 0;
|
||||||
auto after = prg::count();
|
if (got != expect)
|
||||||
std::cout << "Without memoizers: " << "\n";
|
|
||||||
std::cout << "PRG invocation: " << after-before << "\n";
|
|
||||||
auto t2 = high_resolution_clock::now();
|
|
||||||
duration<double, std::milli> ms_double = t2 - t1;
|
|
||||||
std::cout << "Time of execution: " << ms_double.count() << "ms\n";
|
|
||||||
|
|
||||||
// Evaluating the DPF and counting how much it cost with memoizers
|
|
||||||
auto t3 = high_resolution_clock::now();
|
|
||||||
before = prg::count();
|
|
||||||
auto path = dpf::make_basic_path_memoizer(dpf0);
|
|
||||||
for (int i = 0; i<1024*1024; i++)
|
|
||||||
{
|
{
|
||||||
dpf::eval_point(dpf0, i, path);
|
std::cerr << "basic_path_memoizer\n";
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
// Printing out the results
|
}
|
||||||
after = prg::count();
|
//! [path-memoizer]
|
||||||
std::cout << "With memoizers: " << "\n";
|
|
||||||
std::cout << "PRG invocation: " << after-before << "\n";
|
|
||||||
auto t4 = high_resolution_clock::now();
|
|
||||||
duration<double, std::milli> ms_double2 = t4 - t3;
|
|
||||||
std::cout << "Time of execution with the memoizers: " << ms_double2.count() << "ms\n";
|
|
||||||
|
|
||||||
|
// One node, no prefix reuse. Correct for a single query.
|
||||||
|
auto once0 = dpf::make_nonmemoizing_path_memoizer<key_t>();
|
||||||
|
auto once1 = dpf::make_nonmemoizing_path_memoizer<key_t>();
|
||||||
|
if (dpf::reconstruct(*dpf::eval_point(k0, alpha, once0),
|
||||||
|
*dpf::eval_point(k1, alpha, once1)) != beta)
|
||||||
|
{
|
||||||
|
std::cerr << "nonmemoizing_path_memoizer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [interval-memoizer]
|
||||||
|
const std::uint8_t from = 40;
|
||||||
|
const std::uint8_t to = 50;
|
||||||
|
// Sized for [from, to]. A wider interval throws std::length_error.
|
||||||
|
// `make_full_tree_interval_memoizer` keeps every level instead of two.
|
||||||
|
auto memo0 = dpf::make_basic_interval_memoizer<key_t>(from, to);
|
||||||
|
auto memo1 = dpf::make_basic_interval_memoizer<key_t>(from, to);
|
||||||
|
auto [ibuf0, i0] = dpf::eval_interval(k0, from, to, memo0);
|
||||||
|
auto [ibuf1, i1] = dpf::eval_interval(k1, from, to, memo1);
|
||||||
|
//! [interval-memoizer]
|
||||||
|
|
||||||
|
auto it0 = std::begin(i0);
|
||||||
|
auto it1 = std::begin(i1);
|
||||||
|
for (std::uint8_t x = from; x <= to; ++x, ++it0, ++it1)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "basic_interval_memoizer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A different key rebuilds into the same memoizer.
|
||||||
|
auto [k0b, k1b] = dpf::make_dpf(std::uint8_t{44}, std::uint64_t{9});
|
||||||
|
std::tie(ibuf0, i0) = dpf::eval_interval(k0b, from, to, memo0);
|
||||||
|
std::tie(ibuf1, i1) = dpf::eval_interval(k1b, from, to, memo1);
|
||||||
|
it0 = std::begin(i0);
|
||||||
|
it1 = std::begin(i1);
|
||||||
|
for (std::uint8_t x = from; x <= to; ++x, ++it0, ++it1)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == 44) ? 9 : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "interval memoizer reuse\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [sequence-memoizer]
|
||||||
|
std::array<std::uint8_t, 5> points{1, 7, 42, 100, 200};
|
||||||
|
// The memoizer stores a reference to this recipe and checks it by address.
|
||||||
|
auto recipe = dpf::make_sequence_recipe<key_t>(points.begin(), points.end());
|
||||||
|
auto seq0 = dpf::make_inplace_reversing_sequence_memoizer<key_t>(recipe);
|
||||||
|
auto seq1 = dpf::make_inplace_reversing_sequence_memoizer<key_t>(recipe);
|
||||||
|
auto [sbuf0, s0] = dpf::eval_sequence(k0, recipe, seq0, dpf::return_output_only_tag_{});
|
||||||
|
auto [sbuf1, s1] = dpf::eval_sequence(k1, recipe, seq1, dpf::return_output_only_tag_{});
|
||||||
|
//! [sequence-memoizer]
|
||||||
|
|
||||||
|
it0 = std::begin(s0);
|
||||||
|
it1 = std::begin(s1);
|
||||||
|
for (std::uint8_t x : points)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "sequence memoizer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
++it0;
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tie(sbuf0, s0) = dpf::eval_sequence(k0b, recipe, seq0, dpf::return_output_only_tag_{});
|
||||||
|
std::tie(sbuf1, s1) = dpf::eval_sequence(k1b, recipe, seq1, dpf::return_output_only_tag_{});
|
||||||
|
it0 = std::begin(s0);
|
||||||
|
it1 = std::begin(s1);
|
||||||
|
for (std::uint8_t x : points)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == 44) ? 9 : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "sequence memoizer reuse\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
++it0;
|
||||||
|
++it1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << beta << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,82 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "dpf.hpp"
|
#include "dpf.hpp"
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
/// Output buffers are move-only. `eval_interval` takes the buffer by
|
||||||
|
/// non-const reference, so name it. The iterable points into that buffer;
|
||||||
|
/// read it only while the buffer is still alive, and only over the points
|
||||||
|
/// the iterable covers.
|
||||||
|
int main()
|
||||||
{
|
{
|
||||||
|
const std::uint8_t alpha = 42;
|
||||||
|
const std::uint64_t beta = 7;
|
||||||
|
const std::uint8_t from = 40;
|
||||||
|
const std::uint8_t to = 50;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||||
|
using key_t = dpf::unwrap_party_key_t<std::decay_t<decltype(k0)>>;
|
||||||
|
|
||||||
|
//! [output-buffer]
|
||||||
|
auto buf0 = dpf::make_output_buffer_for_interval(k0, from, to);
|
||||||
|
auto buf1 = dpf::make_output_buffer_for_interval(k1, from, to);
|
||||||
|
auto memo0 = dpf::make_basic_interval_memoizer<key_t>(from, to);
|
||||||
|
auto memo1 = dpf::make_basic_interval_memoizer<key_t>(from, to);
|
||||||
|
|
||||||
|
auto iter0 = dpf::eval_interval(k0, from, to, buf0, memo0);
|
||||||
|
auto iter1 = dpf::eval_interval(k1, from, to, buf1, memo1);
|
||||||
|
|
||||||
|
auto it0 = std::begin(iter0);
|
||||||
|
auto it1 = std::begin(iter1);
|
||||||
|
for (std::uint8_t x = from; x <= to; ++x, ++it0, ++it1)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "output buffer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [output-buffer]
|
||||||
|
|
||||||
|
// The next evaluation overwrites the same slots.
|
||||||
|
auto [k0b, k1b] = dpf::make_dpf(std::uint8_t{44}, std::uint64_t{9});
|
||||||
|
iter0 = dpf::eval_interval(k0b, from, to, buf0, memo0);
|
||||||
|
iter1 = dpf::eval_interval(k1b, from, to, buf1, memo1);
|
||||||
|
it0 = std::begin(iter0);
|
||||||
|
it1 = std::begin(iter1);
|
||||||
|
for (std::uint8_t x = from; x <= to; ++x, ++it0, ++it1)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (x == 44) ? 9 : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "output buffer reuse\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [output-buffer-full]
|
||||||
|
auto full0 = dpf::make_output_buffer_for_full(k0);
|
||||||
|
auto full1 = dpf::make_output_buffer_for_full(k1);
|
||||||
|
auto fmemo0 = dpf::make_basic_full_memoizer<key_t>();
|
||||||
|
auto fmemo1 = dpf::make_basic_full_memoizer<key_t>();
|
||||||
|
auto f0 = dpf::eval_full(k0, full0, fmemo0);
|
||||||
|
auto f1 = dpf::eval_full(k1, full1, fmemo1);
|
||||||
|
//! [output-buffer-full]
|
||||||
|
it0 = std::begin(f0);
|
||||||
|
it1 = std::begin(f1);
|
||||||
|
for (int x = 0; x < 256; ++x, ++it0, ++it1)
|
||||||
|
{
|
||||||
|
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||||
|
std::uint64_t expect = (static_cast<std::uint8_t>(x) == alpha) ? beta : 0;
|
||||||
|
if (got != expect)
|
||||||
|
{
|
||||||
|
std::cerr << "full output buffer\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << beta << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -116,4 +116,6 @@
|
||||||
|
|
||||||
#include "dpf/uint256_t.hpp"
|
#include "dpf/uint256_t.hpp"
|
||||||
|
|
||||||
|
#include "dpf/interval.hpp"
|
||||||
|
|
||||||
#endif // LIBDPF_INCLUDE_DPF_HPP__
|
#endif // LIBDPF_INCLUDE_DPF_HPP__
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@ class advice_bit_iterable_const_iterator
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
using node_type = typename iterator_traits::value_type;
|
using node_type = typename iterator_traits::value_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr
|
constexpr
|
||||||
explicit advice_bit_iterable_const_iterator(const wrapped_type & it) noexcept
|
explicit advice_bit_iterable_const_iterator(const wrapped_type & it) noexcept
|
||||||
|
|
@ -213,28 +214,33 @@ class advice_bit_iterable_const_iterator
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
advice_bit_iterable_const_iterator operator+(std::size_t n) const noexcept
|
advice_bit_iterable_const_iterator operator+(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return advice_bit_iterable_const_iterator(it_ + n);
|
return advice_bit_iterable_const_iterator(it_ + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
advice_bit_iterable_const_iterator & operator-=(std::size_t n) noexcept
|
advice_bit_iterable_const_iterator & operator-=(std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
it_ -= n;
|
it_ -= n;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
advice_bit_iterable_const_iterator operator-(std::size_t n) const noexcept
|
advice_bit_iterable_const_iterator operator-(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return advice_bit_iterable_const_iterator(it_ - n);
|
return advice_bit_iterable_const_iterator(it_ - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
difference_type
|
difference_type
|
||||||
operator-(advice_bit_iterable_const_iterator rhs) const noexcept
|
operator-(advice_bit_iterable_const_iterator rhs) const noexcept
|
||||||
{
|
{
|
||||||
return it_ - rhs.it_;
|
return it_ - rhs.it_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference operator[](std::size_t i) const noexcept
|
reference operator[](std::size_t i) const noexcept
|
||||||
{
|
{
|
||||||
return bit(it_ + i);
|
return bit(it_ + i);
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class aligned_allocator
|
||||||
template <typename Pointer>
|
template <typename Pointer>
|
||||||
struct deleter
|
struct deleter
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void operator()(Pointer p) const noexcept { free(p); }
|
constexpr void operator()(Pointer p) const noexcept { free(p); }
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
|
|
@ -125,6 +126,7 @@ class aligned_allocator
|
||||||
/// @note This function returns the maximum number of elements that can
|
/// @note This function returns the maximum number of elements that can
|
||||||
/// be allocated, not the maximum allocation size in bytes
|
/// be allocated, not the maximum allocation size in bytes
|
||||||
/// @return The maximum supported allocation size.
|
/// @return The maximum supported allocation size.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr size_type max_size() const noexcept
|
constexpr size_type max_size() const noexcept
|
||||||
{
|
{
|
||||||
return std::numeric_limits<size_type>::max() / sizeof(value_type);
|
return std::numeric_limits<size_type>::max() / sizeof(value_type);
|
||||||
|
|
|
||||||
|
|
@ -166,17 +166,22 @@ class wire
|
||||||
friend class session<Ring>;
|
friend class session<Ring>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr wire() noexcept = default;
|
constexpr wire() noexcept = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr std::uint32_t id() const noexcept { return id_; }
|
constexpr std::uint32_t id() const noexcept { return id_; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr session<Ring> * owner() const noexcept { return sess_; }
|
constexpr session<Ring> * owner() const noexcept { return sess_; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator==(wire a, wire b) noexcept
|
friend bool operator==(wire a, wire b) noexcept
|
||||||
{
|
{
|
||||||
return a.sess_ == b.sess_ && a.id_ == b.id_;
|
return a.sess_ == b.sess_ && a.id_ == b.id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator!=(wire a, wire b) noexcept
|
friend bool operator!=(wire a, wire b) noexcept
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
|
|
@ -240,13 +245,16 @@ public:
|
||||||
static constexpr std::uint32_t mono_role_base = 0x40000000u;
|
static constexpr std::uint32_t mono_role_base = 0x40000000u;
|
||||||
static constexpr std::uint32_t dot_role_base = 0x80000000u;
|
static constexpr std::uint32_t dot_role_base = 0x80000000u;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr std::uint32_t wire_role(std::uint32_t id) noexcept { return id; }
|
static constexpr std::uint32_t wire_role(std::uint32_t id) noexcept { return id; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr std::uint32_t mono_role(std::uint32_t i) noexcept
|
static constexpr std::uint32_t mono_role(std::uint32_t i) noexcept
|
||||||
{
|
{
|
||||||
return mono_role_base + i;
|
return mono_role_base + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr std::uint32_t dot_role(std::uint32_t gate) noexcept
|
static constexpr std::uint32_t dot_role(std::uint32_t gate) noexcept
|
||||||
{
|
{
|
||||||
return dot_role_base + gate;
|
return dot_role_base + gate;
|
||||||
|
|
@ -255,6 +263,7 @@ public:
|
||||||
/// Fused within-polynomial λ combinations (Appendix E groupings).
|
/// Fused within-polynomial λ combinations (Appendix E groupings).
|
||||||
static constexpr std::uint32_t bundle_role_base = 0xC0000000u;
|
static constexpr std::uint32_t bundle_role_base = 0xC0000000u;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr std::uint32_t bundle_role(std::uint32_t i) noexcept
|
static constexpr std::uint32_t bundle_role(std::uint32_t i) noexcept
|
||||||
{
|
{
|
||||||
return bundle_role_base + i;
|
return bundle_role_base + i;
|
||||||
|
|
@ -268,6 +277,7 @@ public:
|
||||||
: lanes_(std::move(seed), window)
|
: lanes_(std::move(seed), window)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const seed_type & seed() const noexcept { return lanes_.seed(); }
|
const seed_type & seed() const noexcept { return lanes_.seed(); }
|
||||||
|
|
||||||
Ring blind(std::uint32_t role, std::uint64_t index) const
|
Ring blind(std::uint32_t role, std::uint64_t index) const
|
||||||
|
|
@ -822,11 +832,13 @@ public:
|
||||||
return wires_[check(w)].ready_round;
|
return wires_[check(w)].ready_round;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t wire_count() const noexcept { return wires_.size(); }
|
std::size_t wire_count() const noexcept { return wires_.size(); }
|
||||||
|
|
||||||
/// Product shares beyond the per-wire blinds: subset monomials from
|
/// Product shares beyond the per-wire blinds: subset monomials from
|
||||||
/// `product` gates, plus one fused bundle per public-δ class in a
|
/// `product` gates, plus one fused bundle per public-δ class in a
|
||||||
/// polynomial (Appendix E). A lone mask is not counted.
|
/// polynomial (Appendix E). A lone mask is not counted.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t monomial_count() const noexcept
|
std::size_t monomial_count() const noexcept
|
||||||
{
|
{
|
||||||
return monos_.size() + bundles_.size();
|
return monos_.size() + bundles_.size();
|
||||||
|
|
|
||||||
|
|
@ -183,12 +183,14 @@ operator>>(std::basic_istream<CharT, Traits> & is, dpf::bit & value)
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr dpf::bit operator+(dpf::bit lhs, dpf::bit rhs) noexcept
|
inline constexpr dpf::bit operator+(dpf::bit lhs, dpf::bit rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief GF(2) subtraction. Identical to `operator+`.
|
/// @brief GF(2) subtraction. Identical to `operator+`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr dpf::bit operator-(dpf::bit lhs, dpf::bit rhs) noexcept
|
inline constexpr dpf::bit operator-(dpf::bit lhs, dpf::bit rhs) noexcept
|
||||||
{
|
{
|
||||||
return lhs + rhs;
|
return lhs + rhs;
|
||||||
|
|
@ -216,6 +218,7 @@ struct packed_lane_bits<dpf::bit>
|
||||||
template <>
|
template <>
|
||||||
struct make_from_integral_value<dpf::bit>
|
struct make_from_integral_value<dpf::bit>
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr dpf::bit operator()(bool val) const noexcept
|
constexpr dpf::bit operator()(bool val) const noexcept
|
||||||
{
|
{
|
||||||
return val ? dpf::bit::one : dpf::bit::zero;
|
return val ? dpf::bit::one : dpf::bit::zero;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename Word>
|
template <typename Word>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void check_one_bit(Word mask) noexcept
|
constexpr void check_one_bit(Word mask) noexcept
|
||||||
{
|
{
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
|
@ -135,6 +136,7 @@ class bit_array_base
|
||||||
inline constexpr bit_array_base(const bit_array_base &) = default;
|
inline constexpr bit_array_base(const bit_array_base &) = default;
|
||||||
|
|
||||||
/// @brief default move constructor
|
/// @brief default move constructor
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr bit_array_base(bit_array_base &&) noexcept = default;
|
inline constexpr bit_array_base(bit_array_base &&) noexcept = default;
|
||||||
|
|
||||||
/// @brief default destructor
|
/// @brief default destructor
|
||||||
|
|
@ -145,6 +147,7 @@ class bit_array_base
|
||||||
bit_array_base & operator=(const bit_array_base &) = default;
|
bit_array_base & operator=(const bit_array_base &) = default;
|
||||||
|
|
||||||
/// @brief defaulted move assignment
|
/// @brief defaulted move assignment
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
bit_array_base & operator=(bit_array_base &&) noexcept = default;
|
bit_array_base & operator=(bit_array_base &&) noexcept = default;
|
||||||
|
|
||||||
|
|
@ -163,6 +166,7 @@ class bit_array_base
|
||||||
/// @note Does not perform bounds checking; behaviour is undefined if
|
/// @note Does not perform bounds checking; behaviour is undefined if
|
||||||
/// `pos` is out of bounds
|
/// `pos` is out of bounds
|
||||||
/// @return `data()[pos]`
|
/// @return `data()[pos]`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr word_type data(size_type pos) const noexcept
|
constexpr word_type data(size_type pos) const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +191,7 @@ class bit_array_base
|
||||||
return data()[pos];
|
return data()[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type data_length() const noexcept { return derived_from_this()->data_length(); }
|
constexpr size_type data_length() const noexcept { return derived_from_this()->data_length(); }
|
||||||
|
|
||||||
|
|
@ -261,6 +266,7 @@ class bit_array_base
|
||||||
/// @{
|
/// @{
|
||||||
/// @returns iterator to the first element
|
/// @returns iterator to the first element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr iterator begin() noexcept
|
constexpr iterator begin() noexcept
|
||||||
{
|
{
|
||||||
auto *p = data();
|
auto *p = data();
|
||||||
|
|
@ -269,6 +275,7 @@ class bit_array_base
|
||||||
}
|
}
|
||||||
/// @returns iterator to the first element
|
/// @returns iterator to the first element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const_iterator begin() const noexcept
|
constexpr const_iterator begin() const noexcept
|
||||||
{
|
{
|
||||||
auto *p = data();
|
auto *p = data();
|
||||||
|
|
@ -277,6 +284,7 @@ class bit_array_base
|
||||||
}
|
}
|
||||||
/// @returns iterator to the first element
|
/// @returns iterator to the first element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const_iterator cbegin() const noexcept
|
constexpr const_iterator cbegin() const noexcept
|
||||||
{
|
{
|
||||||
return begin();
|
return begin();
|
||||||
|
|
@ -287,6 +295,7 @@ class bit_array_base
|
||||||
/// @{
|
/// @{
|
||||||
/// @returns iterator to the element following the last element
|
/// @returns iterator to the element following the last element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr iterator end() noexcept
|
constexpr iterator end() noexcept
|
||||||
{
|
{
|
||||||
auto *p = data();
|
auto *p = data();
|
||||||
|
|
@ -296,6 +305,7 @@ class bit_array_base
|
||||||
}
|
}
|
||||||
/// @returns iterator to the element following the last element
|
/// @returns iterator to the element following the last element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const_iterator end() const noexcept
|
constexpr const_iterator end() const noexcept
|
||||||
{
|
{
|
||||||
auto *p = data();
|
auto *p = data();
|
||||||
|
|
@ -305,6 +315,7 @@ class bit_array_base
|
||||||
}
|
}
|
||||||
/// @returns iterator to the element following the last element
|
/// @returns iterator to the element following the last element
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const_iterator cend() const noexcept
|
constexpr const_iterator cend() const noexcept
|
||||||
{
|
{
|
||||||
return end();
|
return end();
|
||||||
|
|
@ -326,6 +337,7 @@ class bit_array_base
|
||||||
/// @details checks if all bits are set to `true`
|
/// @details checks if all bits are set to `true`
|
||||||
/// @return `true` if all of the bits are set to `true`, otherwise `false`
|
/// @return `true` if all of the bits are set to `true`, otherwise `false`
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool all() const noexcept
|
bool all() const noexcept
|
||||||
{
|
{
|
||||||
if (size() == 0) return true;
|
if (size() == 0) return true;
|
||||||
|
|
@ -351,6 +363,7 @@ class bit_array_base
|
||||||
/// `true`, otherwise `false`
|
/// `true`, otherwise `false`
|
||||||
/// @complexity `O(last-first)`
|
/// @complexity `O(last-first)`
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool all(Iterator first, Iterator last) const noexcept
|
bool all(Iterator first, Iterator last) const noexcept
|
||||||
{
|
{
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
@ -365,6 +378,7 @@ class bit_array_base
|
||||||
/// @details checks if any bits are set to `true`
|
/// @details checks if any bits are set to `true`
|
||||||
/// @return `true` if any of the bits are set to `true`, otherwise `false`
|
/// @return `true` if any of the bits are set to `true`, otherwise `false`
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool any() const noexcept
|
bool any() const noexcept
|
||||||
{
|
{
|
||||||
const size_type n = data_length();
|
const size_type n = data_length();
|
||||||
|
|
@ -386,6 +400,7 @@ class bit_array_base
|
||||||
/// `true`, otherwise `false`
|
/// `true`, otherwise `false`
|
||||||
/// @complexity `O(last-first)`
|
/// @complexity `O(last-first)`
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool any(Iterator first, Iterator last) const noexcept
|
bool any(Iterator first, Iterator last) const noexcept
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
@ -400,6 +415,7 @@ class bit_array_base
|
||||||
/// @details checks if none of the bits are set to `true`
|
/// @details checks if none of the bits are set to `true`
|
||||||
/// @return `true` if none of the bits are set to `true`, otherwise `false`
|
/// @return `true` if none of the bits are set to `true`, otherwise `false`
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool none() const noexcept
|
bool none() const noexcept
|
||||||
{
|
{
|
||||||
return !any();
|
return !any();
|
||||||
|
|
@ -412,6 +428,7 @@ class bit_array_base
|
||||||
/// `true`, otherwise `false`
|
/// `true`, otherwise `false`
|
||||||
/// @complexity `O(last-first)`
|
/// @complexity `O(last-first)`
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool none(Iterator first, Iterator last) const noexcept
|
bool none(Iterator first, Iterator last) const noexcept
|
||||||
{
|
{
|
||||||
return !any(first, last);
|
return !any(first, last);
|
||||||
|
|
@ -423,6 +440,7 @@ class bit_array_base
|
||||||
/// @details counts the number of bits that are set to `true`
|
/// @details counts the number of bits that are set to `true`
|
||||||
/// @returns the number of bits set to `true`
|
/// @returns the number of bits set to `true`
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type count() const noexcept
|
size_type count() const noexcept
|
||||||
{
|
{
|
||||||
const size_type n = data_length();
|
const size_type n = data_length();
|
||||||
|
|
@ -443,6 +461,7 @@ class bit_array_base
|
||||||
/// @return the number of bits in the given range that are set to `true`
|
/// @return the number of bits in the given range that are set to `true`
|
||||||
/// @complexity `O(last-first)`
|
/// @complexity `O(last-first)`
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type count(Iterator first, Iterator last) const noexcept
|
size_type count(Iterator first, Iterator last) const noexcept
|
||||||
{
|
{
|
||||||
size_type sum = 0;
|
size_type sum = 0;
|
||||||
|
|
@ -460,6 +479,7 @@ class bit_array_base
|
||||||
/// @details counts the parity of all stored bits
|
/// @details counts the parity of all stored bits
|
||||||
/// @returns the parity of all stored bits
|
/// @returns the parity of all stored bits
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type parity() const noexcept
|
size_type parity() const noexcept
|
||||||
{
|
{
|
||||||
const size_type n = data_length();
|
const size_type n = data_length();
|
||||||
|
|
@ -481,6 +501,7 @@ class bit_array_base
|
||||||
/// @return the parity of all bits in the given range
|
/// @return the parity of all bits in the given range
|
||||||
/// @complexity `O(last-first)`
|
/// @complexity `O(last-first)`
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type parity(Iterator first, Iterator last) const noexcept
|
size_type parity(Iterator first, Iterator last) const noexcept
|
||||||
{
|
{
|
||||||
word_type x = word_type{0};
|
word_type x = word_type{0};
|
||||||
|
|
@ -496,6 +517,7 @@ class bit_array_base
|
||||||
/// @brief returns the number of bits
|
/// @brief returns the number of bits
|
||||||
/// @returns number of bits that the `bit_array_base` holds
|
/// @returns number of bits that the `bit_array_base` holds
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type size() const noexcept
|
constexpr size_type size() const noexcept
|
||||||
|
|
@ -507,6 +529,7 @@ class bit_array_base
|
||||||
/// @{
|
/// @{
|
||||||
/// @brief sets all bits to `true`
|
/// @brief sets all bits to `true`
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void set() noexcept
|
constexpr void set() noexcept
|
||||||
{
|
{
|
||||||
const size_type n = data_length();
|
const size_type n = data_length();
|
||||||
|
|
@ -551,6 +574,7 @@ class bit_array_base
|
||||||
/// @{
|
/// @{
|
||||||
/// @brief sets all bits to `false'
|
/// @brief sets all bits to `false'
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void unset() noexcept
|
constexpr void unset() noexcept
|
||||||
{
|
{
|
||||||
word_type *p = data();
|
word_type *p = data();
|
||||||
|
|
@ -578,6 +602,7 @@ class bit_array_base
|
||||||
/// @{
|
/// @{
|
||||||
/// @brief flips all bits (like `operator~`, but in-place)
|
/// @brief flips all bits (like `operator~`, but in-place)
|
||||||
/// @complexity `O(size())`
|
/// @complexity `O(size())`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void flip() noexcept
|
constexpr void flip() noexcept
|
||||||
{
|
{
|
||||||
const size_type n = data_length();
|
const size_type n = data_length();
|
||||||
|
|
@ -711,6 +736,7 @@ class bit_array_base
|
||||||
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend constexpr dpf::bit operator+(bit_reference lhs, bit_reference rhs) noexcept
|
friend constexpr dpf::bit operator+(bit_reference lhs, bit_reference rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
||||||
|
|
@ -820,6 +846,7 @@ class bit_array_base
|
||||||
|
|
||||||
/// @brief Exchange the bits named by two proxies, including temporaries
|
/// @brief Exchange the bits named by two proxies, including temporaries
|
||||||
/// returned from `operator[]` and `operator*`.
|
/// returned from `operator[]` and `operator*`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend constexpr void swap(bit_reference a, bit_reference b) noexcept
|
friend constexpr void swap(bit_reference a, bit_reference b) noexcept
|
||||||
{
|
{
|
||||||
const bool tmp = static_cast<bool>(a);
|
const bool tmp = static_cast<bool>(a);
|
||||||
|
|
@ -870,6 +897,7 @@ class bit_array_base
|
||||||
static constexpr word_type sentinel = ~word_type(0);
|
static constexpr word_type sentinel = ~word_type(0);
|
||||||
|
|
||||||
/// @brief Low `n` bits set. `n == 0` yields 0. `n >= bits_per_word` yields all ones.
|
/// @brief Low `n` bits set. `n == 0` yields 0. `n >= bits_per_word` yields all ones.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr word_type low_bits_mask(size_type n) noexcept
|
static constexpr word_type low_bits_mask(size_type n) noexcept
|
||||||
{
|
{
|
||||||
if (n == 0) return word_type{0};
|
if (n == 0) return word_type{0};
|
||||||
|
|
@ -878,18 +906,21 @@ class bit_array_base
|
||||||
static_cast<word_type>(~word_type{0}) >> (bits_per_word - n));
|
static_cast<word_type>(~word_type{0}) >> (bits_per_word - n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr size_type pop(word_type w) noexcept
|
static constexpr size_type pop(word_type w) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<size_type>(utils::popcount(w));
|
return static_cast<size_type>(utils::popcount(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Bits strictly below the single set bit in `mask`.
|
/// @brief Bits strictly below the single set bit in `mask`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr word_type bits_below(word_type mask) noexcept
|
static constexpr word_type bits_below(word_type mask) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<word_type>(mask - word_type{1});
|
return static_cast<word_type>(mask - word_type{1});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Bits at and above the single set bit in `mask`.
|
/// @brief Bits at and above the single set bit in `mask`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr word_type bits_at_and_above(word_type mask) noexcept
|
static constexpr word_type bits_at_and_above(word_type mask) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<word_type>(~bits_below(mask));
|
return static_cast<word_type>(~bits_below(mask));
|
||||||
|
|
@ -999,6 +1030,7 @@ class bit_iterator_base
|
||||||
word_type mask_;
|
word_type mask_;
|
||||||
|
|
||||||
/// @brief Singular iterator. Comparable, not dereferenceable.
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr bit_iterator_base() noexcept
|
inline constexpr bit_iterator_base() noexcept
|
||||||
: word_ptr_{nullptr},
|
: word_ptr_{nullptr},
|
||||||
mask_{lsb}
|
mask_{lsb}
|
||||||
|
|
@ -1125,11 +1157,14 @@ class bit_iterator final
|
||||||
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
|
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
|
||||||
|
|
||||||
/// @brief Singular iterator. Comparable, not dereferenceable.
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bit_iterator() noexcept = default;
|
constexpr bit_iterator() noexcept = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
bit_iterator(const bit_iterator &) noexcept = default;
|
bit_iterator(const bit_iterator &) noexcept = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr bit_iterator(bit_iterator &&) noexcept = default;
|
inline constexpr bit_iterator(bit_iterator &&) noexcept = default;
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
|
|
@ -1218,6 +1253,7 @@ class bit_iterator final
|
||||||
return tmp -= amt;
|
return tmp -= amt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend constexpr iterator operator+(difference_type amt, iterator it) noexcept
|
friend constexpr iterator operator+(difference_type amt, iterator it) noexcept
|
||||||
{
|
{
|
||||||
return it + amt;
|
return it + amt;
|
||||||
|
|
@ -1257,6 +1293,7 @@ class const_bit_iterator final
|
||||||
using const_word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::const_word_pointer;
|
using const_word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::const_word_pointer;
|
||||||
|
|
||||||
/// @brief Singular iterator. Comparable, not dereferenceable.
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const_bit_iterator() noexcept = default;
|
constexpr const_bit_iterator() noexcept = default;
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
|
|
@ -1349,6 +1386,7 @@ class const_bit_iterator final
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
const_iterator & operator-=(difference_type amt) noexcept
|
const_iterator & operator-=(difference_type amt) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1356,6 +1394,7 @@ class const_bit_iterator final
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
const_iterator operator+(difference_type amt) const noexcept
|
const_iterator operator+(difference_type amt) const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1363,6 +1402,7 @@ class const_bit_iterator final
|
||||||
return tmp += amt;
|
return tmp += amt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
const_iterator operator-(difference_type amt) const noexcept
|
const_iterator operator-(difference_type amt) const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1370,12 +1410,14 @@ class const_bit_iterator final
|
||||||
return tmp -= amt;
|
return tmp -= amt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend constexpr const_iterator operator+(difference_type amt,
|
friend constexpr const_iterator operator+(difference_type amt,
|
||||||
const_iterator it) noexcept
|
const_iterator it) noexcept
|
||||||
{
|
{
|
||||||
return it + amt;
|
return it + amt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr
|
inline constexpr
|
||||||
const_reference operator[](difference_type i) const noexcept
|
const_reference operator[](difference_type i) const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1402,10 +1444,14 @@ class alignas(utils::max_align_v) static_bit_array final
|
||||||
/// `size()` bits
|
/// `size()` bits
|
||||||
static constexpr size_type data_length_ = utils::quotient_ceiling(Nbits, bits_per_word);
|
static constexpr size_type data_length_ = utils::quotient_ceiling(Nbits, bits_per_word);
|
||||||
public:
|
public:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr static_bit_array(static_bit_array &&) noexcept = default;
|
constexpr static_bit_array(static_bit_array &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr static_bit_array(const static_bit_array &) noexcept = default;
|
constexpr static_bit_array(const static_bit_array &) noexcept = default;
|
||||||
~static_bit_array() = default;
|
~static_bit_array() = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr static_bit_array & operator=(static_bit_array &&) noexcept = default;
|
constexpr static_bit_array & operator=(static_bit_array &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr static_bit_array & operator=(const static_bit_array &) noexcept = default;
|
constexpr static_bit_array & operator=(const static_bit_array &) noexcept = default;
|
||||||
/// @brief constructs a zeroed `static_bit_array` that holds `Nbits` bits
|
/// @brief constructs a zeroed `static_bit_array` that holds `Nbits` bits
|
||||||
inline constexpr static_bit_array()
|
inline constexpr static_bit_array()
|
||||||
|
|
@ -1459,6 +1505,7 @@ class alignas(utils::max_align_v) static_bit_array final
|
||||||
/// @brief returns the number of bits
|
/// @brief returns the number of bits
|
||||||
/// @returns number of bits that the `static_bit_array` holds
|
/// @returns number of bits that the `static_bit_array` holds
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type size() const noexcept
|
constexpr size_type size() const noexcept
|
||||||
|
|
@ -1509,6 +1556,7 @@ class dynamic_bit_array
|
||||||
std::copy_n(other.data_.get(), data_length_ + 1, data_.get());
|
std::copy_n(other.data_.get(), data_length_ + 1, data_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dynamic_bit_array(dynamic_bit_array && other) noexcept
|
dynamic_bit_array(dynamic_bit_array && other) noexcept
|
||||||
: num_bits_{std::exchange(other.num_bits_, 0)},
|
: num_bits_{std::exchange(other.num_bits_, 0)},
|
||||||
data_length_{std::exchange(other.data_length_, 0)},
|
data_length_{std::exchange(other.data_length_, 0)},
|
||||||
|
|
@ -1525,6 +1573,7 @@ class dynamic_bit_array
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dynamic_bit_array & operator=(dynamic_bit_array && other) noexcept
|
dynamic_bit_array & operator=(dynamic_bit_array && other) noexcept
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
|
|
@ -1537,6 +1586,7 @@ class dynamic_bit_array
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
~dynamic_bit_array() noexcept
|
~dynamic_bit_array() noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1595,6 +1645,7 @@ class dynamic_bit_array
|
||||||
/// @brief returns the number of bits
|
/// @brief returns the number of bits
|
||||||
/// @returns number of bits that the `dynamic_bit_array` holds
|
/// @returns number of bits that the `dynamic_bit_array` holds
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type size() const noexcept
|
constexpr size_type size() const noexcept
|
||||||
|
|
@ -1603,6 +1654,8 @@ class dynamic_bit_array
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Store zeros through `volatile` so the wipe is not deleted as a dead store.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
void wipe() noexcept
|
void wipe() noexcept
|
||||||
{
|
{
|
||||||
if (!data_) return;
|
if (!data_) return;
|
||||||
|
|
@ -1620,6 +1673,7 @@ class dynamic_bit_array
|
||||||
|
|
||||||
/// @brief
|
/// @brief
|
||||||
template <typename WordT>
|
template <typename WordT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
|
inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
|
||||||
typename dynamic_bit_array<WordT>::reference rhs) noexcept
|
typename dynamic_bit_array<WordT>::reference rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1630,6 +1684,7 @@ inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
|
||||||
|
|
||||||
template <std::size_t Nbits,
|
template <std::size_t Nbits,
|
||||||
typename WordT>
|
typename WordT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr void swap(typename static_bit_array<Nbits, WordT>::reference lhs,
|
inline constexpr void swap(typename static_bit_array<Nbits, WordT>::reference lhs,
|
||||||
typename static_bit_array<Nbits, WordT>::reference rhs) noexcept
|
typename static_bit_array<Nbits, WordT>::reference rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,9 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bitstring & operator=(const bitstring &) noexcept = default;
|
bitstring & operator=(const bitstring &) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bitstring & operator=(bitstring &&) noexcept = default;
|
bitstring & operator=(bitstring &&) noexcept = default;
|
||||||
~bitstring() = default;
|
~bitstring() = default;
|
||||||
|
|
||||||
|
|
@ -387,6 +389,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
using base::flip;
|
using base::flip;
|
||||||
|
|
||||||
/// @brief Flips every defined bit and clears bits above `Nbits`.
|
/// @brief Flips every defined bit and clears bits above `Nbits`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void flip() noexcept
|
constexpr void flip() noexcept
|
||||||
{
|
{
|
||||||
base::flip();
|
base::flip();
|
||||||
|
|
@ -468,6 +471,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
/// @note Does not perform bounds checking; behaviour is undefined if
|
/// @note Does not perform bounds checking; behaviour is undefined if
|
||||||
/// `pos` is out of bounds
|
/// `pos` is out of bounds
|
||||||
/// @return `data()[pos]`
|
/// @return `data()[pos]`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr word_type data(size_type pos) const noexcept
|
constexpr word_type data(size_type pos) const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -480,6 +484,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
/// @note Does not perform bounds checking; behaviour is undefined if
|
/// @note Does not perform bounds checking; behaviour is undefined if
|
||||||
/// `pos` is out of bounds
|
/// `pos` is out of bounds
|
||||||
/// @return `data()[pos]`
|
/// @return `data()[pos]`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr word_type & data(size_type pos) noexcept
|
constexpr word_type & data(size_type pos) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -488,6 +493,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
|
|
||||||
/// @brief length of the underlying data array
|
/// @brief length of the underlying data array
|
||||||
/// @return the number of elements in the underlying array
|
/// @return the number of elements in the underlying array
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type data_length() const noexcept
|
constexpr size_type data_length() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -497,6 +503,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
/// @brief returns the number of bits
|
/// @brief returns the number of bits
|
||||||
/// @returns number of bits that the `bitstring` holds
|
/// @returns number of bits that the `bitstring` holds
|
||||||
/// @complexity `O(1)`
|
/// @complexity `O(1)`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr size_type size() const noexcept
|
constexpr size_type size() const noexcept
|
||||||
|
|
@ -510,6 +517,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
std::array<word_type, data_length_> data_{};
|
std::array<word_type, data_length_> data_{};
|
||||||
|
|
||||||
/// @brief Mask of the bits that belong to this string in the high word.
|
/// @brief Mask of the bits that belong to this string in the high word.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr word_type defined_high_mask() noexcept
|
static constexpr word_type defined_high_mask() noexcept
|
||||||
{
|
{
|
||||||
constexpr auto rem = Nbits % bits_per_word;
|
constexpr auto rem = Nbits % bits_per_word;
|
||||||
|
|
@ -519,6 +527,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
|
||||||
return static_cast<word_type>((word_type{1} << rem) - word_type{1});
|
return static_cast<word_type>((word_type{1} << rem) - word_type{1});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void clear_unused() noexcept
|
constexpr void clear_unused() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (Nbits % bits_per_word != 0)
|
if constexpr (Nbits % bits_per_word != 0)
|
||||||
|
|
@ -630,6 +639,7 @@ struct countl_zero_symmetric_difference<dpf::bitstring<Nbits, WordT>>
|
||||||
{
|
{
|
||||||
using T = dpf::bitstring<Nbits, WordT>;
|
using T = dpf::bitstring<Nbits, WordT>;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr
|
constexpr
|
||||||
|
|
@ -706,6 +716,7 @@ struct make_from_integral_value<dpf::bitstring<Nbits, WordT>>
|
||||||
using integral_type = std::conditional_t<std::is_void_v<T_integral_type>, simde_uint128, T_integral_type>;
|
using integral_type = std::conditional_t<std::is_void_v<T_integral_type>, simde_uint128, T_integral_type>;
|
||||||
static constexpr auto mod = utils::mod_pow_2<integral_type>{};
|
static constexpr auto mod = utils::mod_pow_2<integral_type>{};
|
||||||
static constexpr auto bits_per_last_word = Nbits % T::bits_per_word;
|
static constexpr auto bits_per_last_word = Nbits % T::bits_per_word;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr dpf::bitstring<Nbits, WordT> operator()(integral_type val) const noexcept
|
constexpr dpf::bitstring<Nbits, WordT> operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
dpf::bitstring<Nbits, WordT> ret;
|
dpf::bitstring<Nbits, WordT> ret;
|
||||||
|
|
@ -731,6 +742,7 @@ struct mod_pow_2<dpf::bitstring<Nbits, WordT>>
|
||||||
using T = dpf::bitstring<Nbits, WordT>;
|
using T = dpf::bitstring<Nbits, WordT>;
|
||||||
static constexpr auto to_int = to_integral_type<T>{};
|
static constexpr auto to_int = to_integral_type<T>{};
|
||||||
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
|
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(T val, std::size_t n) const noexcept
|
std::size_t operator()(T val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod(to_int(val), n);
|
return mod(to_int(val), n);
|
||||||
|
|
@ -1141,14 +1153,23 @@ class numeric_limits<dpf::bitstring<Nbits, WordT>>
|
||||||
= std::numeric_limits<typename dpf::bitstring<Nbits, WordT>::integral_type>::traps;
|
= std::numeric_limits<typename dpf::bitstring<Nbits, WordT>::integral_type>::traps;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> min() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
|
static constexpr dpf::bitstring<Nbits, WordT> min() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> lowest() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
|
static constexpr dpf::bitstring<Nbits, WordT> lowest() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> max() noexcept { return ~dpf::bitstring<Nbits, WordT>{}; }
|
static constexpr dpf::bitstring<Nbits, WordT> max() noexcept { return ~dpf::bitstring<Nbits, WordT>{}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> epsilon() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> epsilon() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> round_error() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> round_error() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> infinity() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> infinity() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> quiet_NaN() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> quiet_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> signaling_NaN() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> signaling_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::bitstring<Nbits, WordT> denorm_min() noexcept { return 0; }
|
static constexpr dpf::bitstring<Nbits, WordT> denorm_min() noexcept { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
475
include/dpf/blocked_dcf.hpp
Normal file
475
include/dpf/blocked_dcf.hpp
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
/// @file dpf/blocked_dcf.hpp
|
||||||
|
/// @brief Blocked-checkpoint comparison: one ring word per block of levels.
|
||||||
|
/// @details The seed spine stays dense. Ring words are published only at a
|
||||||
|
/// public checkpoint schedule. Point eval expands parked siblings
|
||||||
|
/// up to the next checkpoint; a full-domain memoizer already holds
|
||||||
|
/// those nodes. `q` tail bits, when the comparison sets the key
|
||||||
|
/// depth, are a residual table on the node at height `h`.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
|
#include "dpf/dcf.hpp"
|
||||||
|
#include "dpf/path_memoizer.hpp"
|
||||||
|
#include "dpf/twiddle.hpp"
|
||||||
|
#include "dpf/utils.hpp"
|
||||||
|
|
||||||
|
namespace dpf
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace blocked
|
||||||
|
{
|
||||||
|
|
||||||
|
template <std::size_t H, std::size_t B>
|
||||||
|
struct schedule
|
||||||
|
{
|
||||||
|
static constexpr std::size_t count =
|
||||||
|
(B == 0 || H == 0) ? 0 : (H + B - 1) / B;
|
||||||
|
|
||||||
|
static constexpr auto depths = [] {
|
||||||
|
std::array<std::size_t, count == 0 ? 1 : count> cs{};
|
||||||
|
if constexpr (count == 0)
|
||||||
|
return cs;
|
||||||
|
const std::size_t base = H / count;
|
||||||
|
const std::size_t extra = H % count;
|
||||||
|
std::size_t acc = 0;
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
acc += base + (i < extra ? 1 : 0);
|
||||||
|
cs[i] = acc;
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}();
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static constexpr bool contains(std::size_t depth) noexcept
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if (depths[i] == depth)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static constexpr std::size_t index(std::size_t depth) noexcept
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if (depths[i] == depth)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return static_cast<std::size_t>(-1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PRG, typename Node>
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t rho_of(const Node & node, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
auto kids = PRG::eval01(dpf::unset_lo_2bits(node));
|
||||||
|
return dcf_impl::convert_node(kids[0], mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr int control_sign(uint8_t t0, uint8_t t1) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<int>(t0) - static_cast<int>(t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t mul_sgn(int sgn, uint64_t v, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
if (sgn > 0)
|
||||||
|
return v & mask;
|
||||||
|
if (sgn < 0)
|
||||||
|
return dcf_impl::neg_m(v, mask);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Group element `sgn` (`+1`, `-1`, or `0`) used as an `assign_cmp` coefficient.
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t sgn_coeff(int sgn, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
if (sgn > 0)
|
||||||
|
return 1ULL & mask;
|
||||||
|
if (sgn < 0)
|
||||||
|
return dcf_impl::neg_m(1ULL, mask);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PRG, typename Node>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t checkpoint_word(const Node & n0, const Node & n1, uint64_t beta,
|
||||||
|
uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
const int sgn = control_sign(
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n1)));
|
||||||
|
const uint64_t r0 = rho_of<PRG>(n0, mask);
|
||||||
|
const uint64_t r1 = rho_of<PRG>(n1, mask);
|
||||||
|
const uint64_t inner =
|
||||||
|
(beta + dcf_impl::neg_m(r0, mask) + r1) & mask;
|
||||||
|
return mul_sgn(sgn, inner, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Node>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t checkpoint_coeff(const Node & n0, const Node & n1,
|
||||||
|
uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
return sgn_coeff(control_sign(
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n1))), mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PRG, typename Node>
|
||||||
|
HEDLEY_NON_NULL(4)
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
void suffix_masks(const Node & seed, std::size_t q, uint64_t mask,
|
||||||
|
uint64_t * out) noexcept
|
||||||
|
{
|
||||||
|
Node cur[4]{};
|
||||||
|
Node nxt[8]{};
|
||||||
|
cur[0] = seed;
|
||||||
|
std::size_t n = 1;
|
||||||
|
for (std::size_t lvl = 0; lvl < q; ++lvl)
|
||||||
|
{
|
||||||
|
std::size_t m = 0;
|
||||||
|
for (std::size_t i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
auto kids = PRG::eval01(dpf::unset_lo_2bits(cur[i]));
|
||||||
|
nxt[m++] = kids[0];
|
||||||
|
nxt[m++] = kids[1];
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < m; ++i)
|
||||||
|
cur[i] = nxt[i];
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < n; ++i)
|
||||||
|
out[i] = dcf_impl::convert_node(cur[i], mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PRG, typename Node>
|
||||||
|
HEDLEY_NON_NULL(8)
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
void tail_words(const Node & n0, const Node & n1, uint64_t beta, uint64_t mask,
|
||||||
|
bool include_eq, uint64_t suffix, std::size_t q, uint64_t * words,
|
||||||
|
uint64_t * coeffs) noexcept
|
||||||
|
{
|
||||||
|
uint64_t u0[4]{};
|
||||||
|
uint64_t u1[4]{};
|
||||||
|
suffix_masks<PRG>(n0, q, mask, u0);
|
||||||
|
suffix_masks<PRG>(n1, q, mask, u1);
|
||||||
|
const int sgn = control_sign(
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
|
||||||
|
static_cast<uint8_t>(dpf::get_lo_bit(n1)));
|
||||||
|
const uint64_t coeff = sgn_coeff(sgn, mask);
|
||||||
|
const std::size_t n = std::size_t{1} << q;
|
||||||
|
for (std::size_t z = 0; z < n; ++z)
|
||||||
|
{
|
||||||
|
const bool pred = include_eq
|
||||||
|
? (z <= suffix)
|
||||||
|
: (z < suffix);
|
||||||
|
const uint64_t inner =
|
||||||
|
((pred ? beta : 0ULL) + dcf_impl::neg_m(u0[z], mask) + u1[z]) & mask;
|
||||||
|
words[z] = mul_sgn(sgn, inner, mask);
|
||||||
|
if (coeffs != nullptr)
|
||||||
|
coeffs[z] = pred ? coeff : 0ULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t add_membership(uint64_t acc, const typename KeyT::interior_node & node,
|
||||||
|
uint64_t word, uint64_t mask, int party) noexcept
|
||||||
|
{
|
||||||
|
using prg = typename KeyT::interior_prg;
|
||||||
|
const uint8_t t = static_cast<uint8_t>(dpf::get_lo_bit(node));
|
||||||
|
const uint64_t y =
|
||||||
|
(rho_of<prg>(node, mask) + (t ? word : 0ULL)) & mask;
|
||||||
|
return (acc + (party ? dcf_impl::neg_m(y, mask) : y)) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT>
|
||||||
|
uint64_t add_frontier(uint64_t acc, const typename KeyT::interior_node & seed,
|
||||||
|
std::size_t from_depth, std::size_t to_depth, const KeyT & dpf, uint64_t word,
|
||||||
|
uint64_t mask, int party)
|
||||||
|
{
|
||||||
|
using node = typename KeyT::interior_node;
|
||||||
|
std::vector<node> cur;
|
||||||
|
std::vector<node> nxt;
|
||||||
|
cur.push_back(seed);
|
||||||
|
for (std::size_t lvl = from_depth; lvl < to_depth; ++lvl)
|
||||||
|
{
|
||||||
|
nxt.clear();
|
||||||
|
nxt.reserve(cur.size() * 2);
|
||||||
|
const node cw0 = dpf.correction_word(lvl, false);
|
||||||
|
const node cw1 = dpf.correction_word(lvl, true);
|
||||||
|
for (const node & fs : cur)
|
||||||
|
{
|
||||||
|
auto kids = KeyT::traverse_interior01(fs, cw0, cw1);
|
||||||
|
nxt.push_back(kids[0]);
|
||||||
|
nxt.push_back(kids[1]);
|
||||||
|
}
|
||||||
|
cur.swap(nxt);
|
||||||
|
}
|
||||||
|
for (const node & fs : cur)
|
||||||
|
acc = add_membership<KeyT>(acc, fs, word, mask, party);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT, typename InputT>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t query_suffix(InputT tx, std::size_t nbits, std::size_t q) noexcept
|
||||||
|
{
|
||||||
|
if (q == 0)
|
||||||
|
return 0;
|
||||||
|
uint64_t z = 0;
|
||||||
|
auto bit_mask = KeyT::msb_mask >> (nbits - q);
|
||||||
|
for (std::size_t j = 0; j < q; ++j, bit_mask >>= 1)
|
||||||
|
z = (z << 1) | static_cast<uint64_t>(!!(bit_mask & tx));
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t finish_share(const KeyT & dpf, uint64_t suffix, uint64_t acc,
|
||||||
|
const typename KeyT::interior_node & at_h, int party) noexcept
|
||||||
|
{
|
||||||
|
using namespace dcf_impl;
|
||||||
|
using prg = typename KeyT::interior_prg;
|
||||||
|
const auto & ch = dpf.cmp();
|
||||||
|
const uint64_t mask = ch.mask;
|
||||||
|
constexpr std::size_t q = KeyT::cmp_q;
|
||||||
|
constexpr std::size_t h = KeyT::cmp_h;
|
||||||
|
if constexpr (q == 0)
|
||||||
|
{
|
||||||
|
if (ch.include_eq)
|
||||||
|
{
|
||||||
|
constexpr auto wi = schedule<h, KeyT::cmp_block>::index(h);
|
||||||
|
acc = add_membership<KeyT>(acc, at_h, dpf.value_cw(wi), mask, party);
|
||||||
|
}
|
||||||
|
(void)suffix;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const uint64_t z = suffix;
|
||||||
|
uint64_t u[4]{};
|
||||||
|
suffix_masks<prg>(at_h, q, mask, u);
|
||||||
|
const uint8_t t = static_cast<uint8_t>(dpf::get_lo_bit(at_h));
|
||||||
|
const uint64_t y = (u[z] + (t ? dpf.tail_cw(z) : 0ULL)) & mask;
|
||||||
|
acc = (acc + (party ? neg_m(y, mask) : y)) & mask;
|
||||||
|
}
|
||||||
|
if (ch.eval_as_ge)
|
||||||
|
acc = neg_m(acc, mask);
|
||||||
|
const uint64_t add = [&]() -> uint64_t {
|
||||||
|
if constexpr (is_party_key_v<KeyT>)
|
||||||
|
return dpf.cmp_addend().raw();
|
||||||
|
else
|
||||||
|
return dpf.cmp_addend();
|
||||||
|
}();
|
||||||
|
return (acc + add) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT, typename InputT, typename PathMemoizer>
|
||||||
|
uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
|
||||||
|
{
|
||||||
|
using node = typename KeyT::interior_node;
|
||||||
|
const auto & ch = dpf.cmp();
|
||||||
|
const uint64_t mask = ch.mask;
|
||||||
|
const uint64_t add = [&]() -> uint64_t {
|
||||||
|
if constexpr (is_party_key_v<KeyT>)
|
||||||
|
return dpf.cmp_addend().raw();
|
||||||
|
else
|
||||||
|
return dpf.cmp_addend();
|
||||||
|
}();
|
||||||
|
if (ch.trivial == cmp_trivial::always_true
|
||||||
|
|| ch.trivial == cmp_trivial::always_false)
|
||||||
|
return add & mask;
|
||||||
|
|
||||||
|
constexpr std::size_t h = KeyT::cmp_h;
|
||||||
|
using sched = schedule<h, KeyT::cmp_block>;
|
||||||
|
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||||
|
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
|
||||||
|
|
||||||
|
dpf::detail::ensure_level(dpf, tx, path, h);
|
||||||
|
|
||||||
|
struct parked
|
||||||
|
{
|
||||||
|
node seed;
|
||||||
|
std::size_t depth;
|
||||||
|
};
|
||||||
|
parked pend[128];
|
||||||
|
std::size_t npend = 0;
|
||||||
|
|
||||||
|
uint64_t acc = 0;
|
||||||
|
auto bit_mask = KeyT::msb_mask;
|
||||||
|
for (std::size_t level = 0; level < h; ++level, bit_mask >>= 1)
|
||||||
|
{
|
||||||
|
const bool xi = !!(bit_mask & tx);
|
||||||
|
const node & parent = path[level];
|
||||||
|
const node right = KeyT::traverse_interior(parent,
|
||||||
|
dpf.correction_word(level, true), true);
|
||||||
|
if (!xi)
|
||||||
|
{
|
||||||
|
pend[npend].seed = right;
|
||||||
|
pend[npend].depth = level + 1;
|
||||||
|
++npend;
|
||||||
|
}
|
||||||
|
const std::size_t c = level + 1;
|
||||||
|
if (sched::contains(c))
|
||||||
|
{
|
||||||
|
const uint64_t word = dpf.value_cw(sched::index(c));
|
||||||
|
for (std::size_t p = 0; p < npend; ++p)
|
||||||
|
{
|
||||||
|
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
|
||||||
|
dpf, word, mask, party);
|
||||||
|
}
|
||||||
|
npend = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const uint64_t suffix = query_suffix<KeyT>(tx, nbits, KeyT::cmp_q);
|
||||||
|
return finish_share(dpf, suffix, acc, path[h], party);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT, typename Integral, typename Memo>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
bool memo_has(const Memo & memo, Integral prefix, std::size_t depth,
|
||||||
|
Integral from_lane, Integral to_excl) noexcept
|
||||||
|
{
|
||||||
|
const auto shift = KeyT::cmp_depth - depth;
|
||||||
|
const auto from_p = from_lane >> shift;
|
||||||
|
if (prefix < from_p)
|
||||||
|
return false;
|
||||||
|
const auto idx = static_cast<std::size_t>(prefix - from_p);
|
||||||
|
const auto count = memo.get_nodes_at_level(depth, from_lane, to_excl);
|
||||||
|
return idx < count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT, typename Integral, typename Memo>
|
||||||
|
const typename KeyT::interior_node & memo_node(const Memo & memo, Integral prefix,
|
||||||
|
std::size_t depth, Integral from_lane)
|
||||||
|
{
|
||||||
|
const auto shift = KeyT::cmp_depth - depth;
|
||||||
|
const auto from_p = from_lane >> shift;
|
||||||
|
const auto idx = static_cast<std::size_t>(prefix - from_p);
|
||||||
|
return memo[depth][idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyT, typename Integral, typename Memo>
|
||||||
|
uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
|
||||||
|
Integral from_lane, Integral to_excl, const Memo & memo)
|
||||||
|
{
|
||||||
|
using node = typename KeyT::interior_node;
|
||||||
|
const auto & ch = dpf.cmp();
|
||||||
|
const uint64_t mask = ch.mask;
|
||||||
|
const uint64_t add = [&]() -> uint64_t {
|
||||||
|
if constexpr (is_party_key_v<KeyT>)
|
||||||
|
return dpf.cmp_addend().raw();
|
||||||
|
else
|
||||||
|
return dpf.cmp_addend();
|
||||||
|
}();
|
||||||
|
if (ch.trivial == cmp_trivial::always_true
|
||||||
|
|| ch.trivial == cmp_trivial::always_false)
|
||||||
|
return add & mask;
|
||||||
|
|
||||||
|
constexpr std::size_t h = KeyT::cmp_h;
|
||||||
|
using sched = schedule<h, KeyT::cmp_block>;
|
||||||
|
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
|
||||||
|
|
||||||
|
struct parked
|
||||||
|
{
|
||||||
|
node seed;
|
||||||
|
Integral prefix;
|
||||||
|
std::size_t depth;
|
||||||
|
};
|
||||||
|
parked pend[128];
|
||||||
|
std::size_t npend = 0;
|
||||||
|
|
||||||
|
uint64_t acc = 0;
|
||||||
|
Integral path_pref = 0;
|
||||||
|
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||||
|
for (std::size_t level = 0; level < h; ++level)
|
||||||
|
{
|
||||||
|
const bool xi = ((lane >> (nbits - 1 - level)) & Integral{1}) != 0;
|
||||||
|
const node & parent = memo_node<KeyT>(memo, path_pref, level, from_lane);
|
||||||
|
const Integral sib = static_cast<Integral>((path_pref << 1) | Integral{1});
|
||||||
|
if (!xi)
|
||||||
|
{
|
||||||
|
const node right = KeyT::traverse_interior(parent,
|
||||||
|
dpf.correction_word(level, true), true);
|
||||||
|
pend[npend].seed = right;
|
||||||
|
pend[npend].prefix = sib;
|
||||||
|
pend[npend].depth = level + 1;
|
||||||
|
++npend;
|
||||||
|
}
|
||||||
|
path_pref = static_cast<Integral>((path_pref << 1) | Integral{xi ? 1 : 0});
|
||||||
|
const std::size_t c = level + 1;
|
||||||
|
if (!sched::contains(c))
|
||||||
|
continue;
|
||||||
|
const uint64_t word = dpf.value_cw(sched::index(c));
|
||||||
|
for (std::size_t p = 0; p < npend; ++p)
|
||||||
|
{
|
||||||
|
const std::size_t extra = c - pend[p].depth;
|
||||||
|
const Integral leftmost =
|
||||||
|
static_cast<Integral>(pend[p].prefix << extra);
|
||||||
|
const Integral rightmost = static_cast<Integral>(
|
||||||
|
leftmost + static_cast<Integral>((Integral{1} << extra) - 1));
|
||||||
|
const bool covered =
|
||||||
|
memo_has<KeyT>(memo, leftmost, c, from_lane, to_excl)
|
||||||
|
&& memo_has<KeyT>(memo, rightmost, c, from_lane, to_excl);
|
||||||
|
if (covered && extra < 16)
|
||||||
|
{
|
||||||
|
const Integral nleaf = static_cast<Integral>(Integral{1} << extra);
|
||||||
|
for (Integral k = 0; k < nleaf; ++k)
|
||||||
|
{
|
||||||
|
const auto pref = static_cast<Integral>(leftmost + k);
|
||||||
|
acc = add_membership<KeyT>(acc,
|
||||||
|
memo_node<KeyT>(memo, pref, c, from_lane), word, mask,
|
||||||
|
party);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
|
||||||
|
dpf, word, mask, party);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
npend = 0;
|
||||||
|
}
|
||||||
|
const node & at_h = memo_node<KeyT>(memo, path_pref, h, from_lane);
|
||||||
|
const uint64_t suffix = static_cast<uint64_t>(lane)
|
||||||
|
& (KeyT::cmp_q == 0 ? 0ULL : ((1ULL << KeyT::cmp_q) - 1ULL));
|
||||||
|
return finish_share(dpf, suffix, acc, at_h, party);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blocked
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace dpf
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
|
||||||
|
|
@ -39,6 +39,7 @@ namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename PRG>
|
template <typename PRG>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
typename PRG::block_type mask_master(typename PRG::block_type master) noexcept
|
typename PRG::block_type mask_master(typename PRG::block_type master) noexcept
|
||||||
{
|
{
|
||||||
unsigned char raw[sizeof(master)];
|
unsigned char raw[sizeof(master)];
|
||||||
|
|
@ -140,6 +141,7 @@ struct buffered_slot
|
||||||
return lane_codec<PRG, T>::at(seed_, index);
|
return lane_codec<PRG, T>::at(seed_, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::uint64_t sampled() const noexcept { return absolute_pos_; }
|
std::uint64_t sampled() const noexcept { return absolute_pos_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -165,7 +167,13 @@ typename PRG::block_type sample_master_seed()
|
||||||
return dpf::uniform_sample<typename PRG::block_type>();
|
return dpf::uniform_sample<typename PRG::block_type>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fixed lanes. Lane `I` is `PRG::eval(master, I)`.
|
/// Forward cursor over one PRG stream per value type.
|
||||||
|
///
|
||||||
|
/// `get<I>()` and `fill<I>()` consume the cursor. `at<I>(index)` reads an
|
||||||
|
/// absolute index and leaves the cursor where it is. `sampled<I>()` reports
|
||||||
|
/// how far `get` and `fill` have advanced. `per_stream_buffer_elems` is at
|
||||||
|
/// least 1.
|
||||||
|
/// @snippet evaluation/buffered_prg.cpp buffered-prg
|
||||||
template <typename PRG, typename... Ts>
|
template <typename PRG, typename... Ts>
|
||||||
class buffered_prg
|
class buffered_prg
|
||||||
{
|
{
|
||||||
|
|
@ -184,6 +192,7 @@ public:
|
||||||
buffers_(make_buffers(per_stream_buffer_elems))
|
buffers_(make_buffers(per_stream_buffer_elems))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const seed_type & seed() const noexcept { return seed_; }
|
const seed_type & seed() const noexcept { return seed_; }
|
||||||
|
|
||||||
template <std::size_t I>
|
template <std::size_t I>
|
||||||
|
|
@ -208,6 +217,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t I>
|
template <std::size_t I>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::uint64_t sampled() const noexcept
|
std::uint64_t sampled() const noexcept
|
||||||
{
|
{
|
||||||
static_assert(I < stream_count, "stream index out of range");
|
static_assert(I < stream_count, "stream index out of range");
|
||||||
|
|
@ -238,9 +248,12 @@ private:
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
using aes_buffered_prg = buffered_prg<dpf::prg::aes128, Ts...>;
|
using aes_buffered_prg = buffered_prg<dpf::prg::aes128, Ts...>;
|
||||||
|
|
||||||
/// Dynamic lanes of one value type. `value_at(role, index)` and
|
/// Seekable value and mask streams for a runtime set of roles.
|
||||||
/// `mask_at(role, index)` are independent of call order. A window cache
|
///
|
||||||
/// refills from the requested index.
|
/// `value_at(role, index)` and `mask_at(role, index)` are independent of
|
||||||
|
/// call order. A repeated index returns the same element. `window` is at
|
||||||
|
/// least 1.
|
||||||
|
/// @snippet evaluation/buffered_prg.cpp lane-table
|
||||||
template <typename T, typename PRG = dpf::prg::aes128>
|
template <typename T, typename PRG = dpf::prg::aes128>
|
||||||
class lane_table
|
class lane_table
|
||||||
{
|
{
|
||||||
|
|
@ -267,6 +280,7 @@ public:
|
||||||
lane_table(lane_table &&) = default;
|
lane_table(lane_table &&) = default;
|
||||||
lane_table & operator=(lane_table &&) = default;
|
lane_table & operator=(lane_table &&) = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const seed_type & seed() const noexcept { return seed_; }
|
const seed_type & seed() const noexcept { return seed_; }
|
||||||
|
|
||||||
T value_at(std::uint32_t role, std::uint64_t index) const
|
T value_at(std::uint32_t role, std::uint64_t index) const
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,61 @@
|
||||||
namespace dpf
|
namespace dpf
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template <typename Beta>
|
||||||
|
struct ic_pack;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_ic_pack : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Beta>
|
||||||
|
struct is_ic_pack<ic_pack<Beta>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename ...Ts>
|
||||||
|
inline constexpr bool no_ic_pack_v =
|
||||||
|
(!is_ic_pack<std::decay_t<Ts>>::value && ...);
|
||||||
|
|
||||||
/// Comparison kind for the optional DCF channel on a key.
|
/// Comparison kind for the optional DCF channel on a key.
|
||||||
|
/// `lt`/`leq`/`gt`/`geq` are the comparison predicates. The later kinds are
|
||||||
|
/// path paints: one constant on each sibling subtree of the secret point,
|
||||||
|
/// evaluated by the same value-correction walk.
|
||||||
enum class cmp_kind : uint8_t
|
enum class cmp_kind : uint8_t
|
||||||
{
|
{
|
||||||
lt = 0,
|
lt = 0,
|
||||||
leq = 1,
|
leq = 1,
|
||||||
gt = 2,
|
gt = 2,
|
||||||
geq = 3
|
geq = 3,
|
||||||
|
lcp = 4, // common-prefix length
|
||||||
|
prefix = 5, // matched prefix, in the lane's high bits
|
||||||
|
mask = 6, // high-bit mask of that length
|
||||||
|
one_hot = 7, // 2^{length} (0 when the bit does not fit)
|
||||||
|
break_bit = 8, // secret bit at the first difference
|
||||||
|
prefix_with_length = 9, // (low-aligned prefix << length_bits) | length
|
||||||
|
paint = 10 // caller-supplied unit plant
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// True for the path-paint kinds. Comparisons stay `lt`/`leq`/`gt`/`geq`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline constexpr bool is_paint_kind(cmp_kind kind) noexcept
|
||||||
|
{
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case cmp_kind::lcp:
|
||||||
|
case cmp_kind::prefix:
|
||||||
|
case cmp_kind::mask:
|
||||||
|
case cmp_kind::one_hot:
|
||||||
|
case cmp_kind::break_bit:
|
||||||
|
case cmp_kind::prefix_with_length:
|
||||||
|
case cmp_kind::paint:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unit plant for `path_paint`. `prefix` is the in-lane matched prefix.
|
||||||
|
using paint_callback = uint64_t (*)(std::size_t matched, uint64_t prefix,
|
||||||
|
bool leaf, const void * ctx);
|
||||||
|
|
||||||
enum class cmp_trivial : uint8_t
|
enum class cmp_trivial : uint8_t
|
||||||
{
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
|
|
@ -48,6 +94,7 @@ namespace dcf_impl
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename Beta>
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
Beta default_false() noexcept
|
Beta default_false() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||||
|
|
@ -57,6 +104,7 @@ Beta default_false() noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Beta>
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
|
uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
|
||||||
uint64_t mask) noexcept
|
uint64_t mask) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -79,6 +127,7 @@ uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Beta>
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
|
uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||||
|
|
@ -88,6 +137,7 @@ uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Beta>
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
Beta sub_beta(const Beta & a, const Beta & b) noexcept
|
Beta sub_beta(const Beta & a, const Beta & b) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||||
|
|
@ -99,6 +149,7 @@ Beta sub_beta(const Beta & a, const Beta & b) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Beta>
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
Beta u64_to_beta(uint64_t v) noexcept
|
Beta u64_to_beta(uint64_t v) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||||
|
|
@ -107,7 +158,10 @@ Beta u64_to_beta(uint64_t v) noexcept
|
||||||
return static_cast<Beta>(v);
|
return static_cast<Beta>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
|
||||||
{
|
{
|
||||||
if (out_bits >= 64)
|
if (out_bits >= 64)
|
||||||
return ~0ULL;
|
return ~0ULL;
|
||||||
|
|
@ -116,14 +170,18 @@ inline uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
|
||||||
return (1ULL << out_bits) - 1ULL;
|
return (1ULL << out_bits) - 1ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
uint64_t neg_m(uint64_t x, uint64_t mask) noexcept
|
constexpr uint64_t neg_m(uint64_t x, uint64_t mask) noexcept
|
||||||
{
|
{
|
||||||
return (0ULL - x) & mask;
|
return (0ULL - x) & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
uint64_t sgn_m(uint8_t t1, uint64_t x, uint64_t mask) noexcept
|
constexpr uint64_t sgn_m(uint8_t t1, uint64_t x, uint64_t mask) noexcept
|
||||||
{
|
{
|
||||||
return t1 ? neg_m(x, mask) : x;
|
return t1 ? neg_m(x, mask) : x;
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +201,7 @@ uint64_t convert_node(simde__m128i n, uint64_t mask) noexcept
|
||||||
/// same block source so their keys stay byte-identical (matched tapes).
|
/// same block source so their keys stay byte-identical (matched tapes).
|
||||||
template <typename BlockSampler>
|
template <typename BlockSampler>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
||||||
{
|
{
|
||||||
return convert_node(dpf::unset_lo_2bits(sample()), mask);
|
return convert_node(dpf::unset_lo_2bits(sample()), mask);
|
||||||
|
|
@ -150,6 +209,7 @@ uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
||||||
|
|
||||||
/// One level of value CW on GGM children. Updates running `Va`.
|
/// One level of value CW on GGM children. Updates running `Va`.
|
||||||
/// `ai` is the keep-path bit of the (effective) threshold.
|
/// `ai` is the keep-path bit of the (effective) threshold.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
|
inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
|
||||||
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
|
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
|
||||||
uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
|
uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
|
||||||
|
|
@ -179,8 +239,127 @@ inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
|
||||||
return vcw;
|
return vcw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same recurrence as `make_value_cw`, planting `plant` on the lose child
|
||||||
|
/// in both directions. `plant == 0` leaves the correction unchanged.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline uint64_t make_value_cw_planted(simde__m128i c0L, simde__m128i c0R,
|
||||||
|
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
|
||||||
|
uint64_t & Va, uint64_t plant, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
(void)t0;
|
||||||
|
uint64_t v0K, v1K, v0Lo, v1Lo;
|
||||||
|
if (ai == 0)
|
||||||
|
{
|
||||||
|
v0K = convert_node(c0L, mask);
|
||||||
|
v1K = convert_node(c1L, mask);
|
||||||
|
v0Lo = convert_node(c0R, mask);
|
||||||
|
v1Lo = convert_node(c1R, mask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v0K = convert_node(c0R, mask);
|
||||||
|
v1K = convert_node(c1R, mask);
|
||||||
|
v0Lo = convert_node(c0L, mask);
|
||||||
|
v1Lo = convert_node(c1L, mask);
|
||||||
|
}
|
||||||
|
uint64_t vcw = sgn_m(t1,
|
||||||
|
(v1Lo + neg_m(v0Lo, mask) + neg_m(Va, mask)) & mask, mask);
|
||||||
|
vcw = (vcw + sgn_m(t1, plant, mask)) & mask;
|
||||||
|
Va = (Va + neg_m(v1K, mask) + v0K + sgn_m(t1, vcw, mask)) & mask;
|
||||||
|
return vcw;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline unsigned __int128 paint_lane_mask(std::size_t nbits) noexcept
|
||||||
|
{
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
if (nbits == 0)
|
||||||
|
return 0;
|
||||||
|
if (nbits >= 128)
|
||||||
|
return ~u128{0};
|
||||||
|
return (u128{1} << nbits) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// High `d` bits of an `nbits`-wide lane, in that lane's own positions.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline unsigned __int128 paint_high_bits(unsigned __int128 alpha,
|
||||||
|
std::size_t nbits, std::size_t d) noexcept
|
||||||
|
{
|
||||||
|
alpha &= paint_lane_mask(nbits);
|
||||||
|
if (d == 0 || nbits == 0)
|
||||||
|
return 0;
|
||||||
|
if (d >= nbits)
|
||||||
|
return alpha;
|
||||||
|
const std::size_t drop = nbits - d;
|
||||||
|
return (alpha >> drop) << drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline unsigned __int128 paint_low_aligned(unsigned __int128 alpha,
|
||||||
|
std::size_t nbits, std::size_t d) noexcept
|
||||||
|
{
|
||||||
|
alpha &= paint_lane_mask(nbits);
|
||||||
|
if (d == 0 || nbits == 0)
|
||||||
|
return 0;
|
||||||
|
if (d >= nbits)
|
||||||
|
return alpha;
|
||||||
|
return alpha >> (nbits - d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unit (β = 1) lose-subtree or leaf plant. The caller scales by δ.
|
||||||
|
/// `matched` is the number of leading bits already shared with α. A lose
|
||||||
|
/// subtree at that depth reconstructs to this value; `leaf` is the full match.
|
||||||
|
inline uint64_t paint_unit(cmp_kind kind, std::size_t matched,
|
||||||
|
unsigned __int128 alpha, std::size_t nbits, std::size_t length_bits,
|
||||||
|
bool leaf, paint_callback fn, const void * ctx)
|
||||||
|
{
|
||||||
|
if (nbits == 0)
|
||||||
|
return 0;
|
||||||
|
const std::size_t d = leaf ? nbits : matched;
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case cmp_kind::lcp:
|
||||||
|
return static_cast<uint64_t>(d);
|
||||||
|
case cmp_kind::prefix:
|
||||||
|
return static_cast<uint64_t>(paint_high_bits(alpha, nbits, d));
|
||||||
|
case cmp_kind::mask:
|
||||||
|
return static_cast<uint64_t>(
|
||||||
|
paint_high_bits(paint_lane_mask(nbits), nbits, d));
|
||||||
|
case cmp_kind::one_hot:
|
||||||
|
return d >= 64 ? 0ULL : (1ULL << d);
|
||||||
|
case cmp_kind::break_bit:
|
||||||
|
if (leaf || matched >= nbits)
|
||||||
|
return 0;
|
||||||
|
return static_cast<uint64_t>(
|
||||||
|
(alpha >> (nbits - 1 - matched)) & 1);
|
||||||
|
case cmp_kind::prefix_with_length:
|
||||||
|
{
|
||||||
|
if (length_bits >= 128)
|
||||||
|
return static_cast<uint64_t>(d);
|
||||||
|
unsigned __int128 packed =
|
||||||
|
paint_low_aligned(alpha, nbits, d) << length_bits;
|
||||||
|
packed |= static_cast<unsigned __int128>(d);
|
||||||
|
return static_cast<uint64_t>(packed);
|
||||||
|
}
|
||||||
|
case cmp_kind::paint:
|
||||||
|
if (fn == nullptr)
|
||||||
|
return 0;
|
||||||
|
return fn(d, static_cast<uint64_t>(paint_high_bits(alpha, nbits, d)),
|
||||||
|
leaf, ctx);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
inline uint64_t scale_plant(uint64_t unit, uint64_t scale, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
return (unit * scale) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
/// Final leaf value CW. `on_path` is the payload reconstructed when the query
|
/// Final leaf value CW. `on_path` is the payload reconstructed when the query
|
||||||
/// stays on α's path through all levels (0 for strict lt/geq; β for leq/gt).
|
/// stays on α's path through all levels (0 for strict lt/geq; β for leq/gt).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline uint64_t make_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
|
inline uint64_t make_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
|
||||||
uint64_t Va, uint64_t mask, uint64_t on_path = 0) noexcept
|
uint64_t Va, uint64_t mask, uint64_t on_path = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -206,7 +385,11 @@ struct cmp_meta
|
||||||
bool eval_as_ge = false; // invert path-sum (geq / gt)
|
bool eval_as_ge = false; // invert path-sum (geq / gt)
|
||||||
bool include_eq = false; // plant δ on the α-path leaf (leq / gt)
|
bool include_eq = false; // plant δ on the α-path leaf (leq / gt)
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
bool incremental = false; // final correction saved at every depth
|
||||||
|
int block_width = 0; // 0 = per-level path-sum
|
||||||
|
int tail_bits = 0; // residual q; 0 when the tree covers every bit
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool empty() const noexcept { return !active; }
|
bool empty() const noexcept { return !active; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -225,6 +408,7 @@ struct cmp_pack
|
||||||
static constexpr bool is_cmp = true;
|
static constexpr bool is_cmp = true;
|
||||||
static constexpr cmp_kind kind = Kind;
|
static constexpr cmp_kind kind = Kind;
|
||||||
static constexpr std::size_t prefix = 0;
|
static constexpr std::size_t prefix = 0;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
using beta_type = Beta;
|
using beta_type = Beta;
|
||||||
Beta if_true;
|
Beta if_true;
|
||||||
Beta if_false;
|
Beta if_false;
|
||||||
|
|
@ -239,6 +423,7 @@ struct cmp_at_pack
|
||||||
static constexpr bool is_cmp = true;
|
static constexpr bool is_cmp = true;
|
||||||
static constexpr cmp_kind kind = Kind;
|
static constexpr cmp_kind kind = Kind;
|
||||||
static constexpr std::size_t prefix = N;
|
static constexpr std::size_t prefix = N;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
using beta_type = Beta;
|
using beta_type = Beta;
|
||||||
Beta if_true;
|
Beta if_true;
|
||||||
Beta if_false;
|
Beta if_false;
|
||||||
|
|
@ -289,6 +474,228 @@ inline auto geq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t
|
||||||
return cmp_at_pack<N, cmp_kind::geq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
return cmp_at_pack<N, cmp_kind::geq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Path paints. Same channel and same (if_true, if_false) scale as a comparison:
|
||||||
|
// the reconstructed value is if_false + (if_true − if_false) · unit(x).
|
||||||
|
// `unit` is the common-prefix length, the matched prefix, a mask, and so on.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct spec_is_incremental : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct spec_is_incremental<T, std::void_t<decltype(T::incremental)>>
|
||||||
|
: std::bool_constant<T::incremental> {};
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct spec_length_bits : std::integral_constant<std::size_t, 0> {};
|
||||||
|
template <typename T>
|
||||||
|
struct spec_length_bits<T, std::void_t<decltype(T::length_bits)>>
|
||||||
|
: std::integral_constant<std::size_t, T::length_bits> {};
|
||||||
|
|
||||||
|
template <cmp_kind Kind, typename Beta, std::size_t LengthBits = 0>
|
||||||
|
struct paint_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr cmp_kind kind = Kind;
|
||||||
|
static constexpr std::size_t prefix = 0;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
|
static constexpr std::size_t length_bits = LengthBits;
|
||||||
|
static constexpr bool incremental = false;
|
||||||
|
using beta_type = Beta;
|
||||||
|
Beta if_true;
|
||||||
|
Beta if_false;
|
||||||
|
|
||||||
|
explicit paint_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N, cmp_kind Kind, typename Beta, std::size_t LengthBits = 0>
|
||||||
|
struct paint_at_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr cmp_kind kind = Kind;
|
||||||
|
static constexpr std::size_t prefix = N;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
|
static constexpr std::size_t length_bits = LengthBits;
|
||||||
|
static constexpr bool incremental = false;
|
||||||
|
using beta_type = Beta;
|
||||||
|
Beta if_true;
|
||||||
|
Beta if_false;
|
||||||
|
|
||||||
|
explicit paint_at_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Beta = uint64_t>
|
||||||
|
inline auto lcp(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::lcp, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Beta = uint64_t>
|
||||||
|
inline auto lcp_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::lcp, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta = uint64_t>
|
||||||
|
inline auto common_prefix(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::prefix, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Beta = uint64_t>
|
||||||
|
inline auto common_prefix_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::prefix, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta = uint64_t>
|
||||||
|
inline auto prefix_mask(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::mask, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Beta = uint64_t>
|
||||||
|
inline auto prefix_mask_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::mask, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta = uint64_t>
|
||||||
|
inline auto diverge_one_hot(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::one_hot, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Beta = uint64_t>
|
||||||
|
inline auto diverge_one_hot_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::one_hot, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta = uint64_t>
|
||||||
|
inline auto break_bit(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::break_bit, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Beta = uint64_t>
|
||||||
|
inline auto break_bit_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::break_bit, Beta>(std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Low `LengthBits` hold the common-prefix length. Above them sits the
|
||||||
|
/// matched prefix packed into the low bits of the lane (`α >> (N − d)`).
|
||||||
|
template <std::size_t LengthBits = 8, typename Beta = uint64_t>
|
||||||
|
inline auto prefix_with_length(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_pack<cmp_kind::prefix_with_length, Beta, LengthBits>(
|
||||||
|
std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
template <std::size_t N, std::size_t LengthBits = 8, typename Beta = uint64_t>
|
||||||
|
inline auto prefix_with_length_at(Beta t = Beta{1},
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_at_pack<N, cmp_kind::prefix_with_length, Beta, LengthBits>(
|
||||||
|
std::move(t), std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary unit plant. `fn(matched, in_lane_prefix, leaf)` returns the β = 1
|
||||||
|
/// value of that sibling subtree (`leaf` is the full match, `matched == N`).
|
||||||
|
/// The result is scaled by `if_true − if_false` like the canned recipes.
|
||||||
|
template <typename Beta, typename Fn>
|
||||||
|
struct paint_fn_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr cmp_kind kind = cmp_kind::paint;
|
||||||
|
static constexpr std::size_t prefix = 0;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
|
static constexpr std::size_t length_bits = 0;
|
||||||
|
static constexpr bool incremental = false;
|
||||||
|
using beta_type = Beta;
|
||||||
|
Beta if_true;
|
||||||
|
Beta if_false;
|
||||||
|
Fn fn;
|
||||||
|
|
||||||
|
paint_fn_pack(Beta t, Beta f, Fn g)
|
||||||
|
: if_true{std::move(t)}, if_false{std::move(f)}, fn{std::move(g)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N, typename Beta, typename Fn>
|
||||||
|
struct paint_fn_at_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr cmp_kind kind = cmp_kind::paint;
|
||||||
|
static constexpr std::size_t prefix = N;
|
||||||
|
static constexpr std::size_t block_width = 0;
|
||||||
|
static constexpr std::size_t length_bits = 0;
|
||||||
|
static constexpr bool incremental = false;
|
||||||
|
using beta_type = Beta;
|
||||||
|
Beta if_true;
|
||||||
|
Beta if_false;
|
||||||
|
Fn fn;
|
||||||
|
|
||||||
|
paint_fn_at_pack(Beta t, Beta f, Fn g)
|
||||||
|
: if_true{std::move(t)}, if_false{std::move(f)}, fn{std::move(g)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn, typename Beta>
|
||||||
|
inline auto path_paint(Fn fn, Beta t,
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_fn_pack<std::decay_t<Beta>, std::decay_t<Fn>>(
|
||||||
|
std::move(t), std::move(f), std::move(fn));
|
||||||
|
}
|
||||||
|
template <typename Fn>
|
||||||
|
inline auto path_paint(Fn fn)
|
||||||
|
{
|
||||||
|
return path_paint(std::move(fn), uint64_t{1});
|
||||||
|
}
|
||||||
|
template <std::size_t N, typename Fn, typename Beta>
|
||||||
|
inline auto path_paint_at(Fn fn, Beta t,
|
||||||
|
Beta f = detail::dcf_impl::default_false<Beta>())
|
||||||
|
{
|
||||||
|
return paint_fn_at_pack<N, std::decay_t<Beta>, std::decay_t<Fn>>(
|
||||||
|
std::move(t), std::move(f), std::move(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Incremental comparison: the same predicate, correct at every prefix length.
|
||||||
|
/// Evaluate the full point with `cmp`, and a prefix with `cmp_prefix<L>`.
|
||||||
|
template <typename Spec>
|
||||||
|
struct idcf_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr bool incremental = true;
|
||||||
|
static constexpr cmp_kind kind = Spec::kind;
|
||||||
|
static constexpr std::size_t prefix = Spec::prefix;
|
||||||
|
static constexpr std::size_t block_width = Spec::block_width;
|
||||||
|
static constexpr std::size_t length_bits = spec_length_bits<Spec>::value;
|
||||||
|
using beta_type = typename Spec::beta_type;
|
||||||
|
beta_type if_true;
|
||||||
|
beta_type if_false;
|
||||||
|
|
||||||
|
explicit idcf_pack(Spec spec)
|
||||||
|
: if_true{std::move(spec.if_true)}, if_false{std::move(spec.if_false)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Spec>
|
||||||
|
inline auto idcf(Spec spec)
|
||||||
|
{
|
||||||
|
static_assert(is_paint_kind(Spec::kind) == false,
|
||||||
|
"idcf wraps lt/leq/gt/geq; path paints are already one full-domain value");
|
||||||
|
static_assert(Spec::block_width == 0,
|
||||||
|
"idcf uses the per-level path, not blocked checkpoints");
|
||||||
|
return idcf_pack<Spec>{std::move(spec)};
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Equality specs: eq / eq_at
|
// Equality specs: eq / eq_at
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -330,10 +737,54 @@ inline auto eq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<
|
||||||
return eq_at_pack<N, std::decay_t<Beta>>(std::move(t), std::move(f));
|
return eq_at_pack<N, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <std::size_t BlockWidth, typename Spec>
|
||||||
|
struct block_width_pack
|
||||||
|
{
|
||||||
|
static_assert(BlockWidth >= 1, "block_width<B> needs B >= 1");
|
||||||
|
static_assert(Spec::block_width == 0, "comparison is already block_width");
|
||||||
|
static constexpr bool is_cmp = true;
|
||||||
|
static constexpr std::size_t block_width = BlockWidth;
|
||||||
|
static constexpr cmp_kind kind = Spec::kind;
|
||||||
|
static constexpr std::size_t prefix = Spec::prefix;
|
||||||
|
static constexpr bool incremental = spec_is_incremental<Spec>::value;
|
||||||
|
static constexpr std::size_t length_bits = spec_length_bits<Spec>::value;
|
||||||
|
using beta_type = typename Spec::beta_type;
|
||||||
|
beta_type if_true;
|
||||||
|
beta_type if_false;
|
||||||
|
|
||||||
|
explicit block_width_pack(Spec spec)
|
||||||
|
: if_true{std::move(spec.if_true)}, if_false{std::move(spec.if_false)} { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t BlockWidth>
|
||||||
|
struct block_width_fn
|
||||||
|
{
|
||||||
|
template <typename Spec>
|
||||||
|
constexpr auto operator()(Spec spec) const
|
||||||
|
{
|
||||||
|
return block_width_pack<BlockWidth, Spec>{std::move(spec)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t BlockWidth>
|
||||||
|
inline constexpr block_width_fn<BlockWidth> block_width{};
|
||||||
|
|
||||||
template <typename T> struct is_cmp_spec : std::false_type {};
|
template <typename T> struct is_cmp_spec : std::false_type {};
|
||||||
template <cmp_kind K, typename B> struct is_cmp_spec<cmp_pack<K, B>> : std::true_type {};
|
template <cmp_kind K, typename B> struct is_cmp_spec<cmp_pack<K, B>> : std::true_type {};
|
||||||
template <std::size_t N, cmp_kind K, typename B>
|
template <std::size_t N, cmp_kind K, typename B>
|
||||||
struct is_cmp_spec<cmp_at_pack<N, K, B>> : std::true_type {};
|
struct is_cmp_spec<cmp_at_pack<N, K, B>> : std::true_type {};
|
||||||
|
template <cmp_kind K, typename B, std::size_t L>
|
||||||
|
struct is_cmp_spec<paint_pack<K, B, L>> : std::true_type {};
|
||||||
|
template <std::size_t N, cmp_kind K, typename B, std::size_t L>
|
||||||
|
struct is_cmp_spec<paint_at_pack<N, K, B, L>> : std::true_type {};
|
||||||
|
template <typename B, typename Fn>
|
||||||
|
struct is_cmp_spec<paint_fn_pack<B, Fn>> : std::true_type {};
|
||||||
|
template <std::size_t N, typename B, typename Fn>
|
||||||
|
struct is_cmp_spec<paint_fn_at_pack<N, B, Fn>> : std::true_type {};
|
||||||
|
template <typename Spec>
|
||||||
|
struct is_cmp_spec<idcf_pack<Spec>> : std::true_type {};
|
||||||
|
template <std::size_t B, typename Spec>
|
||||||
|
struct is_cmp_spec<block_width_pack<B, Spec>> : std::true_type {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline constexpr bool is_cmp_spec_v = is_cmp_spec<T>::value;
|
inline constexpr bool is_cmp_spec_v = is_cmp_spec<T>::value;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
/// @file dpf/doerner_shelat.hpp
|
/// @file dpf/doerner_shelat.hpp
|
||||||
/// @brief Doerner–Shelat generation of a dealer DPF key.
|
/// @brief Doerner–Shelat generation of a dealer DPF key.
|
||||||
/// @details Two XOR shares of the point are walked level by level. Correction
|
/// @details Two shares of the point are walked level by level — XOR shares by
|
||||||
/// words, advice bits, seeds, and leaves are the ones `make_dpf`
|
/// default, or additive shares when tagged with `arith_input`.
|
||||||
/// would emit for the XOR of those shares, the same roots, and the
|
/// Correction words, advice bits, seeds, and leaves are the ones
|
||||||
/// same beaver coins. Beaver pads used to hide the path bit cancel
|
/// `make_dpf` would emit for the reconstructed point, the same roots,
|
||||||
/// and are not part of the key. Pad randomness must not come from
|
/// and the same beaver coins. Beaver pads used to hide the path bit
|
||||||
/// `uniform_fill` if the beaver tape is being matched.
|
/// cancel and are not part of the key. Pad randomness must not come
|
||||||
|
/// from `uniform_fill` if the beaver tape is being matched.
|
||||||
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
/// see [LICENSE.md](@ref license) for details.
|
/// see [LICENSE.md](@ref license) for details.
|
||||||
|
|
@ -28,6 +29,14 @@
|
||||||
namespace dpf
|
namespace dpf
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Tag: Doerner–Shelat / geneval takes additive shares of the point
|
||||||
|
/// (`x0 + x1` in the input ring). Default calls take XOR shares.
|
||||||
|
struct arith_input_t
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr arith_input_t arith_input{};
|
||||||
|
|
||||||
/// Roots and the Beaver-pad stream for one Doerner–Shelat generation.
|
/// Roots and the Beaver-pad stream for one Doerner–Shelat generation.
|
||||||
/// `root` is called twice, same as `make_dpf`: party 0 clears the low bit of
|
/// `root` is called twice, same as `make_dpf`: party 0 clears the low bit of
|
||||||
/// the first sample, party 1 sets the low bit of the second.
|
/// the first sample, party 1 sets the low bit of the second.
|
||||||
|
|
@ -86,12 +95,14 @@ struct ds_and_shares
|
||||||
simde__m128i z1;
|
simde__m128i z1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
simde__m128i ds_xor(simde__m128i a, simde__m128i b) noexcept
|
simde__m128i ds_xor(simde__m128i a, simde__m128i b) noexcept
|
||||||
{
|
{
|
||||||
return simde_mm_xor_si128(a, b);
|
return simde_mm_xor_si128(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
simde__m128i ds_gate(uint8_t bit, simde__m128i block) noexcept
|
simde__m128i ds_gate(uint8_t bit, simde__m128i block) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -128,6 +139,7 @@ ds_and_pads ds_sample_and(PadRng & pad)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
simde__m128i ds_cw_share(simde__m128i L, simde__m128i R, uint8_t my_bit,
|
simde__m128i ds_cw_share(simde__m128i L, simde__m128i R, uint8_t my_bit,
|
||||||
const ds_cw_party & mine, const ds_blind & their) noexcept
|
const ds_cw_party & mine, const ds_blind & their) noexcept
|
||||||
|
|
@ -144,6 +156,7 @@ simde__m128i ds_cw_share(simde__m128i L, simde__m128i R, uint8_t my_bit,
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline void ds_cw_blinds(const ds_cw_pads & p,
|
inline void ds_cw_blinds(const ds_cw_pads & p,
|
||||||
simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
||||||
simde__m128i L1, simde__m128i R1, uint8_t bit1,
|
simde__m128i L1, simde__m128i R1, uint8_t bit1,
|
||||||
|
|
@ -155,6 +168,7 @@ inline void ds_cw_blinds(const ds_cw_pads & p,
|
||||||
b1.msg = ds_xor(ds_xor(L1, R1), p.p1.rand);
|
b1.msg = ds_xor(ds_xor(L1, R1), p.p1.rand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m128i ds_cw_outs(const ds_cw_pads & p,
|
inline simde__m128i ds_cw_outs(const ds_cw_pads & p,
|
||||||
simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
||||||
simde__m128i L1, simde__m128i R1, uint8_t bit1,
|
simde__m128i L1, simde__m128i R1, uint8_t bit1,
|
||||||
|
|
@ -165,6 +179,7 @@ inline simde__m128i ds_cw_outs(const ds_cw_pads & p,
|
||||||
ds_cw_share(L1, R1, bit1, p.p1, b0));
|
ds_cw_share(L1, R1, bit1, p.p1, b0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline uint8_t ds_open_advice(simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
inline uint8_t ds_open_advice(simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
||||||
simde__m128i L1, simde__m128i R1, uint8_t bit1) noexcept
|
simde__m128i L1, simde__m128i R1, uint8_t bit1) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -177,6 +192,7 @@ inline uint8_t ds_open_advice(simde__m128i L0, simde__m128i R0, uint8_t bit0,
|
||||||
return static_cast<uint8_t>((t1 << 1) | (t0 & 1u));
|
return static_cast<uint8_t>((t1 << 1) | (t0 & 1u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline void ds_next_terms(simde__m128i L, simde__m128i R, uint8_t advice,
|
inline void ds_next_terms(simde__m128i L, simde__m128i R, uint8_t advice,
|
||||||
simde__m128i cw, uint8_t tpack, simde__m128i & M, simde__m128i & base) noexcept
|
simde__m128i cw, uint8_t tpack, simde__m128i & M, simde__m128i & base) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -190,6 +206,7 @@ inline void ds_next_terms(simde__m128i L, simde__m128i R, uint8_t advice,
|
||||||
base = (advice & 1u) ? ds_xor(L, cw_base) : L;
|
base = (advice & 1u) ? ds_xor(L, cw_base) : L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline ds_and_shares ds_and_open(const ds_and_pads & p, simde__m128i M,
|
inline ds_and_shares ds_and_open(const ds_and_pads & p, simde__m128i M,
|
||||||
uint8_t b_recv) noexcept
|
uint8_t b_recv) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -202,6 +219,7 @@ inline ds_and_shares ds_and_open(const ds_and_pads & p, simde__m128i M,
|
||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m128i ds_deliver(uint8_t b_exp, simde__m128i base, simde__m128i M,
|
inline simde__m128i ds_deliver(uint8_t b_exp, simde__m128i base, simde__m128i M,
|
||||||
const ds_and_shares & z) noexcept
|
const ds_and_shares & z) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -249,6 +267,11 @@ struct ds_cmp_gen_state
|
||||||
bool track_coeff = false;
|
bool track_coeff = false;
|
||||||
uint64_t Va1 = 0;
|
uint64_t Va1 = 0;
|
||||||
uint64_t last_vcw_coeff = 0;
|
uint64_t last_vcw_coeff = 0;
|
||||||
|
cmp_kind kind = cmp_kind::lt;
|
||||||
|
bool paint = false;
|
||||||
|
std::size_t length_bits = 0;
|
||||||
|
paint_callback paint_cb = nullptr;
|
||||||
|
const void * paint_ctx = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Local joint simulation: today's `ds_cw_outs` / `ds_open_advice` / `ds_and_open`.
|
/// Local joint simulation: today's `ds_cw_outs` / `ds_open_advice` / `ds_and_open`.
|
||||||
|
|
@ -288,6 +311,7 @@ struct local_cw_protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open CW + advice only (AND pads stay in `blinds` for a later open).
|
/// Open CW + advice only (AND pads stay in `blinds` for a later open).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::pair<simde__m128i, uint8_t> open_cw(const ds_level_blinds & b) noexcept
|
std::pair<simde__m128i, uint8_t> open_cw(const ds_level_blinds & b) noexcept
|
||||||
{
|
{
|
||||||
return {ds_cw_outs(b.cwp, b.L0, b.R0, b.bit0, b.L1, b.R1, b.bit1,
|
return {ds_cw_outs(b.cwp, b.L0, b.R0, b.bit0, b.L1, b.R1, b.bit1,
|
||||||
|
|
@ -297,6 +321,7 @@ struct local_cw_protocol
|
||||||
|
|
||||||
/// Open the public value CW for this level (local: clear convert+make_value_cw).
|
/// Open the public value CW for this level (local: clear convert+make_value_cw).
|
||||||
/// MPC backends open additive shares of the same word.
|
/// MPC backends open additive shares of the same word.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t open_value_cw(const ds_level_blinds & b, uint8_t adv0, uint8_t adv1,
|
uint64_t open_value_cw(const ds_level_blinds & b, uint8_t adv0, uint8_t adv1,
|
||||||
int ai, uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
|
int ai, uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -304,6 +329,16 @@ struct local_cw_protocol
|
||||||
adv1, ai, Va, beta, mask);
|
adv1, ai, Va, beta, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open a path-paint value CW. `plant` is the scaled lose-subtree constant.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t open_planted_cw(const ds_level_blinds & b, uint8_t adv0, uint8_t adv1,
|
||||||
|
int ai, uint64_t & Va, uint64_t plant, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
return dcf_impl::make_value_cw_planted(b.L0, b.R0, b.L1, b.R1, adv0,
|
||||||
|
adv1, ai, Va, plant, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
ds_and_shares open_and(const ds_and_pads & p, simde__m128i M,
|
ds_and_shares open_and(const ds_and_pads & p, simde__m128i M,
|
||||||
uint8_t b_recv) noexcept
|
uint8_t b_recv) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -313,6 +348,7 @@ struct local_cw_protocol
|
||||||
/// Open the final comparison leaf CW. Wraps `make_final_cw` so the
|
/// Open the final comparison leaf CW. Wraps `make_final_cw` so the
|
||||||
/// Doerner–Shelat gen does not call it directly on reconstructed seeds;
|
/// Doerner–Shelat gen does not call it directly on reconstructed seeds;
|
||||||
/// an MPC backend would open additive shares of the same word.
|
/// an MPC backend would open additive shares of the same word.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t open_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
|
uint64_t open_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
|
||||||
uint64_t Va, uint64_t mask, uint64_t on_path) noexcept
|
uint64_t Va, uint64_t mask, uint64_t on_path) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -323,18 +359,81 @@ struct local_cw_protocol
|
||||||
/// the shared root sampler so the blind matches the dealer's; an MPC
|
/// the shared root sampler so the blind matches the dealer's; an MPC
|
||||||
/// backend would instead pull a group-width element from the pad stream.
|
/// backend would instead pull a group-width element from the pad stream.
|
||||||
template <typename BlockSampler>
|
template <typename BlockSampler>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
||||||
{
|
{
|
||||||
return dcf_impl::sample_addend_blind(mask,
|
return dcf_impl::sample_addend_blind(mask,
|
||||||
std::forward<BlockSampler>(sample));
|
std::forward<BlockSampler>(sample));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Majority of three bits (next carry of a full adder).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static constexpr uint8_t majority(uint8_t a, uint8_t b, uint8_t c) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<uint8_t>((a & b) | (a & c) | (b & c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One additive digit: sum bit `a XOR b XOR cin`, carry out = majority.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static constexpr uint8_t open_sum_bit(uint8_t a, uint8_t b, uint8_t cin,
|
||||||
|
uint8_t & cout) noexcept
|
||||||
|
{
|
||||||
|
cout = majority(a, b, cin);
|
||||||
|
return static_cast<uint8_t>(a ^ b ^ cin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reconstruct `a0 + a1` via a LSB→MSB carry chain, then flip the MSB
|
||||||
|
/// when the domain is signed — matching `make_dpf` on the sum. The call
|
||||||
|
/// site never forms the sum; an MPC backend would open the same bits.
|
||||||
|
template <typename InputT>
|
||||||
|
InputT open_arith_point(InputT a0, InputT a1) const
|
||||||
|
{
|
||||||
|
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||||
|
using FromI = typename utils::make_from_integral_value<InputT>::integral_type;
|
||||||
|
using U = std::make_unsigned_t<FromI>;
|
||||||
|
const U u0 = static_cast<U>(to_int(a0));
|
||||||
|
const U u1 = static_cast<U>(to_int(a1));
|
||||||
|
U sum = 0;
|
||||||
|
uint8_t carry = 0;
|
||||||
|
constexpr std::size_t nbits = utils::bitlength_of_v<InputT>;
|
||||||
|
for (std::size_t i = 0; i < nbits; ++i)
|
||||||
|
{
|
||||||
|
const uint8_t b0 = static_cast<uint8_t>((u0 >> i) & U{1});
|
||||||
|
const uint8_t b1 = static_cast<uint8_t>((u1 >> i) & U{1});
|
||||||
|
const uint8_t s = open_sum_bit(b0, b1, carry, carry);
|
||||||
|
sum = static_cast<U>(sum | (static_cast<U>(s) << i));
|
||||||
|
}
|
||||||
|
InputT out = utils::make_from_integral_value<InputT>{}(
|
||||||
|
static_cast<FromI>(sum));
|
||||||
|
utils::flip_msb_if_signed_integral(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode shares for the XOR-style CW walk. XOR mode flips party 0's MSB
|
||||||
|
/// (linear over XOR). Arithmetic mode opens the sum (carry + signed MSB)
|
||||||
|
/// and returns `(alpha, 0)` so the walk matches `make_dpf(alpha)`.
|
||||||
|
template <typename InputT>
|
||||||
|
void encode_walk_shares(InputT & x0, InputT & x1, bool arith) const
|
||||||
|
{
|
||||||
|
if (arith)
|
||||||
|
{
|
||||||
|
const InputT alpha = open_arith_point(x0, x1);
|
||||||
|
x0 = alpha;
|
||||||
|
x1 = InputT{};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
utils::flip_msb_if_signed_integral(x0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open a group of leaf correction words for one prefix group. In this
|
/// Open a group of leaf correction words for one prefix group. In this
|
||||||
/// local joint simulation both XOR shares of the point are present, so the
|
/// local joint simulation both XOR shares of the point are present, so the
|
||||||
/// point is reconstructed *inside* the protocol and handed to `leaf_fn`
|
/// point is reconstructed *inside* the protocol and handed to `leaf_fn`
|
||||||
/// (which runs `make_leaves` for the group). The Doerner–Shelat gen never
|
/// (which runs `make_leaves` for the group). The Doerner–Shelat gen never
|
||||||
/// forms `x = x0 ^ x1` at its own call site; an MPC backend would instead
|
/// forms `x = x0 ^ x1` at its own call site; an MPC backend would instead
|
||||||
/// run a per-group leaf CW exchange that never reveals `x`.
|
/// run a per-group leaf CW exchange that never reveals `x`. After
|
||||||
|
/// `encode_walk_shares`, arithmetic inputs are already `(alpha, 0)`.
|
||||||
template <typename InputT, typename LeafFn>
|
template <typename InputT, typename LeafFn>
|
||||||
void open_leaf_group(InputT x0, InputT x1, LeafFn && leaf_fn)
|
void open_leaf_group(InputT x0, InputT x1, LeafFn && leaf_fn)
|
||||||
{
|
{
|
||||||
|
|
@ -351,6 +450,7 @@ struct ds_gen_state
|
||||||
NodeT root0;
|
NodeT root0;
|
||||||
NodeT root1;
|
NodeT root1;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
void init(NodeT r0, NodeT r1) noexcept
|
void init(NodeT r0, NodeT r1) noexcept
|
||||||
{
|
{
|
||||||
root0 = r0;
|
root0 = r0;
|
||||||
|
|
@ -361,9 +461,13 @@ struct ds_gen_state
|
||||||
home[1] = 1;
|
home[1] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
NodeT & seed0() noexcept { return inbox[home[0]]; }
|
NodeT & seed0() noexcept { return inbox[home[0]]; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
NodeT & seed1() noexcept { return inbox[home[1]]; }
|
NodeT & seed1() noexcept { return inbox[home[1]]; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const NodeT & seed0() const noexcept { return inbox[home[0]]; }
|
const NodeT & seed0() const noexcept { return inbox[home[0]]; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const NodeT & seed1() const noexcept { return inbox[home[1]]; }
|
const NodeT & seed1() const noexcept { return inbox[home[1]]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -403,6 +507,27 @@ void ds_advance_level(ds_gen_state<NodeT> & st, InputT x0, InputT x1,
|
||||||
{
|
{
|
||||||
const int ai = static_cast<int>(
|
const int ai = static_cast<int>(
|
||||||
(cmp->thresh >> (cmp->nbits - 1 - level)) & 1);
|
(cmp->thresh >> (cmp->nbits - 1 - level)) & 1);
|
||||||
|
if (cmp->paint)
|
||||||
|
{
|
||||||
|
const uint64_t unit = dcf_impl::paint_unit(cmp->kind, level,
|
||||||
|
cmp->thresh, cmp->nbits, cmp->length_bits, false,
|
||||||
|
cmp->paint_cb, cmp->paint_ctx);
|
||||||
|
const uint64_t plant = dcf_impl::scale_plant(unit, cmp->beta,
|
||||||
|
cmp->mask);
|
||||||
|
*value_cw_out = proto.open_planted_cw(blinds, adv0, adv1, ai,
|
||||||
|
cmp->Va, plant, cmp->mask);
|
||||||
|
if (cmp->track_coeff)
|
||||||
|
{
|
||||||
|
const uint64_t plant1 = dcf_impl::scale_plant(unit, 1ULL,
|
||||||
|
cmp->mask);
|
||||||
|
const uint64_t v1 = proto.open_planted_cw(blinds, adv0, adv1,
|
||||||
|
ai, cmp->Va1, plant1, cmp->mask);
|
||||||
|
cmp->last_vcw_coeff =
|
||||||
|
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
*value_cw_out = proto.open_value_cw(blinds, adv0, adv1, ai, cmp->Va,
|
*value_cw_out = proto.open_value_cw(blinds, adv0, adv1, ai, cmp->Va,
|
||||||
cmp->beta, cmp->mask);
|
cmp->beta, cmp->mask);
|
||||||
if (cmp->track_coeff)
|
if (cmp->track_coeff)
|
||||||
|
|
@ -414,6 +539,7 @@ void ds_advance_level(ds_gen_state<NodeT> & st, InputT x0, InputT x1,
|
||||||
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
|
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto [cw, tpack] = proto.open_cw(blinds);
|
auto [cw, tpack] = proto.open_cw(blinds);
|
||||||
|
|
||||||
|
|
@ -475,14 +601,14 @@ template <typename InteriorPRG,
|
||||||
typename ...OutputTs,
|
typename ...OutputTs,
|
||||||
typename RootSampler,
|
typename RootSampler,
|
||||||
typename CwProtocol>
|
typename CwProtocol>
|
||||||
auto make_dpf_doerner_shelat_impl(InputT x0, InputT x1,
|
auto make_dpf_doerner_shelat_impl(bool arith, InputT x0, InputT x1,
|
||||||
RootSampler & root_sampler, CwProtocol & proto, OutputT && y,
|
RootSampler & root_sampler, CwProtocol & proto, OutputT && y,
|
||||||
OutputTs && ...ys)
|
OutputTs && ...ys)
|
||||||
{
|
{
|
||||||
static_assert(!dpf::is_wildcard_v<InputT>,
|
static_assert(!dpf::is_wildcard_v<InputT>,
|
||||||
"Doerner–Shelat gen takes XOR shares of a concrete point");
|
"Doerner–Shelat gen takes shares of a concrete point");
|
||||||
static_assert(!dpf::is_secret_share_v<InputT>,
|
static_assert(!dpf::is_secret_share_v<InputT>,
|
||||||
"Doerner–Shelat: pass additive_share of xor_wrapper, or raw XOR shares");
|
"Doerner–Shelat: pass additive_share of xor_wrapper, or raw shares");
|
||||||
static_assert(sizeof(typename InteriorPRG::block_type) == sizeof(simde__m128i),
|
static_assert(sizeof(typename InteriorPRG::block_type) == sizeof(simde__m128i),
|
||||||
"Doerner–Shelat gen uses the AES-block interior node");
|
"Doerner–Shelat gen uses the AES-block interior node");
|
||||||
|
|
||||||
|
|
@ -492,7 +618,7 @@ auto make_dpf_doerner_shelat_impl(InputT x0, InputT x1,
|
||||||
using input_type = typename dpf_type::input_type;
|
using input_type = typename dpf_type::input_type;
|
||||||
constexpr auto depth = dpf_type::depth;
|
constexpr auto depth = dpf_type::depth;
|
||||||
|
|
||||||
utils::flip_msb_if_signed_integral(x0);
|
proto.encode_walk_shares(x0, x1, arith);
|
||||||
|
|
||||||
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
|
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
|
||||||
const node root1 = dpf::set_lo_bit(static_cast<node>(root_sampler()));
|
const node root1 = dpf::set_lo_bit(static_cast<node>(root_sampler()));
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
static constexpr std::size_t num_outputs = 1 + sizeof...(OutputTs);
|
static constexpr std::size_t num_outputs = 1 + sizeof...(OutputTs);
|
||||||
static constexpr std::size_t cmp_depth = 0;
|
static constexpr std::size_t cmp_depth = 0;
|
||||||
static constexpr std::size_t cmp_out_bits = 0;
|
static constexpr std::size_t cmp_out_bits = 0;
|
||||||
|
static constexpr std::size_t cmp_block = 0;
|
||||||
|
static constexpr bool cmp_idcf = false;
|
||||||
|
static constexpr std::size_t cmp_q = 0;
|
||||||
|
static constexpr std::size_t cmp_h = 0;
|
||||||
|
static constexpr std::size_t cmp_checkpoints = 0;
|
||||||
|
static constexpr std::size_t cmp_tail = 0;
|
||||||
/// Classic keys are single-level; the unified eval surface keeps routing
|
/// Classic keys are single-level; the unified eval surface keeps routing
|
||||||
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
|
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
|
||||||
static constexpr bool is_multilevel = false;
|
static constexpr bool is_multilevel = false;
|
||||||
|
|
@ -286,6 +292,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
return std::get<I>(leaf_nodes).beaver();
|
return std::get<I>(leaf_nodes).beaver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
constexpr bool is_wildcard(std::size_t i) const noexcept
|
constexpr bool is_wildcard(std::size_t i) const noexcept
|
||||||
|
|
@ -442,41 +449,68 @@ namespace incr
|
||||||
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
|
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
|
||||||
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
|
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
|
||||||
/// place with no tree re-walk / re-PRG.
|
/// place with no tree re-walk / re-PRG.
|
||||||
template <std::size_t Depth, typename ValueCwWord, bool Wild>
|
template <std::size_t Depth, typename ValueCwWord, bool Wild,
|
||||||
|
std::size_t TailLen = 0, bool Idcf = false>
|
||||||
struct cmp_wild_state { };
|
struct cmp_wild_state { };
|
||||||
template <std::size_t Depth, typename ValueCwWord>
|
template <std::size_t Depth, typename ValueCwWord, std::size_t TailLen, bool Idcf>
|
||||||
struct cmp_wild_state<Depth, ValueCwWord, true>
|
struct cmp_wild_state<Depth, ValueCwWord, true, TailLen, Idcf>
|
||||||
{
|
{
|
||||||
std::array<ValueCwWord, Depth> value_cw_coeff{};
|
std::array<ValueCwWord, Depth> value_cw_coeff{};
|
||||||
ValueCwWord cw_last_coeff{0};
|
ValueCwWord cw_last_coeff{0};
|
||||||
|
std::array<ValueCwWord, TailLen> tail_coeff{};
|
||||||
|
std::array<ValueCwWord, Idcf ? Depth + 1 : 0> prefix_cw_coeff{};
|
||||||
bool assigned{false};
|
bool assigned{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t Depth, typename ValueCwWord, bool Wild = false>
|
template <std::size_t Depth, typename ValueCwWord, bool Wild = false,
|
||||||
|
std::size_t TailLen = 0, bool Blocked = false, bool Idcf = false>
|
||||||
struct cmp_storage
|
struct cmp_storage
|
||||||
{
|
{
|
||||||
using value_cw_word = ValueCwWord;
|
using value_cw_word = ValueCwWord;
|
||||||
using value_cw_array = std::array<value_cw_word, Depth>;
|
using value_cw_array = std::array<value_cw_word, Depth>;
|
||||||
|
using tail_array = std::array<value_cw_word, TailLen>;
|
||||||
|
static constexpr std::size_t prefix_cw_len = Idcf ? Depth + 1 : 0;
|
||||||
|
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
|
||||||
cmp_storage() = default;
|
cmp_storage() = default;
|
||||||
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
||||||
value_cw_word cw_last_in, value_cw_word cmp_addend_in)
|
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
|
||||||
|
tail_array tail = {}, tail_array tail_coeff = {},
|
||||||
|
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
|
||||||
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
||||||
cmp_addend_{cmp_addend_in} { }
|
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
|
||||||
|
{
|
||||||
|
if constexpr (Wild && Blocked)
|
||||||
|
wild_.tail_coeff = tail_coeff;
|
||||||
|
else
|
||||||
|
(void)tail_coeff;
|
||||||
|
if constexpr (Wild && Idcf)
|
||||||
|
wild_.prefix_cw_coeff = prefix_coeff;
|
||||||
|
else
|
||||||
|
(void)prefix_coeff;
|
||||||
|
}
|
||||||
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
||||||
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
|
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
|
||||||
value_cw_array coeff, value_cw_word cw_last_coeff)
|
value_cw_array coeff, value_cw_word cw_last_coeff,
|
||||||
|
tail_array tail = {}, tail_array tail_coeff = {},
|
||||||
|
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
|
||||||
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
||||||
cmp_addend_{cmp_addend_in}
|
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
|
||||||
{
|
{
|
||||||
if constexpr (Wild)
|
if constexpr (Wild)
|
||||||
{
|
{
|
||||||
wild_.value_cw_coeff = coeff;
|
wild_.value_cw_coeff = coeff;
|
||||||
wild_.cw_last_coeff = cw_last_coeff;
|
wild_.cw_last_coeff = cw_last_coeff;
|
||||||
|
if constexpr (Blocked)
|
||||||
|
wild_.tail_coeff = tail_coeff;
|
||||||
|
if constexpr (Idcf)
|
||||||
|
wild_.prefix_cw_coeff = prefix_coeff;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(void)coeff;
|
(void)coeff;
|
||||||
(void)cw_last_coeff;
|
(void)cw_last_coeff;
|
||||||
|
(void)tail_coeff;
|
||||||
|
(void)prefix_coeff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,15 +519,32 @@ struct cmp_storage
|
||||||
{
|
{
|
||||||
return static_cast<uint64_t>(value_cw_[level]);
|
return static_cast<uint64_t>(value_cw_[level]);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
|
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
const tail_array & tail_cw() const noexcept { return tail_; }
|
||||||
|
uint64_t tail_cw(std::size_t i) const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(tail_[i]);
|
||||||
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t cmp_addend() const noexcept
|
uint64_t cmp_addend() const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<uint64_t>(cmp_addend_);
|
return static_cast<uint64_t>(cmp_addend_);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
|
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool has_cmp() const noexcept { return cmp_.active; }
|
bool has_cmp() const noexcept { return cmp_.active; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
const prefix_cw_array & prefix_cws() const noexcept { return prefix_cw_; }
|
||||||
|
uint64_t prefix_cw(std::size_t i) const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(prefix_cw_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr bool is_wildcard = Wild;
|
static constexpr bool is_wildcard = Wild;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool cmp_assigned() const noexcept
|
bool cmp_assigned() const noexcept
|
||||||
{
|
{
|
||||||
if constexpr (Wild)
|
if constexpr (Wild)
|
||||||
|
|
@ -518,9 +569,27 @@ struct cmp_storage
|
||||||
const uint64_t c = static_cast<uint64_t>(wild_.value_cw_coeff[i]);
|
const uint64_t c = static_cast<uint64_t>(wild_.value_cw_coeff[i]);
|
||||||
value_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
value_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
||||||
}
|
}
|
||||||
|
if constexpr (Blocked)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < TailLen; ++i)
|
||||||
|
{
|
||||||
|
const uint64_t base = static_cast<uint64_t>(tail_[i]);
|
||||||
|
const uint64_t c = static_cast<uint64_t>(wild_.tail_coeff[i]);
|
||||||
|
tail_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
const uint64_t lbase = static_cast<uint64_t>(cw_last_);
|
const uint64_t lbase = static_cast<uint64_t>(cw_last_);
|
||||||
const uint64_t lc = static_cast<uint64_t>(wild_.cw_last_coeff);
|
const uint64_t lc = static_cast<uint64_t>(wild_.cw_last_coeff);
|
||||||
cw_last_ = static_cast<value_cw_word>((lbase + lc * delta) & mask);
|
cw_last_ = static_cast<value_cw_word>((lbase + lc * delta) & mask);
|
||||||
|
if constexpr (Idcf)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < prefix_cw_len; ++i)
|
||||||
|
{
|
||||||
|
const uint64_t base = static_cast<uint64_t>(prefix_cw_[i]);
|
||||||
|
const uint64_t c = static_cast<uint64_t>(wild_.prefix_cw_coeff[i]);
|
||||||
|
prefix_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
cmp_addend_ = static_cast<value_cw_word>(addend_share & mask);
|
cmp_addend_ = static_cast<value_cw_word>(addend_share & mask);
|
||||||
wild_.assigned = true;
|
wild_.assigned = true;
|
||||||
}
|
}
|
||||||
|
|
@ -531,14 +600,17 @@ struct cmp_storage
|
||||||
value_cw_array value_cw_{};
|
value_cw_array value_cw_{};
|
||||||
value_cw_word cw_last_{0};
|
value_cw_word cw_last_{0};
|
||||||
value_cw_word cmp_addend_{0};
|
value_cw_word cmp_addend_{0};
|
||||||
cmp_wild_state<Depth, value_cw_word, Wild> wild_{};
|
tail_array tail_{};
|
||||||
|
prefix_cw_array prefix_cw_{};
|
||||||
|
cmp_wild_state<Depth, value_cw_word, Wild, TailLen, Idcf> wild_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
|
/// Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
|
||||||
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
|
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
|
||||||
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
||||||
typename PlacedTuple, std::size_t CmpDepth = 0,
|
typename PlacedTuple, std::size_t CmpDepth = 0,
|
||||||
std::size_t CmpOutBits = 0, bool CmpWild = false>
|
std::size_t CmpOutBits = 0, bool CmpWild = false,
|
||||||
|
std::size_t CmpBlock = 0, bool CmpIdcf = false>
|
||||||
struct incr_key_base
|
struct incr_key_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -554,6 +626,26 @@ struct incr_key_base
|
||||||
static constexpr std::size_t cmp_out_bits = CmpOutBits;
|
static constexpr std::size_t cmp_out_bits = CmpOutBits;
|
||||||
/// True when the comparison payload is an unassigned wildcard.
|
/// True when the comparison payload is an unassigned wildcard.
|
||||||
static constexpr bool cmp_is_wildcard = CmpWild;
|
static constexpr bool cmp_is_wildcard = CmpWild;
|
||||||
|
/// 0 = per-level path-sum. `B >= 1` = blocked checkpoints of width `B`.
|
||||||
|
static constexpr std::size_t cmp_block = CmpBlock;
|
||||||
|
static constexpr bool cmp_idcf = CmpIdcf;
|
||||||
|
static constexpr std::size_t max_output_level =
|
||||||
|
detail::incr::max_tree_level_v<node_type, PlacedTuple>;
|
||||||
|
/// Residual tail width. 2 only when dropping those levels does not cut an
|
||||||
|
/// output and the comparison itself is what sets the tree height.
|
||||||
|
static constexpr std::size_t cmp_q = [] {
|
||||||
|
if (CmpBlock == 0 || CmpDepth <= 2)
|
||||||
|
return std::size_t{0};
|
||||||
|
if (max_output_level > CmpDepth - 2)
|
||||||
|
return std::size_t{0};
|
||||||
|
return std::size_t{2};
|
||||||
|
}();
|
||||||
|
static constexpr std::size_t cmp_h =
|
||||||
|
(CmpBlock == 0) ? CmpDepth : (CmpDepth - cmp_q);
|
||||||
|
static constexpr std::size_t cmp_checkpoints =
|
||||||
|
(CmpBlock == 0 || cmp_h == 0) ? 0 : (cmp_h + CmpBlock - 1) / CmpBlock;
|
||||||
|
static constexpr std::size_t cmp_tail =
|
||||||
|
(CmpBlock == 0 || cmp_q == 0) ? 0 : (std::size_t{1} << cmp_q);
|
||||||
/// Multi-level / comparison keys route through the slot-aware eval path.
|
/// Multi-level / comparison keys route through the slot-aware eval path.
|
||||||
static constexpr bool is_multilevel = true;
|
static constexpr bool is_multilevel = true;
|
||||||
/// Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
|
/// Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
|
||||||
|
|
@ -564,8 +656,11 @@ struct incr_key_base
|
||||||
|
|
||||||
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
|
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
|
||||||
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
|
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
|
||||||
static constexpr std::size_t depth = std::max(
|
static constexpr std::size_t depth = std::max(max_output_level,
|
||||||
detail::incr::max_tree_level_v<node_type, PlacedTuple>, CmpDepth);
|
(CmpBlock == 0) ? CmpDepth : cmp_h);
|
||||||
|
static constexpr std::size_t value_cw_len =
|
||||||
|
(CmpBlock == 0) ? depth
|
||||||
|
: (cmp_checkpoints == 0 ? std::size_t{1} : cmp_checkpoints);
|
||||||
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
|
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
|
||||||
using integral_type = utils::integral_type_from_bitlength_t<
|
using integral_type = utils::integral_type_from_bitlength_t<
|
||||||
input_bits, utils::bitlength_of_v<std::size_t>>;
|
input_bits, utils::bitlength_of_v<std::size_t>>;
|
||||||
|
|
@ -577,7 +672,10 @@ struct incr_key_base
|
||||||
|
|
||||||
using correction_words_array = std::array<interior_node, depth>;
|
using correction_words_array = std::array<interior_node, depth>;
|
||||||
using correction_advice_array = std::array<psnip_uint8_t, depth>;
|
using correction_advice_array = std::array<psnip_uint8_t, depth>;
|
||||||
using value_cw_array = std::array<value_cw_word, depth>;
|
using value_cw_array = std::array<value_cw_word, value_cw_len>;
|
||||||
|
using tail_array = std::array<value_cw_word, cmp_tail>;
|
||||||
|
static constexpr std::size_t prefix_cw_len = CmpIdcf ? depth + 1 : 0;
|
||||||
|
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
|
||||||
using meta_array = std::array<detail::incr::slot_meta, num_outputs>;
|
using meta_array = std::array<detail::incr::slot_meta, num_outputs>;
|
||||||
static constexpr meta_array meta =
|
static constexpr meta_array meta =
|
||||||
detail::incr::build_meta<node_type, PlacedTuple>();
|
detail::incr::build_meta<node_type, PlacedTuple>();
|
||||||
|
|
@ -675,14 +773,17 @@ struct incr_key_base
|
||||||
detail::cmp_meta cmp = {}, value_cw_array value_cws = {},
|
detail::cmp_meta cmp = {}, value_cw_array value_cws = {},
|
||||||
uint64_t cw_last_in = 0, uint64_t cmp_addend_in = 0,
|
uint64_t cw_last_in = 0, uint64_t cmp_addend_in = 0,
|
||||||
addend_tuple addends = {}, value_cw_array value_cw_coeff = {},
|
addend_tuple addends = {}, value_cw_array value_cw_coeff = {},
|
||||||
uint64_t cw_last_coeff_in = 0)
|
uint64_t cw_last_coeff_in = 0, tail_array tail_in = {},
|
||||||
|
tail_array tail_coeff_in = {}, prefix_cw_array prefix_in = {},
|
||||||
|
prefix_cw_array prefix_coeff_in = {})
|
||||||
: leaf_nodes{std::move(leaves)},
|
: leaf_nodes{std::move(leaves)},
|
||||||
offset_x{offset_share},
|
offset_x{offset_share},
|
||||||
cmp_store_{cmp, value_cws,
|
cmp_store_{cmp, value_cws,
|
||||||
static_cast<value_cw_word>(cw_last_in),
|
static_cast<value_cw_word>(cw_last_in),
|
||||||
static_cast<value_cw_word>(cmp_addend_in),
|
static_cast<value_cw_word>(cmp_addend_in),
|
||||||
value_cw_coeff,
|
value_cw_coeff,
|
||||||
static_cast<value_cw_word>(cw_last_coeff_in)},
|
static_cast<value_cw_word>(cw_last_coeff_in),
|
||||||
|
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
|
||||||
public_addends{std::move(addends)},
|
public_addends{std::move(addends)},
|
||||||
root_{root},
|
root_{root},
|
||||||
correction_words_{correction_words},
|
correction_words_{correction_words},
|
||||||
|
|
@ -706,10 +807,19 @@ struct incr_key_base
|
||||||
return correction_advice_;
|
return correction_advice_;
|
||||||
}
|
}
|
||||||
const value_cw_array & value_cw() const { return cmp_store_.value_cw(); }
|
const value_cw_array & value_cw() const { return cmp_store_.value_cw(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
|
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
const prefix_cw_array & prefix_cws() const noexcept
|
||||||
|
{
|
||||||
|
return cmp_store_.prefix_cws();
|
||||||
|
}
|
||||||
|
uint64_t prefix_cw(std::size_t i) const { return cmp_store_.prefix_cw(i); }
|
||||||
/// Party-local share of the constant absorb (`if_false`, or
|
/// Party-local share of the constant absorb (`if_false`, or
|
||||||
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
|
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
|
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const detail::cmp_meta & cmp() const noexcept { return cmp_store_.cmp(); }
|
const detail::cmp_meta & cmp() const noexcept { return cmp_store_.cmp(); }
|
||||||
const digest_type & common_part_hash() const { return common_part_hash_; }
|
const digest_type & common_part_hash() const { return common_part_hash_; }
|
||||||
const leaf_wrapper_tuple & leaves() const { return leaf_nodes; }
|
const leaf_wrapper_tuple & leaves() const { return leaf_nodes; }
|
||||||
|
|
@ -728,6 +838,8 @@ struct incr_key_base
|
||||||
(correction_advice_[level] >> direction) & 1);
|
(correction_advice_[level] >> direction) & 1);
|
||||||
}
|
}
|
||||||
uint64_t value_cw(std::size_t level) const { return cmp_store_.value_cw(level); }
|
uint64_t value_cw(std::size_t level) const { return cmp_store_.value_cw(level); }
|
||||||
|
const tail_array & tail_cw() const { return cmp_store_.tail_cw(); }
|
||||||
|
uint64_t tail_cw(std::size_t i) const { return cmp_store_.tail_cw(i); }
|
||||||
|
|
||||||
template <std::size_t I = 0>
|
template <std::size_t I = 0>
|
||||||
const auto & leaf() const
|
const auto & leaf() const
|
||||||
|
|
@ -812,6 +924,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t I = 0>
|
template <std::size_t I = 0>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto traverse_exterior(const interior_node & node) const noexcept
|
auto traverse_exterior(const interior_node & node) const noexcept
|
||||||
{
|
{
|
||||||
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
|
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
|
||||||
|
|
@ -836,9 +949,11 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// Public `if_false` addends for `eq` / `eq_at` slots.
|
/// Public `if_false` addends for `eq` / `eq_at` slots.
|
||||||
addend_tuple public_addends{};
|
addend_tuple public_addends{};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool has_cmp() const noexcept { return cmp_store_.has_cmp(); }
|
bool has_cmp() const noexcept { return cmp_store_.has_cmp(); }
|
||||||
/// True once a wildcard comparison payload has been assigned (always true
|
/// True once a wildcard comparison payload has been assigned (always true
|
||||||
/// for concrete cmp keys and for keys without a comparison channel).
|
/// for concrete cmp keys and for keys without a comparison channel).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool cmp_assigned() const noexcept { return cmp_store_.cmp_assigned(); }
|
bool cmp_assigned() const noexcept { return cmp_store_.cmp_assigned(); }
|
||||||
|
|
||||||
/// Patch the value CWs / `cw_last` for a resolved payload δ and install
|
/// Patch the value CWs / `cw_last` for a resolved payload δ and install
|
||||||
|
|
@ -850,7 +965,9 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cmp_storage<depth, value_cw_word, CmpWild> cmp_store_{};
|
cmp_storage<value_cw_len, value_cw_word, CmpWild, cmp_tail, (CmpBlock > 0),
|
||||||
|
CmpIdcf>
|
||||||
|
cmp_store_{};
|
||||||
interior_node root_;
|
interior_node root_;
|
||||||
correction_words_array correction_words_;
|
correction_words_array correction_words_;
|
||||||
correction_advice_array correction_advice_;
|
correction_advice_array correction_advice_;
|
||||||
|
|
@ -890,7 +1007,13 @@ using dpf_key_base_t = std::conditional_t<
|
||||||
OutputT, OutputTs...>::cmp_out_bits,
|
OutputT, OutputTs...>::cmp_out_bits,
|
||||||
dpf::detail::incr::normalize_pack<
|
dpf::detail::incr::normalize_pack<
|
||||||
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
||||||
OutputT, OutputTs...>::cmp_wild>>;
|
OutputT, OutputTs...>::cmp_wild,
|
||||||
|
dpf::detail::incr::normalize_pack<
|
||||||
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
||||||
|
OutputT, OutputTs...>::cmp_block,
|
||||||
|
dpf::detail::incr::normalize_pack<
|
||||||
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
||||||
|
OutputT, OutputTs...>::cmp_idcf>>;
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
@ -919,39 +1042,40 @@ namespace incr
|
||||||
// expanding the placed slots into the output pack and appending the phantom
|
// expanding the placed slots into the output pack and appending the phantom
|
||||||
// cmp tag when a comparison channel is present.
|
// cmp tag when a comparison channel is present.
|
||||||
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
||||||
typename InteriorPRG,
|
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
|
||||||
typename ExteriorPRG, typename InputT, typename ...Ps>
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
||||||
struct assemble_key
|
struct assemble_key
|
||||||
{
|
{
|
||||||
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
||||||
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild>>;
|
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>>;
|
||||||
};
|
};
|
||||||
template <std::size_t CmpOutBits, bool CmpWild, typename InteriorPRG,
|
template <std::size_t CmpOutBits, bool CmpWild, std::size_t CmpBlock,
|
||||||
|
bool CmpIdcf, typename InteriorPRG,
|
||||||
typename ExteriorPRG, typename InputT, typename ...Ps>
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
||||||
struct assemble_key<0, CmpOutBits, CmpWild, InteriorPRG, ExteriorPRG, InputT,
|
struct assemble_key<0, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, InteriorPRG,
|
||||||
Ps...>
|
ExteriorPRG, InputT, Ps...>
|
||||||
{
|
{
|
||||||
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...>;
|
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
||||||
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
||||||
bool CmpWild = false>
|
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
|
||||||
struct incr_dpf_key_of;
|
struct incr_dpf_key_of;
|
||||||
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
||||||
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
|
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
|
||||||
bool CmpWild>
|
bool CmpWild, std::size_t CmpBlock, bool CmpIdcf>
|
||||||
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
|
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
|
||||||
CmpDepth, CmpOutBits, CmpWild>
|
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>
|
||||||
{
|
{
|
||||||
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild,
|
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock,
|
||||||
InteriorPRG, ExteriorPRG, InputT, Ps...>::type;
|
CmpIdcf, InteriorPRG, ExteriorPRG, InputT, Ps...>::type;
|
||||||
};
|
};
|
||||||
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
||||||
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
||||||
bool CmpWild = false>
|
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
|
||||||
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
|
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
|
||||||
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild>::type;
|
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>::type;
|
||||||
|
|
||||||
} // namespace incr
|
} // namespace incr
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,10 @@ struct alignas(utils::max_align_v) dpf_output
|
||||||
subtractive_share<OutputT, Party>>;
|
subtractive_share<OutputT, Party>>;
|
||||||
|
|
||||||
dpf_output(const dpf_output &) = default;
|
dpf_output(const dpf_output &) = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dpf_output(dpf_output &&) noexcept = default;
|
dpf_output(dpf_output &&) noexcept = default;
|
||||||
dpf_output & operator=(const dpf_output &) = default;
|
dpf_output & operator=(const dpf_output &) = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dpf_output & operator=(dpf_output &&) noexcept = default;
|
dpf_output & operator=(dpf_output &&) noexcept = default;
|
||||||
~dpf_output() = default;
|
~dpf_output() = default;
|
||||||
|
|
||||||
|
|
@ -156,6 +158,7 @@ auto make_eval_dpf_output(const Node & node, Input x)
|
||||||
/// Wrap a raw comparison `Beta` value as an additive share when `KeyT` is a
|
/// Wrap a raw comparison `Beta` value as an additive share when `KeyT` is a
|
||||||
/// `party_key`.
|
/// `party_key`.
|
||||||
template <typename KeyT, typename Beta>
|
template <typename KeyT, typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto make_eval_cmp_result(Beta raw) noexcept
|
auto make_eval_cmp_result(Beta raw) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (is_party_key_v<KeyT>)
|
if constexpr (is_party_key_v<KeyT>)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
/// @file dpf/eval_full.hpp
|
/// @file dpf/eval_full.hpp
|
||||||
/// @brief
|
/// @brief Evaluate every input in the DPF domain.
|
||||||
/// @details
|
/// @details Equivalent to `eval_interval` from
|
||||||
|
/// `std::numeric_limits<input_type>::min()` through `max()`.
|
||||||
|
/// @snippet evaluation/eval_full.cpp eval-full
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -135,6 +137,7 @@ auto eval_full(const DpfKey & dpf,
|
||||||
return std::make_pair(std::move(outbufs), std::move(iterable));
|
return std::make_pair(std::move(outbufs), std::move(iterable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate the whole domain, allocating a basic full memoizer and a buffer.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,12 @@ void eval_prepare_nodes(const DpfKey & dpf, InputT from, InputT to,
|
||||||
|
|
||||||
integral_type from_node = utils::get_from_node<dpf_type>(from);
|
integral_type from_node = utils::get_from_node<dpf_type>(from);
|
||||||
integral_type to_node = utils::get_to_node<dpf_type>(to);
|
integral_type to_node = utils::get_to_node<dpf_type>(to);
|
||||||
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
|
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||||
|
const bool wraps = utils::interval_wraps(
|
||||||
|
static_cast<integral_type>(to_int(from)),
|
||||||
|
static_cast<integral_type>(to_int(to)),
|
||||||
|
utils::bitlength_of_v<InputT>);
|
||||||
|
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
|
||||||
// The memoizer keeps one interval. A wrap is two intervals, and walking
|
// The memoizer keeps one interval. A wrap is two intervals, and walking
|
||||||
// the first clobbers the second, so only a single segment can be cached.
|
// the first clobbers the second, so only a single segment can be cached.
|
||||||
if (segs.n == 1)
|
if (segs.n == 1)
|
||||||
|
|
@ -359,7 +364,12 @@ auto eval_inner_product_impl(const DpfKey & dpf, InputT from, InputT to,
|
||||||
|
|
||||||
integral_type from_node = utils::get_from_node<dpf_type>(from);
|
integral_type from_node = utils::get_from_node<dpf_type>(from);
|
||||||
integral_type to_node = utils::get_to_node<dpf_type>(to);
|
integral_type to_node = utils::get_to_node<dpf_type>(to);
|
||||||
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
|
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||||
|
const bool wraps = utils::interval_wraps(
|
||||||
|
static_cast<integral_type>(to_int(from)),
|
||||||
|
static_cast<integral_type>(to_int(to)),
|
||||||
|
utils::bitlength_of_v<InputT>);
|
||||||
|
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
|
||||||
|
|
||||||
auto accs = std::make_tuple(
|
auto accs = std::make_tuple(
|
||||||
ip_accum<typename DpfKey::concrete_output_type<Is>>{}...);
|
ip_accum<typename DpfKey::concrete_output_type<Is>>{}...);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
/// @file dpf/eval_interval.hpp
|
/// @file dpf/eval_interval.hpp
|
||||||
/// @brief
|
/// @brief Evaluate every input in a closed interval.
|
||||||
/// @details
|
/// @details `[from, to]` is inclusive. The returned iterable yields one
|
||||||
|
/// share per input, in that order. Pass a named output buffer;
|
||||||
|
/// this overload binds it as a non-const reference. An interval
|
||||||
|
/// memoizer is optional and comes after the buffer.
|
||||||
|
/// @snippet evaluation/eval_interval.cpp eval-interval
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -339,7 +343,12 @@ auto eval_interval_impl(const DpfKey & dpf, InputT from, InputT to,
|
||||||
|
|
||||||
integral_type from_node = utils::get_from_node<dpf_type>(from),
|
integral_type from_node = utils::get_from_node<dpf_type>(from),
|
||||||
to_node = utils::get_to_node<dpf_type>(to);
|
to_node = utils::get_to_node<dpf_type>(to);
|
||||||
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
|
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||||
|
const bool wraps = utils::interval_wraps(
|
||||||
|
static_cast<integral_type>(to_int(from)),
|
||||||
|
static_cast<integral_type>(to_int(to)),
|
||||||
|
utils::bitlength_of_v<InputT>);
|
||||||
|
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
|
||||||
|
|
||||||
auto idxs = std::index_sequence<IIs...>{};
|
auto idxs = std::index_sequence<IIs...>{};
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
|
|
@ -388,6 +397,10 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
/// Write outputs `I, Is...` for `[from, to]` into `outbufs`.
|
||||||
|
/// @param outbufs Named buffer, or a tuple of buffers when several outputs
|
||||||
|
/// are selected. Must outlive the returned iterable.
|
||||||
|
/// @param memoizer Workspace sized for at least this interval.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
@ -404,6 +417,7 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
|
||||||
return internal::eval_interval<I, Is...>(dpf, dpf.offset_x(from), dpf.offset_x(to), outbufs, memoizer, std::make_index_sequence<1+sizeof...(Is)>());
|
return internal::eval_interval<I, Is...>(dpf, dpf.offset_x(from), dpf.offset_x(to), outbufs, memoizer, std::make_index_sequence<1+sizeof...(Is)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate `[from, to]` into `outbufs`, allocating a basic interval memoizer.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
@ -421,6 +435,9 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
|
||||||
dpf::make_basic_interval_memoizer<DpfKey>(from, to));
|
dpf::make_basic_interval_memoizer<DpfKey>(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate `[from, to]` with a caller-supplied memoizer.
|
||||||
|
/// @return `std::pair` of a new buffer (or tuple of buffers) and an iterable
|
||||||
|
/// into that buffer.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
@ -445,6 +462,7 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
|
||||||
return std::make_pair(std::move(outbufs), std::move(iterable));
|
return std::make_pair(std::move(outbufs), std::move(iterable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate `[from, to]`, allocating a basic interval memoizer and a buffer.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
/// @file dpf/eval_point.hpp
|
/// @file dpf/eval_point.hpp
|
||||||
/// @brief
|
/// @brief Evaluate one DPF input.
|
||||||
/// @details
|
/// @details `eval_point(key, x)` returns a handle; `*handle` is that party's
|
||||||
|
/// share of output 0. `eval_point<I>` selects another output.
|
||||||
|
/// `eval_point<I0, I1, ...>` returns a tuple of shares.
|
||||||
|
/// Pass a `basic_path_memoizer` lvalue to resume a previous path.
|
||||||
|
/// An unassigned wildcard output throws `std::runtime_error`.
|
||||||
|
/// @snippet evaluation/eval_point.cpp eval-point
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -72,6 +77,10 @@ auto eval_point(const DpfKey & dpf, InputT && x, PathMemoizer && path)
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
/// Evaluate output `I` at `x`.
|
||||||
|
/// @param path Mutable path memoizer. The default is a fresh
|
||||||
|
/// nonmemoizing workspace for this call.
|
||||||
|
/// @return Handle whose `operator*` is the party's share.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
typename InputT,
|
typename InputT,
|
||||||
|
|
@ -88,6 +97,8 @@ auto eval_point(const DpfKey & dpf, InputT && x, PathMemoizer && path = PathMemo
|
||||||
internal::eval_point<I>(dpf, tx, path), tx);
|
internal::eval_point<I>(dpf, tx, path), tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate several outputs at `x`.
|
||||||
|
/// @return Tuple of shares, already dereferenced.
|
||||||
template <std::size_t I0,
|
template <std::size_t I0,
|
||||||
std::size_t I1,
|
std::size_t I1,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
/// @file dpf/eval_sequence.hpp
|
/// @file dpf/eval_sequence.hpp
|
||||||
/// @brief
|
/// @brief Evaluate a sorted list of DPF inputs.
|
||||||
/// @details
|
/// @details The range is nondecreasing; an unsorted range throws
|
||||||
|
/// `std::runtime_error`. `return_output_only_tag_` stores one share
|
||||||
|
/// per point. `return_entire_node_tag_` stores whole leaves and is
|
||||||
|
/// the default. A `sequence_recipe` repeats the list, and a sequence
|
||||||
|
/// memoizer bound to that recipe object resumes the traversal.
|
||||||
|
/// @snippet evaluation/eval_sequence.cpp eval-sequence
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -143,6 +148,9 @@ inline auto eval_sequence(const DpfKey & dpf, ForwardIterator begin, ForwardIter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate the sorted range `[begin, end)`, allocating a buffer.
|
||||||
|
/// @param return_type `return_entire_node_tag_{}` or `return_output_only_tag_{}`.
|
||||||
|
/// @return Pair of buffer (or tuple of buffers) and an iterable in list order.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
@ -446,6 +454,9 @@ auto eval_sequence(const DpfKey & dpf, const sequence_recipe & recipe,
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
/// Evaluate `recipe` into a named buffer, reusing `memoizer`.
|
||||||
|
/// @param recipe The same object `memoizer` was constructed from.
|
||||||
|
/// @param outbufs Named buffer. The returned iterable refers into it.
|
||||||
template <std::size_t I = 0,
|
template <std::size_t I = 0,
|
||||||
std::size_t ...Is,
|
std::size_t ...Is,
|
||||||
typename DpfKey,
|
typename DpfKey,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,15 @@ struct cmp_t
|
||||||
};
|
};
|
||||||
inline constexpr cmp_t cmp{};
|
inline constexpr cmp_t cmp{};
|
||||||
|
|
||||||
|
/// Prefix of an `idcf` comparison. `L` is the number of leading bits.
|
||||||
|
template <std::size_t L>
|
||||||
|
struct cmp_prefix_t
|
||||||
|
{
|
||||||
|
static constexpr std::size_t length = L;
|
||||||
|
};
|
||||||
|
template <std::size_t L>
|
||||||
|
inline constexpr cmp_prefix_t<L> cmp_prefix{};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_out : std::false_type
|
struct is_out : std::false_type
|
||||||
{
|
{
|
||||||
|
|
@ -53,10 +62,22 @@ struct is_cmp_target : std::bool_constant<std::is_same_v<std::decay_t<T>, cmp_t>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline constexpr bool is_cmp_target_v = is_cmp_target<T>::value;
|
inline constexpr bool is_cmp_target_v = is_cmp_target<T>::value;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_cmp_prefix_target : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <std::size_t L>
|
||||||
|
struct is_cmp_prefix_target<cmp_prefix_t<L>> : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr bool is_cmp_prefix_target_v =
|
||||||
|
is_cmp_prefix_target<std::decay_t<T>>::value;
|
||||||
|
|
||||||
/// True for channel tags that must not bind as the key in classic eval_*.
|
/// True for channel tags that must not bind as the key in classic eval_*.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline constexpr bool is_eval_channel_tag_v =
|
inline constexpr bool is_eval_channel_tag_v =
|
||||||
is_out_v<T> || is_cmp_target_v<T>;
|
is_out_v<T> || is_cmp_target_v<T> || is_cmp_prefix_target_v<T>;
|
||||||
|
|
||||||
template <typename T, typename = void>
|
template <typename T, typename = void>
|
||||||
struct looks_like_dpf_key : std::false_type
|
struct looks_like_dpf_key : std::false_type
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
/// @file dpf/eval_unified.hpp
|
/// @file dpf/eval_unified.hpp
|
||||||
/// @brief Target-first eval surface for DPF / iDPF / DCF channels.
|
/// @brief Target-first eval surface for DPF / iDPF / DCF channels.
|
||||||
/// @details `eval_*(out<I>, …)` and `eval_*(cmp, …)` are the public API.
|
/// @details `eval_*(out<I>, …)` selects point-output slot `I`.
|
||||||
|
/// `eval_*(cmp, …)` selects the comparison channel. Memoizer and
|
||||||
|
/// buffer arguments match the classic overloads: a path memoizer
|
||||||
|
/// on `eval_point`, an output buffer then an interval memoizer on
|
||||||
|
/// `eval_interval`. `make_output_buffer(out<I>, key, from, to)` and
|
||||||
|
/// `make_output_buffer(cmp, key, n)` size the buffer for that channel.
|
||||||
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
|
@ -38,6 +43,7 @@ namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
template <std::size_t I, std::size_t N, typename KeyT>
|
template <std::size_t I, std::size_t N, typename KeyT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr std::size_t resolved_out_prefix() noexcept
|
constexpr std::size_t resolved_out_prefix() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (is_multilevel_key_v<KeyT>)
|
if constexpr (is_multilevel_key_v<KeyT>)
|
||||||
|
|
@ -91,6 +97,15 @@ auto eval_point(cmp_t, const KeyT & key, QueryT && x,
|
||||||
std::forward<PathMemoizer>(path));
|
std::forward<PathMemoizer>(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <std::size_t L, typename Beta = uint64_t, typename KeyT, typename QueryT,
|
||||||
|
typename PathMemoizer = basic_path_memoizer<KeyT>>
|
||||||
|
auto eval_point(cmp_prefix_t<L>, const KeyT & key, QueryT && x,
|
||||||
|
PathMemoizer && path = PathMemoizer{})
|
||||||
|
{
|
||||||
|
return detail::incr::eval_cmp_prefix_point_impl<L, Beta>(key,
|
||||||
|
std::forward<QueryT>(x), std::forward<PathMemoizer>(path));
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// eval_interval(target, key, from, to [, buf [, memo]])
|
// eval_interval(target, key, from, to [, buf [, memo]])
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -388,11 +403,12 @@ auto eval_out_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
|
||||||
utils::flip_msb_if_signed_integral(from);
|
utils::flip_msb_if_signed_integral(from);
|
||||||
utils::flip_msb_if_signed_integral(to);
|
utils::flip_msb_if_signed_integral(to);
|
||||||
|
|
||||||
integral_type from_node = utils::leaf_node_floor(
|
const auto from_i = static_cast<integral_type>(to_int(from));
|
||||||
static_cast<integral_type>(to_int(from)), lg_opl);
|
const auto to_i = static_cast<integral_type>(to_int(to));
|
||||||
integral_type to_node = utils::leaf_node_ceil_exclusive(
|
integral_type from_node = utils::leaf_node_floor(from_i, lg_opl);
|
||||||
static_cast<integral_type>(to_int(to)), lg_opl);
|
integral_type to_node = utils::leaf_node_ceil_exclusive(to_i, lg_opl);
|
||||||
const auto segs = utils::split_leaf_nodes(from_node, to_node, to_level);
|
const bool wraps = utils::interval_wraps(from_i, to_i, N);
|
||||||
|
const auto segs = utils::split_leaf_nodes(from_node, to_node, to_level, wraps);
|
||||||
|
|
||||||
ml_ip_accum<output_type, exterior_node> acc{};
|
ml_ip_accum<output_type, exterior_node> acc{};
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
|
|
@ -437,15 +453,27 @@ Beta eval_cmp_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
|
||||||
constexpr std::size_t stop =
|
constexpr std::size_t stop =
|
||||||
KeyT::cmp_depth == 0 ? KeyT::depth : KeyT::cmp_depth;
|
KeyT::cmp_depth == 0 ? KeyT::depth : KeyT::cmp_depth;
|
||||||
detail::incr::cmp_full_interval_memo<KeyT, stop> memo{count};
|
detail::incr::cmp_full_interval_memo<KeyT, stop> memo{count};
|
||||||
|
const std::size_t levels = unwrap_party_key_t<KeyT>::cmp_block > 0
|
||||||
|
? unwrap_party_key_t<KeyT>::cmp_h : nbits;
|
||||||
detail::incr::eval_cmp_interval_impl_interior(dpf, a, cmp_exclusive_end(b),
|
detail::incr::eval_cmp_interval_impl_interior(dpf, a, cmp_exclusive_end(b),
|
||||||
nbits, memo);
|
nbits, memo, levels);
|
||||||
|
|
||||||
uint64_t dot = 0;
|
uint64_t dot = 0;
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
const auto q = static_cast<integral>(a + static_cast<integral>(i));
|
const auto q = static_cast<integral>(a + static_cast<integral>(i));
|
||||||
const uint64_t raw =
|
const uint64_t raw = [&] {
|
||||||
detail::incr::eval_cmp_from_interval_memo(dpf, q, a, nbits, memo);
|
if constexpr (unwrap_party_key_t<KeyT>::cmp_block > 0)
|
||||||
|
{
|
||||||
|
return detail::blocked::eval_share_memo(dpf, q, a,
|
||||||
|
cmp_exclusive_end(b), memo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return detail::incr::eval_cmp_from_interval_memo(
|
||||||
|
dpf, q, a, nbits, memo);
|
||||||
|
}
|
||||||
|
}();
|
||||||
const uint64_t wt = static_cast<uint64_t>(weights[i]) & mask;
|
const uint64_t wt = static_cast<uint64_t>(weights[i]) & mask;
|
||||||
dot = (dot + ((raw & mask) * wt)) & mask;
|
dot = (dot + ((raw & mask) * wt)) & mask;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@
|
||||||
/// nodes are identical across the two parties, so a dummy word
|
/// nodes are identical across the two parties, so a dummy word
|
||||||
/// cancels.
|
/// cancels.
|
||||||
///
|
///
|
||||||
/// A wildcard-input call takes additive shares of the real point and
|
/// Default calls take XOR shares of the point. Tagged with
|
||||||
/// a public query. It samples a random target, runs geneval there,
|
/// `arith_input`, the point is the ring sum of the two shares; path
|
||||||
/// and shifts the query by `target - x`, which is what
|
/// bits are opened by a carry chain inside the local CW protocol so
|
||||||
/// `offset_x` does after a wildcard key is bound to `x`.
|
/// the words match `make_dpf(x0 + x1)` at the caller's query.
|
||||||
///
|
///
|
||||||
/// `geneval_cmp` is the comparison-channel form. The value-correction
|
/// `geneval_cmp` is the comparison-channel form. The value-correction
|
||||||
/// word is a function of the secret path at every level, so the walk
|
/// word is a function of the secret path at every level, so the walk
|
||||||
|
|
@ -50,13 +50,6 @@
|
||||||
namespace dpf
|
namespace dpf
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Tag for a geneval whose point is known only as additive shares.
|
|
||||||
struct wildcard_input_t
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr wildcard_input_t wildcard_input{};
|
|
||||||
|
|
||||||
/// Shares and the correction words opened along the query trie.
|
/// Shares and the correction words opened along the query trie.
|
||||||
/// `correction_words[i]` / `correction_advice[i]` match a reusable key at
|
/// `correction_words[i]` / `correction_advice[i]` match a reusable key at
|
||||||
/// the same target for every `i < live_levels`. `leaf_live` means the
|
/// the same target for every `i < live_levels`. `leaf_live` means the
|
||||||
|
|
@ -78,6 +71,7 @@ namespace detail
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
T geneval_mod_add(T a, T b) noexcept
|
T geneval_mod_add(T a, T b) noexcept
|
||||||
{
|
{
|
||||||
using U = std::make_unsigned_t<T>;
|
using U = std::make_unsigned_t<T>;
|
||||||
|
|
@ -87,17 +81,6 @@ T geneval_mod_add(T a, T b) noexcept
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
|
||||||
T geneval_mod_sub(T a, T b) noexcept
|
|
||||||
{
|
|
||||||
using U = std::make_unsigned_t<T>;
|
|
||||||
U diff = static_cast<U>(static_cast<U>(a) - static_cast<U>(b));
|
|
||||||
T out;
|
|
||||||
std::memcpy(&out, &diff, sizeof(out));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T geneval_flipped(T x)
|
T geneval_flipped(T x)
|
||||||
{
|
{
|
||||||
|
|
@ -151,8 +134,9 @@ template <typename InteriorPRG,
|
||||||
typename OutputT,
|
typename OutputT,
|
||||||
typename RootSampler,
|
typename RootSampler,
|
||||||
typename PadRng>
|
typename PadRng>
|
||||||
auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
|
auto geneval_run(bool arith, InputT x0, InputT x1,
|
||||||
RootSampler & root_sampler, PadRng & pads, OutputT y)
|
const std::vector<InputT> & queries, RootSampler & root_sampler,
|
||||||
|
PadRng & pads, OutputT y)
|
||||||
{
|
{
|
||||||
static_assert(std::is_integral_v<InputT>,
|
static_assert(std::is_integral_v<InputT>,
|
||||||
"geneval input shares are an integral domain");
|
"geneval input shares are an integral domain");
|
||||||
|
|
@ -172,9 +156,10 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
|
||||||
if (queries.size() > (std::size_t{1} << 22))
|
if (queries.size() > (std::size_t{1} << 22))
|
||||||
throw std::length_error("geneval query is too large");
|
throw std::length_error("geneval query is too large");
|
||||||
|
|
||||||
|
local_cw_protocol<PadRng> proto{pads};
|
||||||
InputT x0c = x0;
|
InputT x0c = x0;
|
||||||
InputT x1c = x1;
|
InputT x1c = x1;
|
||||||
utils::flip_msb_if_signed_integral(x0c);
|
proto.encode_walk_shares(x0c, x1c, arith);
|
||||||
const InputT alpha = utils::xor_input_shares(x0c, x1c);
|
const InputT alpha = utils::xor_input_shares(x0c, x1c);
|
||||||
|
|
||||||
std::vector<InputT> flipped;
|
std::vector<InputT> flipped;
|
||||||
|
|
@ -196,7 +181,6 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
|
||||||
|
|
||||||
const uint64_t secret_leaf = geneval_leaf_id<dpf_type>(alpha);
|
const uint64_t secret_leaf = geneval_leaf_id<dpf_type>(alpha);
|
||||||
|
|
||||||
local_cw_protocol<PadRng> proto{pads};
|
|
||||||
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
||||||
|
|
||||||
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
|
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
|
||||||
|
|
@ -342,6 +326,19 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename InteriorPRG,
|
||||||
|
typename ExteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename OutputT,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng>
|
||||||
|
auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
|
||||||
|
RootSampler & root_sampler, PadRng & pads, OutputT y)
|
||||||
|
{
|
||||||
|
return geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1, queries,
|
||||||
|
root_sampler, pads, y);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename InputT>
|
template <typename InputT>
|
||||||
InputT geneval_from_bits(uint64_t bits)
|
InputT geneval_from_bits(uint64_t bits)
|
||||||
{
|
{
|
||||||
|
|
@ -401,22 +398,6 @@ std::vector<InputT> geneval_inclusive(InputT from, InputT to)
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InputT, typename TargetSampler>
|
|
||||||
InputT geneval_sample_target(TargetSampler & sample)
|
|
||||||
{
|
|
||||||
return static_cast<InputT>(sample());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InputT>
|
|
||||||
std::vector<InputT> geneval_shift_all(const std::vector<InputT> & qs, InputT delta)
|
|
||||||
{
|
|
||||||
std::vector<InputT> out;
|
|
||||||
out.reserve(qs.size());
|
|
||||||
for (const InputT & q : qs)
|
|
||||||
out.push_back(geneval_mod_add(q, delta));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/// Geneval at one public point. The secret point is `x0 XOR x1`.
|
/// Geneval at one public point. The secret point is `x0 XOR x1`.
|
||||||
|
|
@ -430,7 +411,22 @@ HEDLEY_WARN_UNUSED_RESULT
|
||||||
auto geneval_point(InputT x0, InputT x1, InputT query,
|
auto geneval_point(InputT x0, InputT x1, InputT query,
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
{
|
{
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
|
||||||
|
std::vector<InputT>{query}, rng.root, rng.pad, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Geneval at one public point. The secret point is `x0 + x1`.
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename OutputT,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto geneval_point(arith_input_t, InputT x0, InputT x1, InputT query,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
|
{
|
||||||
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
|
||||||
std::vector<InputT>{query}, rng.root, rng.pad, y);
|
std::vector<InputT>{query}, rng.root, rng.pad, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,7 +441,21 @@ HEDLEY_WARN_UNUSED_RESULT
|
||||||
auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to,
|
auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to,
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
{
|
{
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
|
||||||
|
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename OutputT,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto geneval_interval(arith_input_t, InputT x0, InputT x1, InputT from,
|
||||||
|
InputT to, ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
|
{
|
||||||
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
|
||||||
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
|
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,7 +470,21 @@ HEDLEY_WARN_UNUSED_RESULT
|
||||||
auto geneval_full(InputT x0, InputT x1,
|
auto geneval_full(InputT x0, InputT x1,
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
{
|
{
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
|
||||||
|
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename OutputT,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto geneval_full(arith_input_t, InputT x0, InputT x1,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
|
{
|
||||||
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
|
||||||
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
|
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,154 +501,10 @@ auto geneval_sequence(InputT x0, InputT x1, ForwardIterator begin,
|
||||||
ForwardIterator end, ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
ForwardIterator end, ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
{
|
{
|
||||||
std::vector<InputT> qs(begin, end);
|
std::vector<InputT> qs(begin, end);
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
|
||||||
std::move(qs), rng.root, rng.pad, y);
|
std::move(qs), rng.root, rng.pad, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wildcard-input geneval. `x0 + x1` is the real point (additive shares).
|
|
||||||
/// `sample_target()` is the random DPF target; the public query is shifted
|
|
||||||
/// by `target - (x0 + x1)` before the walk.
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng,
|
|
||||||
typename TargetSampler>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_point(wildcard_input_t, InputT x0, InputT x1, InputT query,
|
|
||||||
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target,
|
|
||||||
OutputT y)
|
|
||||||
{
|
|
||||||
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
|
|
||||||
const InputT delta = detail::geneval_mod_sub(alpha,
|
|
||||||
detail::geneval_mod_add(x0, x1));
|
|
||||||
const InputT shifted = detail::geneval_mod_add(query, delta);
|
|
||||||
InputT zero{};
|
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
|
|
||||||
std::vector<InputT>{shifted}, rng.root, rng.pad, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_point(wildcard_input_t, InputT x0, InputT x1, InputT query,
|
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
|
||||||
{
|
|
||||||
return geneval_point<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1, query,
|
|
||||||
std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng,
|
|
||||||
typename TargetSampler>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_interval(wildcard_input_t, InputT x0, InputT x1, InputT from,
|
|
||||||
InputT to, ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target,
|
|
||||||
OutputT y)
|
|
||||||
{
|
|
||||||
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
|
|
||||||
const InputT delta = detail::geneval_mod_sub(alpha,
|
|
||||||
detail::geneval_mod_add(x0, x1));
|
|
||||||
auto shifted = detail::geneval_shift_all(
|
|
||||||
detail::geneval_inclusive(from, to), delta);
|
|
||||||
InputT zero{};
|
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
|
|
||||||
std::move(shifted), rng.root, rng.pad, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_interval(wildcard_input_t, InputT x0, InputT x1, InputT from,
|
|
||||||
InputT to, ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
|
||||||
{
|
|
||||||
return geneval_interval<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
|
|
||||||
from, to, std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng,
|
|
||||||
typename TargetSampler>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_full(wildcard_input_t, InputT x0, InputT x1,
|
|
||||||
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target, OutputT y)
|
|
||||||
{
|
|
||||||
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
|
|
||||||
const InputT delta = detail::geneval_mod_sub(alpha,
|
|
||||||
detail::geneval_mod_add(x0, x1));
|
|
||||||
InputT zero{};
|
|
||||||
auto full = detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
|
|
||||||
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
|
|
||||||
constexpr auto to_int = utils::to_integral_type<InputT>{};
|
|
||||||
const std::size_t n = full.party0.size();
|
|
||||||
std::vector<OutputT> p0(n), p1(n);
|
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
InputT q = detail::geneval_from_bits<InputT>(i);
|
|
||||||
InputT s = detail::geneval_mod_add(q, delta);
|
|
||||||
const std::size_t si = static_cast<std::size_t>(to_int(s));
|
|
||||||
p0[i] = full.party0[si];
|
|
||||||
p1[i] = full.party1[si];
|
|
||||||
}
|
|
||||||
full.party0 = std::move(p0);
|
|
||||||
full.party1 = std::move(p1);
|
|
||||||
return full;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_full(wildcard_input_t, InputT x0, InputT x1,
|
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
|
||||||
{
|
|
||||||
return geneval_full<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
|
|
||||||
std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
|
||||||
typename ExteriorPRG = InteriorPRG,
|
|
||||||
typename InputT,
|
|
||||||
typename OutputT,
|
|
||||||
typename RootSampler,
|
|
||||||
typename PadRng,
|
|
||||||
typename ForwardIterator,
|
|
||||||
typename TargetSampler>
|
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
|
||||||
auto geneval_sequence(wildcard_input_t, InputT x0, InputT x1,
|
|
||||||
ForwardIterator begin, ForwardIterator end,
|
|
||||||
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target, OutputT y)
|
|
||||||
{
|
|
||||||
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
|
|
||||||
const InputT delta = detail::geneval_mod_sub(alpha,
|
|
||||||
detail::geneval_mod_add(x0, x1));
|
|
||||||
std::vector<InputT> qs(begin, end);
|
|
||||||
auto shifted = detail::geneval_shift_all(qs, delta);
|
|
||||||
InputT zero{};
|
|
||||||
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
|
|
||||||
std::move(shifted), rng.root, rng.pad, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename InteriorPRG = dpf::prg::aes128,
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
typename ExteriorPRG = InteriorPRG,
|
typename ExteriorPRG = InteriorPRG,
|
||||||
typename InputT,
|
typename InputT,
|
||||||
|
|
@ -633,12 +513,13 @@ template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
typename PadRng,
|
typename PadRng,
|
||||||
typename ForwardIterator>
|
typename ForwardIterator>
|
||||||
HEDLEY_WARN_UNUSED_RESULT
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
auto geneval_sequence(wildcard_input_t, InputT x0, InputT x1,
|
auto geneval_sequence(arith_input_t, InputT x0, InputT x1,
|
||||||
ForwardIterator begin, ForwardIterator end,
|
ForwardIterator begin, ForwardIterator end,
|
||||||
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
ds_randomness<RootSampler, PadRng> rng, OutputT y)
|
||||||
{
|
{
|
||||||
return geneval_sequence<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
|
std::vector<InputT> qs(begin, end);
|
||||||
begin, end, std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
|
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
|
||||||
|
std::move(qs), rng.root, rng.pad, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opened comparison key material and one prefix share per endpoint.
|
/// Opened comparison key material and one prefix share per endpoint.
|
||||||
|
|
@ -651,6 +532,7 @@ struct geneval_cmp_result
|
||||||
std::vector<simde__m128i, aligned_allocator<simde__m128i>> correction_words;
|
std::vector<simde__m128i, aligned_allocator<simde__m128i>> correction_words;
|
||||||
std::vector<uint8_t> correction_advice;
|
std::vector<uint8_t> correction_advice;
|
||||||
std::vector<uint64_t> value_cw;
|
std::vector<uint64_t> value_cw;
|
||||||
|
std::vector<uint64_t> tail_cw;
|
||||||
uint64_t cw_last = 0;
|
uint64_t cw_last = 0;
|
||||||
uint64_t addend0 = 0;
|
uint64_t addend0 = 0;
|
||||||
uint64_t addend1 = 0;
|
uint64_t addend1 = 0;
|
||||||
|
|
@ -689,11 +571,76 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
|
||||||
out.addend1 = k1.cmp_addend().raw();
|
out.addend1 = k1.cmp_addend().raw();
|
||||||
out.correction_words.resize(depth);
|
out.correction_words.resize(depth);
|
||||||
out.correction_advice.resize(depth);
|
out.correction_advice.resize(depth);
|
||||||
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
out.value_cw.resize(key_type::cmp_checkpoints);
|
||||||
|
for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i)
|
||||||
|
out.value_cw[i] = k0.value_cw(i);
|
||||||
|
out.tail_cw.resize(key_type::cmp_tail);
|
||||||
|
for (std::size_t z = 0; z < key_type::cmp_tail; ++z)
|
||||||
|
out.tail_cw[z] = k0.tail_cw(z);
|
||||||
|
}
|
||||||
|
else
|
||||||
out.value_cw.resize(depth);
|
out.value_cw.resize(depth);
|
||||||
for (std::size_t level = 0; level < depth; ++level)
|
for (std::size_t level = 0; level < depth; ++level)
|
||||||
{
|
{
|
||||||
out.correction_words[level] = k0.correction_word(level);
|
out.correction_words[level] = k0.correction_word(level);
|
||||||
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
|
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
|
||||||
|
if constexpr (key_type::cmp_block == 0)
|
||||||
|
out.value_cw[level] = k0.value_cw(level);
|
||||||
|
}
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw());
|
||||||
|
out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw());
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Comparison geneval with additive shares of the point (`x0 + x1`).
|
||||||
|
template <typename InputT,
|
||||||
|
typename ForwardIterator,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng,
|
||||||
|
typename Spec>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
|
||||||
|
ForwardIterator begin, ForwardIterator end,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, Spec spec)
|
||||||
|
{
|
||||||
|
geneval_cmp_result out;
|
||||||
|
if (begin == end)
|
||||||
|
return out;
|
||||||
|
|
||||||
|
auto keys = make_dpf_doerner_shelat(arith_input, std::move(x0), std::move(x1),
|
||||||
|
std::move(rng), std::move(spec));
|
||||||
|
const auto & k0 = keys.first;
|
||||||
|
const auto & k1 = keys.second;
|
||||||
|
using key_type = std::decay_t<decltype(k0)>;
|
||||||
|
constexpr std::size_t depth = key_type::depth;
|
||||||
|
out.live_levels = depth;
|
||||||
|
out.mask = k0.cmp().mask;
|
||||||
|
out.cw_last = k0.cw_last();
|
||||||
|
out.addend0 = k0.cmp_addend().raw();
|
||||||
|
out.addend1 = k1.cmp_addend().raw();
|
||||||
|
out.correction_words.resize(depth);
|
||||||
|
out.correction_advice.resize(depth);
|
||||||
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
out.value_cw.resize(key_type::cmp_checkpoints);
|
||||||
|
for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i)
|
||||||
|
out.value_cw[i] = k0.value_cw(i);
|
||||||
|
out.tail_cw.resize(key_type::cmp_tail);
|
||||||
|
for (std::size_t z = 0; z < key_type::cmp_tail; ++z)
|
||||||
|
out.tail_cw[z] = k0.tail_cw(z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
out.value_cw.resize(depth);
|
||||||
|
for (std::size_t level = 0; level < depth; ++level)
|
||||||
|
{
|
||||||
|
out.correction_words[level] = k0.correction_word(level);
|
||||||
|
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
|
||||||
|
if constexpr (key_type::cmp_block == 0)
|
||||||
out.value_cw[level] = k0.value_cw(level);
|
out.value_cw[level] = k0.value_cw(level);
|
||||||
}
|
}
|
||||||
for (auto it = begin; it != end; ++it)
|
for (auto it = begin; it != end; ++it)
|
||||||
|
|
@ -718,6 +665,19 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
|
||||||
std::move(rng), dpf::gt(beta));
|
std::move(rng), dpf::gt(beta));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename InputT,
|
||||||
|
typename ForwardIterator,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
|
||||||
|
ForwardIterator begin, ForwardIterator end,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, uint64_t beta)
|
||||||
|
{
|
||||||
|
return geneval_cmp(arith_input, std::move(x0), std::move(x1), begin, end,
|
||||||
|
std::move(rng), dpf::gt(beta));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dpf
|
} // namespace dpf
|
||||||
|
|
||||||
#endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__
|
#endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
674
include/dpf/interval.hpp
Normal file
674
include/dpf/interval.hpp
Normal file
|
|
@ -0,0 +1,674 @@
|
||||||
|
/// @file dpf/interval.hpp
|
||||||
|
/// @brief Public-bound interval containment on one comparison key.
|
||||||
|
/// @details `make_dpf(r, ic(p, q, β))` hides the mask `r` and the payload `β`.
|
||||||
|
/// The bounds are public. Reconstruction is `β` when
|
||||||
|
/// `p ≤ (x − r) mod 2^n ≤ q`, and the false payload otherwise.
|
||||||
|
///
|
||||||
|
/// The key is one `lt` comparison at `γ = r − 1`, the Boyle–Chandran–
|
||||||
|
/// Gilboa–Gupta–Ishai–Kumar–Rathee reduction (EUROCRYPT 2021, Fig. 3).
|
||||||
|
/// Evaluation walks that key at the two public shifts of `x` and adds
|
||||||
|
/// a secret-shared correction. Seed corrections, advice bits, leaves,
|
||||||
|
/// and the path memoizer stay single-path.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
|
#include "dpf/dcf.hpp"
|
||||||
|
#include "dpf/eval_unified.hpp"
|
||||||
|
#include "dpf/geneval.hpp"
|
||||||
|
#include "dpf/incremental.hpp"
|
||||||
|
#include "dpf/output_buffer.hpp"
|
||||||
|
#include "dpf/secret_share.hpp"
|
||||||
|
#include "dpf/utils.hpp"
|
||||||
|
|
||||||
|
namespace dpf
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Beta>
|
||||||
|
struct ic_pack
|
||||||
|
{
|
||||||
|
static constexpr bool is_ic = true;
|
||||||
|
using beta_type = Beta;
|
||||||
|
uint64_t lo = 0;
|
||||||
|
uint64_t hi = 0;
|
||||||
|
Beta if_true{};
|
||||||
|
Beta if_false{};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Spec tag and factory. `dpf::ic(p, q, beta)` builds a pack;
|
||||||
|
/// `eval_point(dpf::ic, key, x)` evaluates it.
|
||||||
|
struct ic_fn
|
||||||
|
{
|
||||||
|
template <typename Lo, typename Hi, typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
ic_pack<std::decay_t<Beta>> operator()(Lo lo, Hi hi, Beta t,
|
||||||
|
Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>()) const
|
||||||
|
{
|
||||||
|
ic_pack<std::decay_t<Beta>> spec;
|
||||||
|
spec.lo = static_cast<uint64_t>(lo);
|
||||||
|
spec.hi = static_cast<uint64_t>(hi);
|
||||||
|
spec.if_true = std::move(t);
|
||||||
|
spec.if_false = std::move(f);
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr ic_fn ic{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_ic_key : std::false_type {};
|
||||||
|
|
||||||
|
template <std::size_t Party, typename Key, typename Input, typename Beta>
|
||||||
|
struct ic_key
|
||||||
|
{
|
||||||
|
static constexpr std::size_t party = Party;
|
||||||
|
static constexpr bool wildcard = Key::cmp_is_wildcard;
|
||||||
|
using input_type = Input;
|
||||||
|
using key_type = party_key<Party, Key>;
|
||||||
|
using beta_type = Beta;
|
||||||
|
|
||||||
|
key_type key;
|
||||||
|
uint64_t lo = 0;
|
||||||
|
uint64_t hi = 0;
|
||||||
|
uint64_t input_mask = 0;
|
||||||
|
uint64_t group_mask = 0;
|
||||||
|
/// Share of `δ`. Public `c_x ∈ {-1,0,1}` scales it locally.
|
||||||
|
uint64_t delta_share = 0;
|
||||||
|
/// Share of `δ · c_r + if_false`.
|
||||||
|
uint64_t cr_share = 0;
|
||||||
|
/// Wildcard only: shares of `1` and of `c_r`, scaled by `δ` in `assign_cmp`.
|
||||||
|
uint64_t delta_coeff = 0;
|
||||||
|
uint64_t cr_coeff = 0;
|
||||||
|
bool assigned = !wildcard;
|
||||||
|
|
||||||
|
ic_key(key_type k, uint64_t lo_in, uint64_t hi_in, uint64_t nmask,
|
||||||
|
uint64_t gmask, uint64_t dshare, uint64_t cshare, uint64_t dcoeff,
|
||||||
|
uint64_t ccoeff)
|
||||||
|
: key(std::move(k))
|
||||||
|
, lo(lo_in)
|
||||||
|
, hi(hi_in)
|
||||||
|
, input_mask(nmask)
|
||||||
|
, group_mask(gmask)
|
||||||
|
, delta_share(dshare)
|
||||||
|
, cr_share(cshare)
|
||||||
|
, delta_coeff(dcoeff)
|
||||||
|
, cr_coeff(ccoeff)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t Party, typename Key, typename Input, typename Beta>
|
||||||
|
struct is_ic_key<ic_key<Party, Key, Input, Beta>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr bool is_ic_key_v = is_ic_key<std::decay_t<T>>::value;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace ic_impl
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t input_mask_of() noexcept
|
||||||
|
{
|
||||||
|
constexpr auto n = utils::bitlength_of_v<Input>;
|
||||||
|
if constexpr (n >= 64)
|
||||||
|
return ~uint64_t{0};
|
||||||
|
else
|
||||||
|
return (uint64_t{1} << n) - 1ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t bits_of(Input x) noexcept
|
||||||
|
{
|
||||||
|
constexpr auto to_int = utils::to_integral_type<Input>{};
|
||||||
|
return static_cast<uint64_t>(to_int(x)) & input_mask_of<Input>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr Input input_from_bits(uint64_t u) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<Input>(u & input_mask_of<Input>());
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t embed_small(int s, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
if (s >= 0)
|
||||||
|
return static_cast<uint64_t>(s) & mask;
|
||||||
|
return dcf_impl::neg_m(static_cast<uint64_t>(-s), mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t mul_mask(uint64_t a, uint64_t b, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(static_cast<unsigned __int128>(a) * b) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dealer correction in Fig. 3, as an element of the payload group.
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t correction(uint64_t r, uint64_t p, uint64_t q,
|
||||||
|
uint64_t nmask, uint64_t gmask) noexcept
|
||||||
|
{
|
||||||
|
const uint64_t aq = (q + r) & nmask;
|
||||||
|
const uint64_t ap = (p + r) & nmask;
|
||||||
|
const uint64_t q0 = (q + 1ULL) & nmask;
|
||||||
|
const uint64_t aq0 = (q0 + r) & nmask;
|
||||||
|
const int s = (ap > aq ? 1 : 0) - (ap > p ? 1 : 0)
|
||||||
|
+ (aq0 > q0 ? 1 : 0) + (aq == nmask ? 1 : 0);
|
||||||
|
return embed_small(s, gmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr int public_cx(uint64_t x, uint64_t p, uint64_t q, uint64_t nmask) noexcept
|
||||||
|
{
|
||||||
|
const uint64_t q0 = (q + 1ULL) & nmask;
|
||||||
|
return (x > p ? 1 : 0) - (x > q0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t shift_p(uint64_t x, uint64_t p, uint64_t nmask) noexcept
|
||||||
|
{
|
||||||
|
return (x + (nmask - p)) & nmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr uint64_t shift_q0(uint64_t x, uint64_t q, uint64_t nmask) noexcept
|
||||||
|
{
|
||||||
|
const uint64_t q0 = (q + 1ULL) & nmask;
|
||||||
|
return (x + (nmask - q0)) & nmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
uint64_t opened_u64(const T & v, uint64_t mask) noexcept
|
||||||
|
{
|
||||||
|
if constexpr (is_secret_share_v<std::decay_t<T>>)
|
||||||
|
return dcf_impl::beta_to_u64_simple(v.raw(), mask);
|
||||||
|
else
|
||||||
|
return dcf_impl::beta_to_u64_simple(v, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void split_target(uint64_t target, uint64_t mask,
|
||||||
|
uint64_t & s0, uint64_t & s1)
|
||||||
|
{
|
||||||
|
const uint64_t blind = dcf_impl::sample_addend_blind(mask,
|
||||||
|
[] { return dpf::uniform_sample<simde__m128i>(); });
|
||||||
|
incr::split_cmp_addend(target, mask, blind, s0, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
void check_input()
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned_v<Input> && !std::is_same_v<Input, bool>,
|
||||||
|
"ic: input type must be an unsigned integer of at most 64 bits");
|
||||||
|
static_assert(utils::bitlength_of_v<Input> <= 64,
|
||||||
|
"ic: input type must be an unsigned integer of at most 64 bits");
|
||||||
|
static_assert(utils::bitlength_of_v<Input> > 0,
|
||||||
|
"ic: input type must be an unsigned integer of at most 64 bits");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input, typename Beta>
|
||||||
|
void check_bounds(const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
const uint64_t nmask = input_mask_of<Input>();
|
||||||
|
if (spec.lo > nmask || spec.hi > nmask)
|
||||||
|
throw std::invalid_argument("ic: bound does not fit in the input domain");
|
||||||
|
if (spec.lo > spec.hi)
|
||||||
|
throw std::invalid_argument("ic: require lo <= hi (the interval does not wrap)");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
uint64_t group_mask_of() noexcept
|
||||||
|
{
|
||||||
|
using B = concrete_type_t<Beta>;
|
||||||
|
if constexpr (std::is_same_v<B, dpf::bit>)
|
||||||
|
return 1ULL;
|
||||||
|
else
|
||||||
|
return dcf_impl::default_mask_for_bits(utils::bitlength_of_v<B>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t Party, typename Key, typename Input, typename Beta>
|
||||||
|
ic_key<Party, Key, Input, Beta> make_side(party_key<Party, Key> key,
|
||||||
|
uint64_t lo, uint64_t hi, uint64_t nmask, uint64_t gmask,
|
||||||
|
uint64_t delta_share, uint64_t cr_share,
|
||||||
|
uint64_t delta_coeff, uint64_t cr_coeff)
|
||||||
|
{
|
||||||
|
return ic_key<Party, Key, Input, Beta>(std::move(key), lo, hi, nmask, gmask,
|
||||||
|
delta_share, cr_share, delta_coeff, cr_coeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input, typename Beta, typename Pair>
|
||||||
|
auto finish(uint64_t r_bits, const ic_pack<Beta> & spec, Pair && inner)
|
||||||
|
{
|
||||||
|
using in_type = std::decay_t<Input>;
|
||||||
|
using party0 = std::decay_t<decltype(inner.first)>;
|
||||||
|
using raw_key = typename party0::key_type;
|
||||||
|
using out_beta = concrete_type_t<Beta>;
|
||||||
|
const uint64_t nmask = input_mask_of<in_type>();
|
||||||
|
const uint64_t gmask = group_mask_of<Beta>();
|
||||||
|
constexpr bool wild = is_wildcard_v<Beta>;
|
||||||
|
uint64_t delta = 0;
|
||||||
|
uint64_t fval = 0;
|
||||||
|
if constexpr (!wild)
|
||||||
|
{
|
||||||
|
delta = dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
|
||||||
|
fval = dcf_impl::beta_to_u64_simple(spec.if_false, gmask);
|
||||||
|
}
|
||||||
|
const uint64_t cr = correction(r_bits, spec.lo, spec.hi, nmask, gmask);
|
||||||
|
|
||||||
|
uint64_t d0 = 0, d1 = 0, c0 = 0, c1 = 0;
|
||||||
|
uint64_t dc0 = 0, dc1 = 0, cc0 = 0, cc1 = 0;
|
||||||
|
if constexpr (wild)
|
||||||
|
{
|
||||||
|
split_target(1ULL & gmask, gmask, dc0, dc1);
|
||||||
|
split_target(cr, gmask, cc0, cc1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
split_target(delta, gmask, d0, d1);
|
||||||
|
const uint64_t absorb =
|
||||||
|
(mul_mask(delta, cr, gmask) + fval) & gmask;
|
||||||
|
split_target(absorb, gmask, c0, c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k0 = make_side<0, raw_key, in_type, out_beta>(std::move(inner.first),
|
||||||
|
spec.lo, spec.hi, nmask, gmask, d0, c0, dc0, cc0);
|
||||||
|
auto k1 = make_side<1, raw_key, in_type, out_beta>(std::move(inner.second),
|
||||||
|
spec.lo, spec.hi, nmask, gmask, d1, c1, dc1, cc1);
|
||||||
|
return std::make_pair(std::move(k0), std::move(k1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Beta>
|
||||||
|
auto inner_lt(const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using B = std::decay_t<Beta>;
|
||||||
|
if constexpr (is_wildcard_v<B>)
|
||||||
|
return lt(spec.if_true, spec.if_false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const uint64_t gmask = group_mask_of<B>();
|
||||||
|
const uint64_t delta =
|
||||||
|
dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
|
||||||
|
return lt(dcf_impl::u64_to_beta<B>(delta), dcf_impl::u64_to_beta<B>(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
Input gamma_of(Input r)
|
||||||
|
{
|
||||||
|
const uint64_t nmask = input_mask_of<Input>();
|
||||||
|
const uint64_t ru = bits_of(r);
|
||||||
|
return input_from_bits<Input>((ru - 1ULL) & nmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IcKey, typename Query, typename Memo>
|
||||||
|
auto eval_one(const IcKey & k, Query && x, Memo & memo)
|
||||||
|
{
|
||||||
|
if (!k.assigned)
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"ic eval: wildcard payload not assigned (call assign_cmp)");
|
||||||
|
using in_type = typename IcKey::input_type;
|
||||||
|
const uint64_t xu = bits_of(in_type(std::forward<Query>(x)));
|
||||||
|
const uint64_t xp = shift_p(xu, k.lo, k.input_mask);
|
||||||
|
const uint64_t xq = shift_q0(xu, k.hi, k.input_mask);
|
||||||
|
const uint64_t a = opened_u64(
|
||||||
|
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xp), memo),
|
||||||
|
k.group_mask);
|
||||||
|
const uint64_t b = opened_u64(
|
||||||
|
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xq), memo),
|
||||||
|
k.group_mask);
|
||||||
|
const int cx = public_cx(xu, k.lo, k.hi, k.input_mask);
|
||||||
|
uint64_t scaled = 0;
|
||||||
|
if (cx == 1)
|
||||||
|
scaled = k.delta_share & k.group_mask;
|
||||||
|
else if (cx == -1)
|
||||||
|
scaled = dcf_impl::neg_m(k.delta_share, k.group_mask);
|
||||||
|
const uint64_t y = (dcf_impl::neg_m(a, k.group_mask) + b + k.cr_share
|
||||||
|
+ scaled) & k.group_mask;
|
||||||
|
return make_eval_cmp_result<typename IcKey::key_type>(
|
||||||
|
dcf_impl::u64_to_beta<typename IcKey::beta_type>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ic_impl
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Dealer key for public bounds `spec` and secret mask `r`.
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_dpf(InputT && r, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using input_type = std::decay_t<InputT>;
|
||||||
|
detail::ic_impl::check_input<input_type>();
|
||||||
|
detail::ic_impl::check_bounds<input_type>(spec);
|
||||||
|
const uint64_t r_bits = detail::ic_impl::bits_of(input_type(r));
|
||||||
|
const input_type gamma = detail::ic_impl::gamma_of(input_type(r));
|
||||||
|
auto inner = make_dpf<InteriorPRG, ExteriorPRG>(gamma,
|
||||||
|
detail::ic_impl::inner_lt(spec));
|
||||||
|
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Doerner–Shelat key. `r0 XOR r1` is the secret mask.
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng,
|
||||||
|
typename InputT,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_dpf_doerner_shelat(InputT r0, InputT r1,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using input_type = std::decay_t<InputT>;
|
||||||
|
detail::ic_impl::check_input<input_type>();
|
||||||
|
detail::ic_impl::check_bounds<input_type>(spec);
|
||||||
|
const input_type r = utils::xor_input_shares(r0, r1);
|
||||||
|
const uint64_t r_bits = detail::ic_impl::bits_of(r);
|
||||||
|
const input_type gamma = detail::ic_impl::gamma_of(r);
|
||||||
|
const input_type g0 = r0;
|
||||||
|
const input_type g1 = utils::xor_input_shares(g0, gamma);
|
||||||
|
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(g0, g1,
|
||||||
|
std::move(rng), detail::ic_impl::inner_lt(spec));
|
||||||
|
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Doerner–Shelat IC key. `r0 + r1` is the secret mask; γ = (r0 + r1) − 1.
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename RootSampler,
|
||||||
|
typename PadRng,
|
||||||
|
typename InputT,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using input_type = std::decay_t<InputT>;
|
||||||
|
detail::ic_impl::check_input<input_type>();
|
||||||
|
detail::ic_impl::check_bounds<input_type>(spec);
|
||||||
|
const uint64_t nmask = detail::ic_impl::input_mask_of<input_type>();
|
||||||
|
const uint64_t r_bits =
|
||||||
|
(detail::ic_impl::bits_of(r0) + detail::ic_impl::bits_of(r1)) & nmask;
|
||||||
|
const input_type r = detail::ic_impl::input_from_bits<input_type>(r_bits);
|
||||||
|
// Additive shares of γ = r − 1: (r0 − 1, r1).
|
||||||
|
const input_type g0 = detail::ic_impl::input_from_bits<input_type>(
|
||||||
|
(detail::ic_impl::bits_of(r0) - 1ULL) & nmask);
|
||||||
|
const input_type g1 = r1;
|
||||||
|
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
|
||||||
|
arith_input, g0, g1, std::move(rng), detail::ic_impl::inner_lt(spec));
|
||||||
|
return detail::ic_impl::finish<input_type>(
|
||||||
|
detail::ic_impl::bits_of(r), spec, std::move(inner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Doerner–Shelat key sampled from the library entropy source.
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_dpf_doerner_shelat(InputT r0, InputT r1, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using block = typename InteriorPRG::block_type;
|
||||||
|
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
|
||||||
|
dpf::uniform_sample<block>, {}};
|
||||||
|
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
|
||||||
|
std::move(r0), std::move(r1), rng, spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InteriorPRG = dpf::prg::aes128,
|
||||||
|
typename ExteriorPRG = InteriorPRG,
|
||||||
|
typename InputT,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
|
||||||
|
const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
using block = typename InteriorPRG::block_type;
|
||||||
|
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
|
||||||
|
dpf::uniform_sample<block>, {}};
|
||||||
|
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
|
||||||
|
arith_input, std::move(r0), std::move(r1), rng, spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open a wildcard interval payload onto an existing key pair.
|
||||||
|
template <typename Key, typename Input, typename Beta, typename Payload>
|
||||||
|
void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
|
||||||
|
ic_key<1, Key, Input, Beta> & k1, const Payload & if_true,
|
||||||
|
const Payload & if_false = Payload{})
|
||||||
|
{
|
||||||
|
static_assert(Key::cmp_is_wildcard,
|
||||||
|
"assign_cmp: interval payload is not a wildcard");
|
||||||
|
const uint64_t mask = k0.group_mask;
|
||||||
|
const uint64_t delta =
|
||||||
|
detail::dcf_impl::beta_delta_u64(if_true, if_false, mask);
|
||||||
|
const uint64_t fval =
|
||||||
|
detail::dcf_impl::beta_to_u64_simple(if_false, mask);
|
||||||
|
assign_cmp(k0.key, k1.key,
|
||||||
|
detail::dcf_impl::u64_to_beta<Beta>(delta),
|
||||||
|
detail::dcf_impl::u64_to_beta<Beta>(0));
|
||||||
|
k0.delta_share = detail::ic_impl::mul_mask(k0.delta_coeff, delta, mask);
|
||||||
|
k1.delta_share = detail::ic_impl::mul_mask(k1.delta_coeff, delta, mask);
|
||||||
|
k0.cr_share = detail::ic_impl::mul_mask(k0.cr_coeff, delta, mask);
|
||||||
|
k1.cr_share = detail::ic_impl::mul_mask(k1.cr_coeff, delta, mask);
|
||||||
|
uint64_t f0 = 0, f1 = 0;
|
||||||
|
detail::ic_impl::split_target(fval, mask, f0, f1);
|
||||||
|
k0.cr_share = (k0.cr_share + f0) & mask;
|
||||||
|
k1.cr_share = (k1.cr_share + f1) & mask;
|
||||||
|
k0.assigned = true;
|
||||||
|
k1.assigned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Point evaluation. `memo` is a path memoizer for the inner comparison key.
|
||||||
|
template <typename IcKey, typename Query,
|
||||||
|
typename Memo = basic_path_memoizer<typename IcKey::key_type>,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto eval_point(ic_fn, const IcKey & key, Query && x, Memo && memo = Memo{})
|
||||||
|
{
|
||||||
|
return detail::ic_impl::eval_one(key, std::forward<Query>(x), memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inclusive interval `[from, to]` on the input domain.
|
||||||
|
template <typename IcKey, typename Lane, typename Buffer, typename Memo,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to,
|
||||||
|
Buffer && buf, Memo && memo)
|
||||||
|
{
|
||||||
|
using in_type = typename IcKey::input_type;
|
||||||
|
const uint64_t nmask = key.input_mask;
|
||||||
|
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
|
||||||
|
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
|
||||||
|
if (a > b)
|
||||||
|
throw std::invalid_argument("ic interval: to < from");
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (uint64_t x = a;; ++x)
|
||||||
|
{
|
||||||
|
buf[i++] = detail::ic_impl::eval_one(key,
|
||||||
|
detail::ic_impl::input_from_bits<in_type>(x), memo);
|
||||||
|
if (x == b)
|
||||||
|
break;
|
||||||
|
if (x == nmask)
|
||||||
|
throw std::invalid_argument("ic interval: to < from");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IcKey, typename Lane, typename Buffer,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to, Buffer && buf)
|
||||||
|
{
|
||||||
|
basic_path_memoizer<typename IcKey::key_type> memo;
|
||||||
|
eval_interval(ic, key, from, to, std::forward<Buffer>(buf), memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate the points in `[begin, end)`.
|
||||||
|
template <typename IcKey, typename Iter, typename Buffer, typename Memo,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end,
|
||||||
|
Buffer && buf, Memo && memo)
|
||||||
|
{
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto it = begin; it != end; ++it, ++i)
|
||||||
|
buf[i] = detail::ic_impl::eval_one(key, *it, memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IcKey, typename Iter, typename Buffer,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end, Buffer && buf)
|
||||||
|
{
|
||||||
|
basic_path_memoizer<typename IcKey::key_type> memo;
|
||||||
|
eval_sequence(ic, key, begin, end, std::forward<Buffer>(buf), memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Buffer of `n` interval shares.
|
||||||
|
template <typename IcKey, typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_output_buffer(ic_fn, const IcKey &, std::size_t n)
|
||||||
|
{
|
||||||
|
using beta = typename IcKey::beta_type;
|
||||||
|
using elem = cmp_buffer_elem_t<typename IcKey::key_type, beta>;
|
||||||
|
return output_buffer<elem>(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Buffer large enough for the inclusive interval `[from, to]`.
|
||||||
|
template <typename IcKey, typename Lane,
|
||||||
|
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
auto make_output_buffer(ic_fn, const IcKey & key, Lane from, Lane to)
|
||||||
|
{
|
||||||
|
using in_type = typename IcKey::input_type;
|
||||||
|
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
|
||||||
|
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
|
||||||
|
if (a > b)
|
||||||
|
throw std::invalid_argument("ic interval: to < from");
|
||||||
|
const uint64_t n = b - a + 1ULL;
|
||||||
|
return make_output_buffer(ic, key, static_cast<std::size_t>(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Doerner–Shelat geneval. `r0 XOR r1` is the secret mask. Each query is
|
||||||
|
/// returned already combined into the interval share.
|
||||||
|
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
geneval_cmp_result geneval_ic(InputT r0, InputT r1, Iter begin, Iter end,
|
||||||
|
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
static_assert(!is_wildcard_v<Beta>,
|
||||||
|
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
|
||||||
|
geneval_cmp_result out;
|
||||||
|
if (begin == end)
|
||||||
|
return out;
|
||||||
|
|
||||||
|
auto keys = make_dpf_doerner_shelat(std::move(r0), std::move(r1),
|
||||||
|
std::move(rng), spec);
|
||||||
|
const auto & k0 = keys.first;
|
||||||
|
const auto & k1 = keys.second;
|
||||||
|
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
|
||||||
|
constexpr std::size_t depth = key_type::depth;
|
||||||
|
out.live_levels = depth;
|
||||||
|
out.mask = k0.key.cmp().mask;
|
||||||
|
out.cw_last = k0.key.cw_last();
|
||||||
|
out.addend0 = k0.key.cmp_addend().raw();
|
||||||
|
out.addend1 = k1.key.cmp_addend().raw();
|
||||||
|
out.correction_words.resize(depth);
|
||||||
|
out.correction_advice.resize(depth);
|
||||||
|
out.value_cw.resize(depth);
|
||||||
|
for (std::size_t level = 0; level < depth; ++level)
|
||||||
|
{
|
||||||
|
out.correction_words[level] = k0.key.correction_word(level);
|
||||||
|
out.correction_advice[level] =
|
||||||
|
static_cast<uint8_t>(k0.key.correction_advice(level));
|
||||||
|
out.value_cw[level] = k0.key.value_cw(level);
|
||||||
|
}
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
out.party0.push_back(detail::ic_impl::opened_u64(
|
||||||
|
eval_point(ic, k0, *it), out.mask));
|
||||||
|
out.party1.push_back(detail::ic_impl::opened_u64(
|
||||||
|
eval_point(ic, k1, *it), out.mask));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additive-share geneval_ic. `r0 + r1` is the secret mask.
|
||||||
|
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
|
||||||
|
typename Beta>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
geneval_cmp_result geneval_ic(arith_input_t, InputT r0, InputT r1, Iter begin,
|
||||||
|
Iter end, ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
|
||||||
|
{
|
||||||
|
static_assert(!is_wildcard_v<Beta>,
|
||||||
|
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
|
||||||
|
geneval_cmp_result out;
|
||||||
|
if (begin == end)
|
||||||
|
return out;
|
||||||
|
|
||||||
|
auto keys = make_dpf_doerner_shelat(arith_input, std::move(r0), std::move(r1),
|
||||||
|
std::move(rng), spec);
|
||||||
|
const auto & k0 = keys.first;
|
||||||
|
const auto & k1 = keys.second;
|
||||||
|
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
|
||||||
|
constexpr std::size_t depth = key_type::depth;
|
||||||
|
out.live_levels = depth;
|
||||||
|
out.mask = k0.key.cmp().mask;
|
||||||
|
out.cw_last = k0.key.cw_last();
|
||||||
|
out.addend0 = k0.key.cmp_addend().raw();
|
||||||
|
out.addend1 = k1.key.cmp_addend().raw();
|
||||||
|
out.correction_words.resize(depth);
|
||||||
|
out.correction_advice.resize(depth);
|
||||||
|
out.value_cw.resize(depth);
|
||||||
|
for (std::size_t level = 0; level < depth; ++level)
|
||||||
|
{
|
||||||
|
out.correction_words[level] = k0.key.correction_word(level);
|
||||||
|
out.correction_advice[level] =
|
||||||
|
static_cast<uint8_t>(k0.key.correction_advice(level));
|
||||||
|
out.value_cw[level] = k0.key.value_cw(level);
|
||||||
|
}
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
out.party0.push_back(detail::ic_impl::opened_u64(
|
||||||
|
eval_point(ic, k0, *it), out.mask));
|
||||||
|
out.party1.push_back(detail::ic_impl::opened_u64(
|
||||||
|
eval_point(ic, k1, *it), out.mask));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dpf
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
/// @file dpf/interval_memoizer.hpp
|
/// @file dpf/interval_memoizer.hpp
|
||||||
/// @brief
|
/// @brief Workspaces for an inclusive interval of DPF leaves.
|
||||||
/// @details
|
/// @details `basic_interval_memoizer` keeps two levels of the interval.
|
||||||
|
/// `full_tree_interval_memoizer` keeps every level. Size either one
|
||||||
|
/// for the widest interval you will evaluate; a wider interval
|
||||||
|
/// throws `std::length_error`. The same key and the same endpoints
|
||||||
|
/// leave the final interior level in place.
|
||||||
|
///
|
||||||
|
/// Factories unwrap `party_key`. Pass the memoizer as a mutable
|
||||||
|
/// lvalue to `eval_interval` or `eval_full`.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -55,10 +62,13 @@ struct interval_memoizer_base
|
||||||
|
|
||||||
// level 0 should access the root
|
// level 0 should access the root
|
||||||
// level goes up to (and including) depth
|
// level goes up to (and including) depth
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type operator[](std::size_t) const noexcept = 0;
|
virtual return_type operator[](std::size_t) const noexcept = 0;
|
||||||
|
|
||||||
// iterators should access most recently completed level
|
// iterators should access most recently completed level
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type begin() const noexcept = 0;
|
virtual return_type begin() const noexcept = 0;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type end() const noexcept = 0;
|
virtual return_type end() const noexcept = 0;
|
||||||
|
|
||||||
virtual std::size_t assign_interval(const dpf_type & dpf, integral_type new_from, integral_type new_to)
|
virtual std::size_t assign_interval(const dpf_type & dpf, integral_type new_from, integral_type new_to)
|
||||||
|
|
@ -151,6 +161,8 @@ struct interval_memoizer_base
|
||||||
std::optional<integral_type> to_;
|
std::optional<integral_type> to_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Two-level workspace for one interval. This is what
|
||||||
|
/// `eval_interval(key, from, to)` allocates when you omit the memoizer.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename Allocator = aligned_allocator<
|
typename Allocator = aligned_allocator<
|
||||||
typename interval_memoizer_key_t<DpfKey>::interior_node>>
|
typename interval_memoizer_key_t<DpfKey>::interior_node>>
|
||||||
|
|
@ -229,6 +241,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
unique_ptr buf;
|
unique_ptr buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Every level of the interval. `retains_all_levels` is true.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename Allocator = aligned_allocator<
|
typename Allocator = aligned_allocator<
|
||||||
typename interval_memoizer_key_t<DpfKey>::interior_node>>
|
typename interval_memoizer_key_t<DpfKey>::interior_node>>
|
||||||
|
|
@ -392,6 +405,7 @@ struct basic_interval_memoizer_at
|
||||||
- utils::shift_right(from_node, offset) + 1;
|
- utils::shift_right(from_node, offset) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
return_type operator[](std::size_t level) const noexcept
|
return_type operator[](std::size_t level) const noexcept
|
||||||
{
|
{
|
||||||
bool b = (depth ^ level) & 1;
|
bool b = (depth ^ level) & 1;
|
||||||
|
|
@ -420,9 +434,6 @@ auto make_interval_memoizer(InputT from, InputT to)
|
||||||
{
|
{
|
||||||
using dpf_type = DpfKey;
|
using dpf_type = DpfKey;
|
||||||
|
|
||||||
utils::flip_msb_if_signed_integral(from);
|
|
||||||
utils::flip_msb_if_signed_integral(to);
|
|
||||||
|
|
||||||
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
|
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
|
||||||
|
|
||||||
return MemoizerT(nodes_in_interval);
|
return MemoizerT(nodes_in_interval);
|
||||||
|
|
@ -430,6 +441,10 @@ auto make_interval_memoizer(InputT from, InputT to)
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Two-level workspace sized for the closed interval `[from, to]`.
|
||||||
|
/// @param from Inclusive start, in the key's input domain.
|
||||||
|
/// @param to Inclusive end. `to` is at least `from` in that domain.
|
||||||
|
/// @snippet evaluation/memoizers.cpp interval-memoizer
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename InputT>
|
typename InputT>
|
||||||
inline auto make_basic_interval_memoizer(InputT from, InputT to)
|
inline auto make_basic_interval_memoizer(InputT from, InputT to)
|
||||||
|
|
@ -448,6 +463,7 @@ inline auto make_basic_interval_memoizer(const DpfKey &, InputT from, InputT to)
|
||||||
return make_basic_interval_memoizer<DpfKey>(from, to);
|
return make_basic_interval_memoizer<DpfKey>(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `make_basic_interval_memoizer` sized for the whole input domain.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
inline auto make_basic_full_memoizer()
|
inline auto make_basic_full_memoizer()
|
||||||
{
|
{
|
||||||
|
|
@ -464,6 +480,7 @@ inline auto make_basic_full_memoizer(const DpfKey &)
|
||||||
return make_basic_full_memoizer<DpfKey>();
|
return make_basic_full_memoizer<DpfKey>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Full-tree workspace sized for the closed interval `[from, to]`.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename InputT>
|
typename InputT>
|
||||||
inline auto make_full_tree_interval_memoizer(InputT from, InputT to)
|
inline auto make_full_tree_interval_memoizer(InputT from, InputT to)
|
||||||
|
|
@ -482,6 +499,7 @@ inline auto make_full_tree_interval_memoizer(const DpfKey &, InputT from, InputT
|
||||||
return make_full_tree_interval_memoizer<DpfKey>(from, to);
|
return make_full_tree_interval_memoizer<DpfKey>(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `make_full_tree_interval_memoizer` sized for the whole input domain.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
inline auto make_full_tree_full_memoizer()
|
inline auto make_full_tree_full_memoizer()
|
||||||
{
|
{
|
||||||
|
|
@ -521,11 +539,13 @@ inline auto make_basic_interval_memoizer(InputT from, InputT to)
|
||||||
utils::flip_msb_if_signed_integral(from);
|
utils::flip_msb_if_signed_integral(from);
|
||||||
utils::flip_msb_if_signed_integral(to);
|
utils::flip_msb_if_signed_integral(to);
|
||||||
|
|
||||||
const integral_type from_node = utils::leaf_node_floor(
|
const auto from_i = static_cast<integral_type>(to_int(from));
|
||||||
static_cast<integral_type>(to_int(from)), lg);
|
const auto to_i = static_cast<integral_type>(to_int(to));
|
||||||
const integral_type to_node = utils::leaf_node_ceil_exclusive(
|
const integral_type from_node = utils::leaf_node_floor(from_i, lg);
|
||||||
static_cast<integral_type>(to_int(to)), lg);
|
const integral_type to_node = utils::leaf_node_ceil_exclusive(to_i, lg);
|
||||||
const auto segs = utils::split_leaf_nodes(from_node, to_node, stop);
|
const bool wraps = utils::interval_wraps(from_i, to_i,
|
||||||
|
utils::bitlength_of_v<InputT>);
|
||||||
|
const auto segs = utils::split_leaf_nodes(from_node, to_node, stop, wraps);
|
||||||
return basic_interval_memoizer_at<DpfKey, stop>(segs.total);
|
return basic_interval_memoizer_at<DpfKey, stop>(segs.total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/// @file dpf/json.hpp
|
/// @file dpf/json.hpp
|
||||||
/// @brief
|
/// @brief nlohmann::json serializers for DPF keys and beaver triples.
|
||||||
/// @details
|
/// @details ADL `adl_serializer` specializations so `nlohmann::json` can
|
||||||
|
/// convert the library's key and triple types.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
|
|
@ -92,6 +93,9 @@ struct adl_serializer<dpf::detail::cmp_meta>
|
||||||
j.at("eval_as_ge").get_to(c.eval_as_ge);
|
j.at("eval_as_ge").get_to(c.eval_as_ge);
|
||||||
j.at("include_eq").get_to(c.include_eq);
|
j.at("include_eq").get_to(c.include_eq);
|
||||||
j.at("active").get_to(c.active);
|
j.at("active").get_to(c.active);
|
||||||
|
c.incremental = j.value("incremental", false);
|
||||||
|
c.block_width = j.value("block_width", 0);
|
||||||
|
c.tail_bits = j.value("tail_bits", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void to_json(nlohmann::json & j, const dpf::detail::cmp_meta & c) // NOLINT(runtime/references)
|
static void to_json(nlohmann::json & j, const dpf::detail::cmp_meta & c) // NOLINT(runtime/references)
|
||||||
|
|
@ -105,6 +109,13 @@ struct adl_serializer<dpf::detail::cmp_meta>
|
||||||
{"include_eq", c.include_eq},
|
{"include_eq", c.include_eq},
|
||||||
{"active", c.active}
|
{"active", c.active}
|
||||||
};
|
};
|
||||||
|
if (c.incremental)
|
||||||
|
j["incremental"] = true;
|
||||||
|
if (c.block_width != 0)
|
||||||
|
{
|
||||||
|
j["block_width"] = c.block_width;
|
||||||
|
j["tail_bits"] = c.tail_bits;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -197,6 +208,12 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
|
||||||
j.at("value_cw").get_to(value_cws);
|
j.at("value_cw").get_to(value_cws);
|
||||||
uint64_t cw_last = j.at("cw_last").template get<uint64_t>();
|
uint64_t cw_last = j.at("cw_last").template get<uint64_t>();
|
||||||
uint64_t cmp_addend = j.at("cmp_addend").template get<uint64_t>();
|
uint64_t cmp_addend = j.at("cmp_addend").template get<uint64_t>();
|
||||||
|
typename dpf_type::tail_array tail{};
|
||||||
|
if constexpr (dpf_type::cmp_block > 0)
|
||||||
|
j.at("tail_cw").get_to(tail);
|
||||||
|
typename dpf_type::prefix_cw_array prefix{};
|
||||||
|
if constexpr (dpf_type::cmp_idcf)
|
||||||
|
j.at("prefix_cw").get_to(prefix);
|
||||||
|
|
||||||
typename dpf_type::leaf_wrapper_tuple leaves{};
|
typename dpf_type::leaf_wrapper_tuple leaves{};
|
||||||
input_type offset_share{};
|
input_type offset_share{};
|
||||||
|
|
@ -204,7 +221,7 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
|
||||||
|
|
||||||
return dpf_type{root, correction_words, correction_advice,
|
return dpf_type{root, correction_words, correction_advice,
|
||||||
std::move(leaves), offset_share, cmp, value_cws, cw_last,
|
std::move(leaves), offset_share, cmp, value_cws, cw_last,
|
||||||
cmp_addend, addends};
|
cmp_addend, addends, {}, 0, tail, {}, prefix};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void to_json(nlohmann::json & j, const dpf_type & dpf) // NOLINT(runtime/references)
|
static void to_json(nlohmann::json & j, const dpf_type & dpf) // NOLINT(runtime/references)
|
||||||
|
|
@ -221,6 +238,10 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
|
||||||
{"cw_last", static_cast<uint64_t>(dpf.cw_last())},
|
{"cw_last", static_cast<uint64_t>(dpf.cw_last())},
|
||||||
{"cmp_addend", static_cast<uint64_t>(dpf.cmp_addend())}
|
{"cmp_addend", static_cast<uint64_t>(dpf.cmp_addend())}
|
||||||
};
|
};
|
||||||
|
if constexpr (dpf_type::cmp_block > 0)
|
||||||
|
j["tail_cw"] = dpf.tail_cw();
|
||||||
|
if constexpr (dpf_type::cmp_idcf)
|
||||||
|
j["prefix_cw"] = dpf.prefix_cws();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,11 +174,13 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
|
||||||
/// @brief default constructor
|
/// @brief default constructor
|
||||||
/// @details Constructs the `basic_fixed_length_string` with a value
|
/// @details Constructs the `basic_fixed_length_string` with a value
|
||||||
/// corresponding to the empty string.
|
/// corresponding to the empty string.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr basic_fixed_length_string() noexcept = default;
|
constexpr basic_fixed_length_string() noexcept = default;
|
||||||
|
|
||||||
/// @brief copy constructor
|
/// @brief copy constructor
|
||||||
/// @details Constructs the `basic_fixed_length_string` with a value
|
/// @details Constructs the `basic_fixed_length_string` with a value
|
||||||
/// copied from another `basic_fixed_length_string`.
|
/// copied from another `basic_fixed_length_string`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr
|
constexpr
|
||||||
basic_fixed_length_string(const basic_fixed_length_string &)
|
basic_fixed_length_string(const basic_fixed_length_string &)
|
||||||
noexcept = default;
|
noexcept = default;
|
||||||
|
|
@ -186,6 +188,7 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
|
||||||
/// @brief move constructor
|
/// @brief move constructor
|
||||||
/// @details Constructs the `basic_fixed_length_string` from another
|
/// @details Constructs the `basic_fixed_length_string` from another
|
||||||
/// `basic_fixed_length_string` using move semantics.
|
/// `basic_fixed_length_string` using move semantics.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr
|
constexpr
|
||||||
basic_fixed_length_string(basic_fixed_length_string &&)
|
basic_fixed_length_string(basic_fixed_length_string &&)
|
||||||
noexcept = default;
|
noexcept = default;
|
||||||
|
|
@ -232,6 +235,7 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
|
||||||
/// @brief move assignment
|
/// @brief move assignment
|
||||||
/// @details Assigns the `basic_fixed_length_string` from another
|
/// @details Assigns the `basic_fixed_length_string` from another
|
||||||
/// `basic_fixed_length_string` using move semantics.
|
/// `basic_fixed_length_string` using move semantics.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr basic_fixed_length_string &
|
constexpr basic_fixed_length_string &
|
||||||
operator=(basic_fixed_length_string &&) noexcept = default;
|
operator=(basic_fixed_length_string &&) noexcept = default;
|
||||||
|
|
||||||
|
|
@ -268,10 +272,12 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
|
||||||
private:
|
private:
|
||||||
constexpr
|
constexpr
|
||||||
// cppcheck-suppress noExplicitConstructor
|
// cppcheck-suppress noExplicitConstructor
|
||||||
|
HEDLEY_NO_THROW
|
||||||
basic_fixed_length_string(integral_type val) // NOLINT(runtime/explicit)
|
basic_fixed_length_string(integral_type val) // NOLINT(runtime/explicit)
|
||||||
noexcept
|
noexcept
|
||||||
: parent::modint(val) { }
|
: parent::modint(val) { }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr basic_fixed_length_string(parent val) noexcept
|
constexpr basic_fixed_length_string(parent val) noexcept
|
||||||
: parent::modint(val) { }
|
: parent::modint(val) { }
|
||||||
|
|
||||||
|
|
@ -448,6 +454,7 @@ struct make_from_integral_value<dpf::basic_fixed_length_string<MaxLen, CharT, Al
|
||||||
{
|
{
|
||||||
using T = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
|
using T = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
|
||||||
using integral_type = integral_type_from_bitlength_t<bitlength_of_v<T>>;
|
using integral_type = integral_type_from_bitlength_t<bitlength_of_v<T>>;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T operator()(integral_type val) const noexcept
|
constexpr T operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
return T{val};
|
return T{val};
|
||||||
|
|
@ -499,14 +506,23 @@ class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits
|
||||||
= std::numeric_limits<typename keyword_type::integral_type>::traps;
|
= std::numeric_limits<typename keyword_type::integral_type>::traps;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type min() noexcept { return keyword_type{""}; }
|
static constexpr keyword_type min() noexcept { return keyword_type{""}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type lowest() noexcept { return keyword_type{""}; }
|
static constexpr keyword_type lowest() noexcept { return keyword_type{""}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type max() noexcept { return ~keyword_type{""}; }
|
static constexpr keyword_type max() noexcept { return ~keyword_type{""}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type epsilon() noexcept { return 0; }
|
static constexpr keyword_type epsilon() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type round_error() noexcept { return 0; }
|
static constexpr keyword_type round_error() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type infinity() noexcept { return 0; }
|
static constexpr keyword_type infinity() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type quiet_NaN() noexcept { return 0; }
|
static constexpr keyword_type quiet_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type signaling_NaN() noexcept { return 0; }
|
static constexpr keyword_type signaling_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr keyword_type denorm_min() noexcept { return 0; }
|
static constexpr keyword_type denorm_min() noexcept { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include "dpf/modint.hpp"
|
#include "dpf/modint.hpp"
|
||||||
#include "dpf/utils.hpp"
|
#include "dpf/utils.hpp"
|
||||||
|
|
||||||
|
|
@ -1492,6 +1494,7 @@ constexpr hit match_full(const program & p, const char * s, std::size_t n)
|
||||||
return run_id(c, root, 0, u256{}, nullptr, 0, 0);
|
return run_id(c, root, 0, u256{}, nullptr, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NON_NULL(4)
|
||||||
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n);
|
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n);
|
||||||
|
|
||||||
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n)
|
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n)
|
||||||
|
|
@ -1590,12 +1593,14 @@ constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char *
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NON_NULL(3)
|
||||||
constexpr int unrank_root(const program & p, u256 rank, char * out)
|
constexpr int unrank_root(const program & p, u256 rank, char * out)
|
||||||
{
|
{
|
||||||
if (!p.lang.all && cmp_card_u(rank, p.lang) >= 0) return -1;
|
if (!p.lang.all && cmp_card_u(rank, p.lang) >= 0) return -1;
|
||||||
return unrank_node(p, p.tree.root, rank, out, 0);
|
return unrank_node(p, p.tree.root, rank, out, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NON_NULL(1)
|
||||||
constexpr program compile_pattern(const char * pattern)
|
constexpr program compile_pattern(const char * pattern)
|
||||||
{
|
{
|
||||||
program p{};
|
program p{};
|
||||||
|
|
@ -1719,6 +1724,7 @@ class keyword2
|
||||||
constexpr keyword2(std::string_view str)
|
constexpr keyword2(std::string_view str)
|
||||||
: parent(encode_(str)) { }
|
: parent(encode_(str)) { }
|
||||||
|
|
||||||
|
HEDLEY_NON_NULL(1)
|
||||||
constexpr keyword2(const char * str)
|
constexpr keyword2(const char * str)
|
||||||
: keyword2(std::string_view(str)) { }
|
: keyword2(std::string_view(str)) { }
|
||||||
|
|
||||||
|
|
@ -1731,7 +1737,7 @@ class keyword2
|
||||||
constexpr keyword2 & operator=(const keyword2 &) noexcept = default;
|
constexpr keyword2 & operator=(const keyword2 &) noexcept = default;
|
||||||
constexpr keyword2 & operator=(keyword2 &&) noexcept = default;
|
constexpr keyword2 & operator=(keyword2 &&) noexcept = default;
|
||||||
|
|
||||||
~keyword2() = default;
|
~keyword2() noexcept = default;
|
||||||
|
|
||||||
/// @brief Rank, including values outside the language that fill the bit width.
|
/// @brief Rank, including values outside the language that fill the bit width.
|
||||||
static constexpr keyword2 from_rank(integral_type rank) noexcept
|
static constexpr keyword2 from_rank(integral_type rank) noexcept
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ static constexpr std::size_t block_length_of_leaf_v
|
||||||
template <typename OutputT,
|
template <typename OutputT,
|
||||||
typename NodeT,
|
typename NodeT,
|
||||||
typename InputT>
|
typename InputT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr std::size_t offset_within_block(InputT x) noexcept
|
constexpr std::size_t offset_within_block(InputT x) noexcept
|
||||||
{
|
{
|
||||||
constexpr auto mod = utils::mod_pow_2<InputT>{};
|
constexpr auto mod = utils::mod_pow_2<InputT>{};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/// @file dpf/literals.hpp
|
||||||
|
/// @brief Re-exports the user-defined literal namespaces.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_LITERALS_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_LITERALS_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_LITERALS_HPP__
|
#define LIBDPF_INCLUDE_DPF_LITERALS_HPP__
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,18 +57,21 @@ class modint
|
||||||
|
|
||||||
/// @brief default constructor
|
/// @brief default constructor
|
||||||
/// @details Constructs a `modint` whose value is initialized to `0`.
|
/// @details Constructs a `modint` whose value is initialized to `0`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint() noexcept = default;
|
constexpr modint() noexcept = default;
|
||||||
|
|
||||||
/// @brief copy constructor
|
/// @brief copy constructor
|
||||||
/// @details Constructs the `modint` with a value copied from another
|
/// @details Constructs the `modint` with a value copied from another
|
||||||
/// `modint`.
|
/// `modint`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint(const modint &) noexcept = default;
|
constexpr modint(const modint &) noexcept = default;
|
||||||
|
|
||||||
/// @brief move constructor
|
/// @brief move constructor
|
||||||
/// @details Constructs the `modint` from another `modint` using move
|
/// @details Constructs the `modint` from another `modint` using move
|
||||||
/// semantics.
|
/// semantics.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint(modint &&) noexcept = default;
|
constexpr modint(modint &&) noexcept = default;
|
||||||
|
|
||||||
|
|
@ -91,12 +94,14 @@ class modint
|
||||||
/// @brief copy assignment
|
/// @brief copy assignment
|
||||||
/// @details Assigns the `modint` with a value copied from another
|
/// @details Assigns the `modint` with a value copied from another
|
||||||
/// `modint`.
|
/// `modint`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint & operator=(const modint &) noexcept = default;
|
constexpr modint & operator=(const modint &) noexcept = default;
|
||||||
|
|
||||||
/// @brief move assignment
|
/// @brief move assignment
|
||||||
/// @details Assigns the `modint` from another `modint` using move
|
/// @details Assigns the `modint` from another `modint` using move
|
||||||
/// semantics.
|
/// semantics.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint & operator=(modint &&) noexcept = default;
|
constexpr modint & operator=(modint &&) noexcept = default;
|
||||||
|
|
||||||
|
|
@ -278,7 +283,11 @@ class modint
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint operator<<(std::size_t shift_amount) const noexcept
|
constexpr modint operator<<(std::size_t shift_amount) const noexcept
|
||||||
{
|
{
|
||||||
return modint{static_cast<integral_type>(this->val << shift_amount)};
|
if (shift_amount >= Nbits)
|
||||||
|
return modint{integral_type{0}};
|
||||||
|
// Reduce first: an unreduced limb shifted by less than Nbits can
|
||||||
|
// still leave the modulus, and a shift of the limb width is UB.
|
||||||
|
return modint{static_cast<integral_type>(this->reduced_value() << shift_amount)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief bitwise-left-shift-assignment operator
|
/// @brief bitwise-left-shift-assignment operator
|
||||||
|
|
@ -290,7 +299,10 @@ class modint
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint & operator<<=(std::size_t shift_amount) noexcept
|
constexpr modint & operator<<=(std::size_t shift_amount) noexcept
|
||||||
{
|
{
|
||||||
this->val <<= shift_amount;
|
if (shift_amount >= Nbits)
|
||||||
|
this->val = integral_type{0};
|
||||||
|
else
|
||||||
|
this->val = static_cast<integral_type>(this->reduced_value() << shift_amount);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,6 +316,8 @@ class modint
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint operator>>(std::size_t shift_amount) const noexcept
|
constexpr modint operator>>(std::size_t shift_amount) const noexcept
|
||||||
{
|
{
|
||||||
|
if (shift_amount >= Nbits)
|
||||||
|
return modint{integral_type{0}};
|
||||||
return modint{static_cast<integral_type>(this->reduced_value() >> shift_amount)};
|
return modint{static_cast<integral_type>(this->reduced_value() >> shift_amount)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,6 +330,9 @@ class modint
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr modint & operator>>=(std::size_t shift_amount) noexcept
|
constexpr modint & operator>>=(std::size_t shift_amount) noexcept
|
||||||
{
|
{
|
||||||
|
if (shift_amount >= Nbits)
|
||||||
|
this->val = integral_type{0};
|
||||||
|
else
|
||||||
this->val = this->reduced_value() >> shift_amount;
|
this->val = this->reduced_value() >> shift_amount;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -603,6 +620,7 @@ class modint
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr modint<Nbits> operator*(typename modint<Nbits>::integral_type lhs,
|
constexpr modint<Nbits> operator*(typename modint<Nbits>::integral_type lhs,
|
||||||
modint<Nbits> rhs) noexcept
|
modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -616,6 +634,7 @@ constexpr modint<Nbits> operator*(typename modint<Nbits>::integral_type lhs,
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator<(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator<(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -626,6 +645,7 @@ constexpr bool operator<(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator<=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator<=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -636,6 +656,7 @@ constexpr bool operator<=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator>(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator>(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -646,6 +667,7 @@ constexpr bool operator>(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator>=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator>=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -656,6 +678,7 @@ constexpr bool operator>=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator==(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator==(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -666,6 +689,7 @@ constexpr bool operator==(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
template <std::size_t Nbits>
|
template <std::size_t Nbits>
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator!=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
constexpr bool operator!=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
return static_cast<typename modint<Nbits>::integral_type>(lhs)
|
||||||
|
|
@ -695,6 +719,7 @@ template <std::size_t Nbits>
|
||||||
struct countl_zero_symmetric_difference<dpf::modint<Nbits>>
|
struct countl_zero_symmetric_difference<dpf::modint<Nbits>>
|
||||||
{
|
{
|
||||||
using T = dpf::modint<Nbits>;
|
using T = dpf::modint<Nbits>;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
|
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
|
||||||
|
|
@ -731,6 +756,7 @@ struct mod_pow_2<dpf::modint<Nbits>>
|
||||||
{
|
{
|
||||||
using T = dpf::modint<Nbits>;
|
using T = dpf::modint<Nbits>;
|
||||||
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
|
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(T val, std::size_t n) const noexcept
|
std::size_t operator()(T val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod(val.val, n);
|
return mod(val.val, n);
|
||||||
|
|
@ -1106,218 +1132,218 @@ constexpr static auto operator "" _u61(unsigned long long int x) { return dpf::m
|
||||||
constexpr static auto operator "" _u62(unsigned long long int x) { return dpf::modints::modint62_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _u62(unsigned long long int x) { return dpf::modints::modint62_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
constexpr static auto operator "" _u63(unsigned long long int x) { return dpf::modints::modint63_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _u63(unsigned long long int x) { return dpf::modints::modint63_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
constexpr static auto operator "" _u64(unsigned long long int x) { return dpf::modints::modint64_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _u64(unsigned long long int x) { return dpf::modints::modint64_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint65_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint65_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint66_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint66_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint67_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint67_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint68_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint68_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint69_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint69_t{x}; }
|
||||||
// 70--79
|
// 70--79
|
||||||
template <char ...digits> constexpr static auto operator "" _u70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint70_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint70_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint71_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint71_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint72_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint72_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint73_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint73_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint74_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint74_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint75_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint75_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint76_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint76_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint77_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint77_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint78_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint78_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint79_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint79_t{x}; }
|
||||||
// 80--89
|
// 80--89
|
||||||
template <char ...digits> constexpr static auto operator "" _u80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint80_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint80_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint81_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint81_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint82_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint82_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint83_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint83_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint84_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint84_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint85_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint85_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint86_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint86_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint87_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint87_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint88_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint88_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint89_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint89_t{x}; }
|
||||||
// 90--99
|
// 90--99
|
||||||
template <char ...digits> constexpr static auto operator "" _u90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint90_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint90_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint91_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint91_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint92_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint92_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint93_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint93_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint94_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint94_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint95_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint95_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint96_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint96_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint97_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint97_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint98_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint98_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint99_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint99_t{x}; }
|
||||||
// 100--109
|
// 100--109
|
||||||
template <char ...digits> constexpr static auto operator "" _u100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint100_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint100_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint101_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint101_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint102_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint102_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint103_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint103_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint104_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint104_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint105_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint105_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint106_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint106_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint107_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint107_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint108_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint108_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint109_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint109_t{x}; }
|
||||||
// 110--119
|
// 110--119
|
||||||
template <char ...digits> constexpr static auto operator "" _u110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint110_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint110_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint111_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint111_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint112_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint112_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint113_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint113_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint114_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint114_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint115_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint115_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint116_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint116_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint117_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint117_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint118_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint118_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint119_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint119_t{x}; }
|
||||||
// 120--128
|
// 120--128
|
||||||
template <char ...digits> constexpr static auto operator "" _u120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint120_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint120_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint121_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint121_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint122_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint122_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint123_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint123_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint124_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint124_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint125_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint125_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint126_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint126_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint127_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint127_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint128_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint128_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint129_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint129_t{x}; }
|
||||||
|
|
||||||
// 120--139
|
// 120--139
|
||||||
template <char ...digits> constexpr static auto operator "" _u130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint130_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint130_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint131_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint131_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint132_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint132_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint133_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint133_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint134_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint134_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint135_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint135_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint136_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint136_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint137_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint137_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint138_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint138_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint139_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint139_t{x}; }
|
||||||
// 140--149
|
// 140--149
|
||||||
template <char ...digits> constexpr static auto operator "" _u140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint140_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint140_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint141_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint141_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint142_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint142_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint143_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint143_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint144_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint144_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint145_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint145_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint146_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint146_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint147_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint147_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint148_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint148_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint149_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint149_t{x}; }
|
||||||
// 150--159
|
// 150--159
|
||||||
template <char ...digits> constexpr static auto operator "" _u150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint150_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint150_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint151_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint151_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint152_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint152_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint153_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint153_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint154_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint154_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint155_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint155_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint156_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint156_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint157_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint157_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint158_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint158_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint159_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint159_t{x}; }
|
||||||
// 160--169
|
// 160--169
|
||||||
template <char ...digits> constexpr static auto operator "" _u160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint160_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint160_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint161_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint161_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint162_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint162_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint163_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint163_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint164_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint164_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint165_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint165_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint166_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint166_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint167_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint167_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint168_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint168_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint169_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint169_t{x}; }
|
||||||
// 170-179
|
// 170-179
|
||||||
template <char ...digits> constexpr static auto operator "" _u170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint170_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint170_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint171_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint171_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint172_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint172_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint173_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint173_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint174_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint174_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint175_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint175_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint176_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint176_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint177_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint177_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint178_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint178_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint179_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint179_t{x}; }
|
||||||
// 180--189
|
// 180--189
|
||||||
template <char ...digits> constexpr static auto operator "" _u180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint180_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint180_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint181_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint181_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint182_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint182_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint183_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint183_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint184_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint184_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint185_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint185_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint186_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint186_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint187_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint187_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint188_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint188_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint189_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint189_t{x}; }
|
||||||
// 190--199
|
// 190--199
|
||||||
template <char ...digits> constexpr static auto operator "" _u190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint190_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint190_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint191_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint191_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint192_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint192_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint193_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint193_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint194_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint194_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint195_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint195_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint196_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint196_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint197_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint197_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint198_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint198_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint199_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint199_t{x}; }
|
||||||
// 200--209
|
// 200--209
|
||||||
template <char ...digits> constexpr static auto operator "" _u200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint200_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint200_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint201_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint201_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint202_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint202_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint203_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint203_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint204_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint204_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint205_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint205_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint206_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint206_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint207_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint207_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint208_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint208_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint209_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint209_t{x}; }
|
||||||
// 210--219
|
// 210--219
|
||||||
template <char ...digits> constexpr static auto operator "" _u210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint210_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint210_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint211_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint211_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint212_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint212_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint213_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint213_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint214_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint214_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint215_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint215_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint216_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint216_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint217_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint217_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint218_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint218_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint219_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint219_t{x}; }
|
||||||
// 220--229
|
// 220--229
|
||||||
template <char ...digits> constexpr static auto operator "" _u220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint220_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint220_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint221_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint221_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint222_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint222_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint223_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint223_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint224_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint224_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint225_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint225_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint226_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint226_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint227_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint227_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint228_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint228_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint229_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint229_t{x}; }
|
||||||
// 230--239
|
// 230--239
|
||||||
template <char ...digits> constexpr static auto operator "" _u230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint230_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint230_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint231_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint231_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint232_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint232_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint233_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint233_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint234_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint234_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint235_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint235_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint236_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint236_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint237_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint237_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint238_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint238_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint239_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint239_t{x}; }
|
||||||
// 240--249
|
// 240--249
|
||||||
template <char ...digits> constexpr static auto operator "" _u240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint240_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint240_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint241_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint241_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint242_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint242_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint243_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint243_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint244_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint244_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint245_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint245_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint246_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint246_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint247_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint247_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint248_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint248_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint249_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint249_t{x}; }
|
||||||
// 250--256
|
// 250--256
|
||||||
template <char ...digits> constexpr static auto operator "" _u250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint250_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint250_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint251_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint251_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint252_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint252_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint253_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint253_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint254_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint254_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint255_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint255_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _u256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint256_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _u256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint256_t{x}; }
|
||||||
|
|
||||||
} // namespace modints
|
} // namespace modints
|
||||||
|
|
||||||
|
|
@ -1362,14 +1388,23 @@ class numeric_limits<dpf::modint<Nbits>>
|
||||||
= std::numeric_limits<typename dpf::modint<Nbits>::integral_type>::traps;
|
= std::numeric_limits<typename dpf::modint<Nbits>::integral_type>::traps;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> min() noexcept { return dpf::modint<Nbits>{0}; }
|
static constexpr dpf::modint<Nbits> min() noexcept { return dpf::modint<Nbits>{0}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> lowest() noexcept { return dpf::modint<Nbits>{0}; }
|
static constexpr dpf::modint<Nbits> lowest() noexcept { return dpf::modint<Nbits>{0}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> max() noexcept { return ~dpf::modint<Nbits>{0}; }
|
static constexpr dpf::modint<Nbits> max() noexcept { return ~dpf::modint<Nbits>{0}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> epsilon() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> epsilon() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> round_error() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> round_error() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> infinity() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> infinity() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> quiet_NaN() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> quiet_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> signaling_NaN() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> signaling_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::modint<Nbits> denorm_min() noexcept { return 0; }
|
static constexpr dpf::modint<Nbits> denorm_min() noexcept { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ template <>
|
||||||
struct make_from_integral_value<dpf::nyble>
|
struct make_from_integral_value<dpf::nyble>
|
||||||
{
|
{
|
||||||
using integral_type = std::uint8_t;
|
using integral_type = std::uint8_t;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr dpf::nyble operator()(integral_type val) const noexcept
|
constexpr dpf::nyble operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
return dpf::to_nyble(val);
|
return dpf::to_nyble(val);
|
||||||
|
|
@ -189,8 +190,11 @@ class numeric_limits<dpf::nyble> : public numeric_limits<std::uint8_t>
|
||||||
public:
|
public:
|
||||||
static constexpr int digits = 4;
|
static constexpr int digits = 4;
|
||||||
static constexpr int digits10 = 1;
|
static constexpr int digits10 = 1;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::nyble min() noexcept { return dpf::nyble::zero; }
|
static constexpr dpf::nyble min() noexcept { return dpf::nyble::zero; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::nyble max() noexcept { return dpf::nyble{15}; }
|
static constexpr dpf::nyble max() noexcept { return dpf::nyble{15}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::nyble lowest() noexcept { return min(); }
|
static constexpr dpf::nyble lowest() noexcept { return min(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
/// @file dpf/output_buffer.hpp
|
/// @file dpf/output_buffer.hpp
|
||||||
/// @brief
|
/// @brief Move-only storage for shares written by multi-point evaluation.
|
||||||
/// @details
|
/// @details Slot type follows the key. A `party_key` leaf buffer holds
|
||||||
|
/// `subtractive_share`s; a comparison buffer holds
|
||||||
|
/// `additive_share`s. `bit`, `twobit`, and `nyble` slots are packed.
|
||||||
|
/// Trivially default-constructible slots are left uninitialized
|
||||||
|
/// because evaluation overwrites every slot it is responsible for.
|
||||||
|
///
|
||||||
|
/// `eval_interval` and recipe `eval_sequence` take the buffer by
|
||||||
|
/// non-const reference. The returned iterable refers into it.
|
||||||
|
/// @snippet evaluation/output_buffers.cpp output-buffer
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
|
|
@ -9,6 +17,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
#define LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
@ -77,9 +87,12 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
|
||||||
using other = output_buffer_allocator<U, Alignment>;
|
using other = output_buffer_allocator<U, Alignment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer_allocator() noexcept = default;
|
output_buffer_allocator() noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer_allocator(const output_buffer_allocator &) noexcept = default;
|
output_buffer_allocator(const output_buffer_allocator &) noexcept = default;
|
||||||
template <typename U>
|
template <typename U>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer_allocator(const output_buffer_allocator<U, Alignment> &) noexcept {}
|
output_buffer_allocator(const output_buffer_allocator<U, Alignment> &) noexcept {}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
|
|
@ -100,6 +113,7 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
void destroy(U * p) noexcept
|
void destroy(U * p) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (!std::is_trivially_destructible_v<U>)
|
if constexpr (!std::is_trivially_destructible_v<U>)
|
||||||
|
|
@ -110,6 +124,7 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t A, typename U, std::size_t B>
|
template <typename T, std::size_t A, typename U, std::size_t B>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator==(const output_buffer_allocator<T, A> &,
|
constexpr bool operator==(const output_buffer_allocator<T, A> &,
|
||||||
const output_buffer_allocator<U, B> &) noexcept
|
const output_buffer_allocator<U, B> &) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -117,12 +132,15 @@ constexpr bool operator==(const output_buffer_allocator<T, A> &,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, std::size_t A, typename U, std::size_t B>
|
template <typename T, std::size_t A, typename U, std::size_t B>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator!=(const output_buffer_allocator<T, A> & lhs,
|
constexpr bool operator!=(const output_buffer_allocator<T, A> & lhs,
|
||||||
const output_buffer_allocator<U, B> & rhs) noexcept
|
const output_buffer_allocator<U, B> & rhs) noexcept
|
||||||
{
|
{
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move-only vector of `T`. Copy construction and copy assignment are
|
||||||
|
/// deleted. `at`, `operator[]`, `data`, iterators, and `size` are public.
|
||||||
template <typename T,
|
template <typename T,
|
||||||
std::size_t Alignment = utils::max_align_v>
|
std::size_t Alignment = utils::max_align_v>
|
||||||
class output_buffer final
|
class output_buffer final
|
||||||
|
|
@ -135,13 +153,17 @@ class output_buffer final
|
||||||
using iterator = typename vector::iterator;
|
using iterator = typename vector::iterator;
|
||||||
using const_iterator = typename vector::const_iterator;
|
using const_iterator = typename vector::const_iterator;
|
||||||
using size_type = typename vector::size_type;
|
using size_type = typename vector::size_type;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer() noexcept = default;
|
output_buffer() noexcept = default;
|
||||||
explicit output_buffer(size_type size) : vector(size) { }
|
explicit output_buffer(size_type size) : vector(size) { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer(output_buffer &&) noexcept = default;
|
output_buffer(output_buffer &&) noexcept = default;
|
||||||
output_buffer(const output_buffer &) = delete;
|
output_buffer(const output_buffer &) = delete;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||||
output_buffer & operator=(const output_buffer &) = delete;
|
output_buffer & operator=(const output_buffer &) = delete;
|
||||||
~output_buffer() = default;
|
HEDLEY_NO_THROW
|
||||||
|
~output_buffer() noexcept = default;
|
||||||
|
|
||||||
// "selectively public" inheritance
|
// "selectively public" inheritance
|
||||||
using vector::at;
|
using vector::at;
|
||||||
|
|
@ -161,11 +183,14 @@ class output_buffer<dpf::bit> : public dpf::dynamic_bit_array<>
|
||||||
using size_type = typename dpf::dynamic_bit_array<>::size_type;
|
using size_type = typename dpf::dynamic_bit_array<>::size_type;
|
||||||
public:
|
public:
|
||||||
explicit output_buffer(size_type size) : dynamic_bit_array(size) { }
|
explicit output_buffer(size_type size) : dynamic_bit_array(size) { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer(output_buffer &&) noexcept = default;
|
output_buffer(output_buffer &&) noexcept = default;
|
||||||
output_buffer(const output_buffer &) = delete;
|
output_buffer(const output_buffer &) = delete;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||||
output_buffer & operator=(const output_buffer &) = delete;
|
output_buffer & operator=(const output_buffer &) = delete;
|
||||||
~output_buffer() = default;
|
HEDLEY_NO_THROW
|
||||||
|
~output_buffer() noexcept = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -175,11 +200,14 @@ class output_buffer<dpf::twobit> : public dpf::dynamic_packed_array<dpf::twobit>
|
||||||
public:
|
public:
|
||||||
using size_type = typename base::size_type;
|
using size_type = typename base::size_type;
|
||||||
explicit output_buffer(size_type size) : base(size) { }
|
explicit output_buffer(size_type size) : base(size) { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer(output_buffer &&) noexcept = default;
|
output_buffer(output_buffer &&) noexcept = default;
|
||||||
output_buffer(const output_buffer &) = delete;
|
output_buffer(const output_buffer &) = delete;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||||
output_buffer & operator=(const output_buffer &) = delete;
|
output_buffer & operator=(const output_buffer &) = delete;
|
||||||
~output_buffer() = default;
|
HEDLEY_NO_THROW
|
||||||
|
~output_buffer() noexcept = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -189,11 +217,14 @@ class output_buffer<dpf::nyble> : public dpf::dynamic_packed_array<dpf::nyble>
|
||||||
public:
|
public:
|
||||||
using size_type = typename base::size_type;
|
using size_type = typename base::size_type;
|
||||||
explicit output_buffer(size_type size) : base(size) { }
|
explicit output_buffer(size_type size) : base(size) { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer(output_buffer &&) noexcept = default;
|
output_buffer(output_buffer &&) noexcept = default;
|
||||||
output_buffer(const output_buffer &) = delete;
|
output_buffer(const output_buffer &) = delete;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||||
output_buffer & operator=(const output_buffer &) = delete;
|
output_buffer & operator=(const output_buffer &) = delete;
|
||||||
~output_buffer() = default;
|
HEDLEY_NO_THROW
|
||||||
|
~output_buffer() noexcept = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LIBDPF_PACKED_SHARE_BUFFER(LANE, PARTY) \
|
#define LIBDPF_PACKED_SHARE_BUFFER(LANE, PARTY) \
|
||||||
|
|
@ -205,11 +236,14 @@ class output_buffer<subtractive_share<LANE, PARTY>>
|
||||||
public: \
|
public: \
|
||||||
using size_type = typename base::size_type; \
|
using size_type = typename base::size_type; \
|
||||||
explicit output_buffer(size_type size) : base(size) {} \
|
explicit output_buffer(size_type size) : base(size) {} \
|
||||||
|
HEDLEY_NO_THROW \
|
||||||
output_buffer(output_buffer &&) noexcept = default; \
|
output_buffer(output_buffer &&) noexcept = default; \
|
||||||
output_buffer(const output_buffer &) = delete; \
|
output_buffer(const output_buffer &) = delete; \
|
||||||
|
HEDLEY_NO_THROW \
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
||||||
output_buffer & operator=(const output_buffer &) = delete; \
|
output_buffer & operator=(const output_buffer &) = delete; \
|
||||||
~output_buffer() = default; \
|
HEDLEY_NO_THROW \
|
||||||
|
~output_buffer() noexcept = default; \
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBDPF_PACKED_SHARE_BUFFER(dpf::twobit, 0);
|
LIBDPF_PACKED_SHARE_BUFFER(dpf::twobit, 0);
|
||||||
|
|
@ -232,12 +266,14 @@ class output_buffer<subtractive_share<dpf::bit, PARTY>>
|
||||||
output_buffer(const output_buffer &) = delete; \
|
output_buffer(const output_buffer &) = delete; \
|
||||||
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
||||||
output_buffer & operator=(const output_buffer &) = delete; \
|
output_buffer & operator=(const output_buffer &) = delete; \
|
||||||
~output_buffer() = default; \
|
~output_buffer() noexcept = default; \
|
||||||
};
|
};
|
||||||
LIBDPF_BIT_SHARE_BUFFER(0);
|
LIBDPF_BIT_SHARE_BUFFER(0);
|
||||||
LIBDPF_BIT_SHARE_BUFFER(1);
|
LIBDPF_BIT_SHARE_BUFFER(1);
|
||||||
#undef LIBDPF_BIT_SHARE_BUFFER
|
#undef LIBDPF_BIT_SHARE_BUFFER
|
||||||
|
|
||||||
|
/// Buffer sized for the closed interval `[from, to]` of output `I`.
|
||||||
|
/// On a `party_key`, elements are subtractive shares of that output.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
std::size_t I = 0,
|
std::size_t I = 0,
|
||||||
typename InputT>
|
typename InputT>
|
||||||
|
|
@ -247,9 +283,6 @@ auto make_output_buffer_for_interval(InputT from, InputT to)
|
||||||
using output_type = typename DpfKey::concrete_output_type<I>;
|
using output_type = typename DpfKey::concrete_output_type<I>;
|
||||||
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
|
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
|
||||||
|
|
||||||
utils::flip_msb_if_signed_integral(from);
|
|
||||||
utils::flip_msb_if_signed_integral(to);
|
|
||||||
|
|
||||||
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
|
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
|
||||||
return dpf::output_buffer<buffer_elem>(nodes_in_interval*dpf_type::outputs_per_leaf);
|
return dpf::output_buffer<buffer_elem>(nodes_in_interval*dpf_type::outputs_per_leaf);
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +318,7 @@ inline auto make_output_buffer_for_interval(const DpfKey &, InputT from, InputT
|
||||||
return make_output_buffer_for_interval<DpfKey, I0, I1, Is...>(from, to);
|
return make_output_buffer_for_interval<DpfKey, I0, I1, Is...>(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Buffer sized for every input of output `I`.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
std::size_t I = 0>
|
std::size_t I = 0>
|
||||||
auto make_output_buffer_for_full()
|
auto make_output_buffer_for_full()
|
||||||
|
|
|
||||||
|
|
@ -50,15 +50,18 @@ class dynamic_packed_array
|
||||||
class lane_ref
|
class lane_ref
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref(word_type * word, unsigned shift) noexcept
|
lane_ref(word_type * word, unsigned shift) noexcept
|
||||||
: word_{word}, shift_{shift} {}
|
: word_{word}, shift_{shift} {}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
operator LaneT() const noexcept
|
operator LaneT() const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<LaneT>((*word_ >> shift_) & lane_mask);
|
return static_cast<LaneT>((*word_ >> shift_) & lane_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
lane_ref & operator=(LaneT value) noexcept
|
lane_ref & operator=(LaneT value) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -69,23 +72,28 @@ class dynamic_packed_array
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref & operator=(const lane_ref & other) noexcept
|
lane_ref & operator=(const lane_ref & other) noexcept
|
||||||
{
|
{
|
||||||
return (*this = static_cast<LaneT>(other));
|
return (*this = static_cast<LaneT>(other));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator==(lane_ref lhs, LaneT rhs) noexcept
|
friend bool operator==(lane_ref lhs, LaneT rhs) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<LaneT>(lhs) == rhs;
|
return static_cast<LaneT>(lhs) == rhs;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator==(LaneT lhs, lane_ref rhs) noexcept
|
friend bool operator==(LaneT lhs, lane_ref rhs) noexcept
|
||||||
{
|
{
|
||||||
return rhs == lhs;
|
return rhs == lhs;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator!=(lane_ref lhs, LaneT rhs) noexcept
|
friend bool operator!=(lane_ref lhs, LaneT rhs) noexcept
|
||||||
{
|
{
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator!=(LaneT lhs, lane_ref rhs) noexcept
|
friend bool operator!=(LaneT lhs, lane_ref rhs) noexcept
|
||||||
{
|
{
|
||||||
return !(rhs == lhs);
|
return !(rhs == lhs);
|
||||||
|
|
@ -108,25 +116,33 @@ class dynamic_packed_array
|
||||||
using pointer = void;
|
using pointer = void;
|
||||||
using reference = lane_ref;
|
using reference = lane_ref;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator() noexcept = default;
|
iterator() noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator(word_type * data, size_type index) noexcept
|
iterator(word_type * data, size_type index) noexcept
|
||||||
: data_{data}, index_{index} {}
|
: data_{data}, index_{index} {}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref operator*() const noexcept { return ref_at(index_); }
|
lane_ref operator*() const noexcept { return ref_at(index_); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref operator[](difference_type n) const noexcept
|
lane_ref operator[](difference_type n) const noexcept
|
||||||
{
|
{
|
||||||
return ref_at(static_cast<size_type>(
|
return ref_at(static_cast<size_type>(
|
||||||
static_cast<difference_type>(index_) + n));
|
static_cast<difference_type>(index_) + n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator++() noexcept { ++index_; return *this; }
|
iterator & operator++() noexcept { ++index_; return *this; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator operator++(int) noexcept
|
iterator operator++(int) noexcept
|
||||||
{
|
{
|
||||||
iterator prev = *this;
|
iterator prev = *this;
|
||||||
++*this;
|
++*this;
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator--() noexcept { --index_; return *this; }
|
iterator & operator--() noexcept { --index_; return *this; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator operator--(int) noexcept
|
iterator operator--(int) noexcept
|
||||||
{
|
{
|
||||||
iterator prev = *this;
|
iterator prev = *this;
|
||||||
|
|
@ -134,53 +150,66 @@ class dynamic_packed_array
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator+=(difference_type n) noexcept
|
iterator & operator+=(difference_type n) noexcept
|
||||||
{
|
{
|
||||||
index_ = static_cast<size_type>(
|
index_ = static_cast<size_type>(
|
||||||
static_cast<difference_type>(index_) + n);
|
static_cast<difference_type>(index_) + n);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator-=(difference_type n) noexcept
|
iterator & operator-=(difference_type n) noexcept
|
||||||
{
|
{
|
||||||
return *this += -n;
|
return *this += -n;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator+(iterator it, difference_type n) noexcept
|
friend iterator operator+(iterator it, difference_type n) noexcept
|
||||||
{
|
{
|
||||||
it += n;
|
it += n;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator+(difference_type n, iterator it) noexcept
|
friend iterator operator+(difference_type n, iterator it) noexcept
|
||||||
{
|
{
|
||||||
return it + n;
|
return it + n;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator-(iterator it, difference_type n) noexcept
|
friend iterator operator-(iterator it, difference_type n) noexcept
|
||||||
{
|
{
|
||||||
it -= n;
|
it -= n;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend difference_type operator-(iterator a, iterator b) noexcept
|
friend difference_type operator-(iterator a, iterator b) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<difference_type>(a.index_)
|
return static_cast<difference_type>(a.index_)
|
||||||
- static_cast<difference_type>(b.index_);
|
- static_cast<difference_type>(b.index_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator==(iterator a, iterator b) noexcept
|
friend bool operator==(iterator a, iterator b) noexcept
|
||||||
{
|
{
|
||||||
return a.index_ == b.index_;
|
return a.index_ == b.index_;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator!=(iterator a, iterator b) noexcept
|
friend bool operator!=(iterator a, iterator b) noexcept
|
||||||
{
|
{
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator<(iterator a, iterator b) noexcept
|
friend bool operator<(iterator a, iterator b) noexcept
|
||||||
{
|
{
|
||||||
return a.index_ < b.index_;
|
return a.index_ < b.index_;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref ref_at(size_type index) const noexcept
|
lane_ref ref_at(size_type index) const noexcept
|
||||||
{
|
{
|
||||||
const size_type bit = index * lane_bits;
|
const size_type bit = index * lane_bits;
|
||||||
|
|
@ -213,12 +242,14 @@ class dynamic_packed_array
|
||||||
dynamic_packed_array(const dynamic_packed_array &) = delete;
|
dynamic_packed_array(const dynamic_packed_array &) = delete;
|
||||||
dynamic_packed_array & operator=(const dynamic_packed_array &) = delete;
|
dynamic_packed_array & operator=(const dynamic_packed_array &) = delete;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dynamic_packed_array(dynamic_packed_array && other) noexcept
|
dynamic_packed_array(dynamic_packed_array && other) noexcept
|
||||||
: nlanes_{std::exchange(other.nlanes_, 0)},
|
: nlanes_{std::exchange(other.nlanes_, 0)},
|
||||||
nwords_{std::exchange(other.nwords_, 0)},
|
nwords_{std::exchange(other.nwords_, 0)},
|
||||||
data_{std::move(other.data_)}
|
data_{std::move(other.data_)}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dynamic_packed_array & operator=(dynamic_packed_array && other) noexcept
|
dynamic_packed_array & operator=(dynamic_packed_array && other) noexcept
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
|
|
@ -232,13 +263,19 @@ class dynamic_packed_array
|
||||||
|
|
||||||
~dynamic_packed_array() = default;
|
~dynamic_packed_array() = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type size() const noexcept { return nlanes_; }
|
size_type size() const noexcept { return nlanes_; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool empty() const noexcept { return nlanes_ == 0; }
|
bool empty() const noexcept { return nlanes_ == 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
size_type data_length() const noexcept { return nwords_; }
|
size_type data_length() const noexcept { return nwords_; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
word_type * data() noexcept { return data_.get(); }
|
word_type * data() noexcept { return data_.get(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const word_type * data() const noexcept { return data_.get(); }
|
const word_type * data() const noexcept { return data_.get(); }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
LaneT operator[](size_type i) const noexcept
|
LaneT operator[](size_type i) const noexcept
|
||||||
{
|
{
|
||||||
assert(i < nlanes_);
|
assert(i < nlanes_);
|
||||||
|
|
@ -247,6 +284,7 @@ class dynamic_packed_array
|
||||||
return static_cast<LaneT>((data_[bit / 64u] >> shift) & lane_mask);
|
return static_cast<LaneT>((data_[bit / 64u] >> shift) & lane_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
lane_ref operator[](size_type i) noexcept
|
lane_ref operator[](size_type i) noexcept
|
||||||
{
|
{
|
||||||
assert(i < nlanes_);
|
assert(i < nlanes_);
|
||||||
|
|
@ -255,11 +293,17 @@ class dynamic_packed_array
|
||||||
static_cast<unsigned>(bit % 64u));
|
static_cast<unsigned>(bit % 64u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator begin() noexcept { return iterator{data(), 0}; }
|
iterator begin() noexcept { return iterator{data(), 0}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator end() noexcept { return iterator{data(), nlanes_}; }
|
iterator end() noexcept { return iterator{data(), nlanes_}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator begin() const noexcept { return iterator{data_.get(), 0}; }
|
iterator begin() const noexcept { return iterator{data_.get(), 0}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator end() const noexcept { return iterator{data_.get(), nlanes_}; }
|
iterator end() const noexcept { return iterator{data_.get(), nlanes_}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator cbegin() const noexcept { return begin(); }
|
iterator cbegin() const noexcept { return begin(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator cend() const noexcept { return end(); }
|
iterator cend() const noexcept { return end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -284,25 +328,30 @@ class packed_share_output : public dynamic_packed_array<LaneT>
|
||||||
class reference
|
class reference
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
explicit reference(typename lanes::reference lane) noexcept : lane_{lane} {}
|
explicit reference(typename lanes::reference lane) noexcept : lane_{lane} {}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
operator share_type() const noexcept
|
operator share_type() const noexcept
|
||||||
{
|
{
|
||||||
return share_type::from_raw(static_cast<LaneT>(lane_));
|
return share_type::from_raw(static_cast<LaneT>(lane_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference & operator=(const share_type & share) noexcept
|
reference & operator=(const share_type & share) noexcept
|
||||||
{
|
{
|
||||||
lane_ = share.raw();
|
lane_ = share.raw();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference & operator=(LaneT value) noexcept
|
reference & operator=(LaneT value) noexcept
|
||||||
{
|
{
|
||||||
lane_ = value;
|
lane_ = value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference & operator=(const reference & other) noexcept
|
reference & operator=(const reference & other) noexcept
|
||||||
{
|
{
|
||||||
return (*this = static_cast<share_type>(other));
|
return (*this = static_cast<share_type>(other));
|
||||||
|
|
@ -321,48 +370,68 @@ class packed_share_output : public dynamic_packed_array<LaneT>
|
||||||
using pointer = void;
|
using pointer = void;
|
||||||
using reference = share_type;
|
using reference = share_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator() noexcept = default;
|
iterator() noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
explicit iterator(typename lanes::iterator it) noexcept : it_{it} {}
|
explicit iterator(typename lanes::iterator it) noexcept : it_{it} {}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
share_type operator*() const noexcept
|
share_type operator*() const noexcept
|
||||||
{
|
{
|
||||||
return share_type::from_raw(static_cast<LaneT>(*it_));
|
return share_type::from_raw(static_cast<LaneT>(*it_));
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
share_type operator[](difference_type n) const noexcept
|
share_type operator[](difference_type n) const noexcept
|
||||||
{
|
{
|
||||||
return share_type::from_raw(static_cast<LaneT>(it_[n]));
|
return share_type::from_raw(static_cast<LaneT>(it_[n]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator++() noexcept { ++it_; return *this; }
|
iterator & operator++() noexcept { ++it_; return *this; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator operator++(int) noexcept { iterator p = *this; ++*this; return p; }
|
iterator operator++(int) noexcept { iterator p = *this; ++*this; return p; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator--() noexcept { --it_; return *this; }
|
iterator & operator--() noexcept { --it_; return *this; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator operator--(int) noexcept { iterator p = *this; --*this; return p; }
|
iterator operator--(int) noexcept { iterator p = *this; --*this; return p; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator+=(difference_type n) noexcept { it_ += n; return *this; }
|
iterator & operator+=(difference_type n) noexcept { it_ += n; return *this; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator & operator-=(difference_type n) noexcept { it_ -= n; return *this; }
|
iterator & operator-=(difference_type n) noexcept { it_ -= n; return *this; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator+(iterator it, difference_type n) noexcept
|
friend iterator operator+(iterator it, difference_type n) noexcept
|
||||||
{
|
{
|
||||||
it += n;
|
it += n;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator+(difference_type n, iterator it) noexcept
|
friend iterator operator+(difference_type n, iterator it) noexcept
|
||||||
{
|
{
|
||||||
return it + n;
|
return it + n;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend iterator operator-(iterator it, difference_type n) noexcept
|
friend iterator operator-(iterator it, difference_type n) noexcept
|
||||||
{
|
{
|
||||||
it -= n;
|
it -= n;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend difference_type operator-(iterator a, iterator b) noexcept
|
friend difference_type operator-(iterator a, iterator b) noexcept
|
||||||
{
|
{
|
||||||
return a.it_ - b.it_;
|
return a.it_ - b.it_;
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator==(iterator a, iterator b) noexcept { return a.it_ == b.it_; }
|
friend bool operator==(iterator a, iterator b) noexcept { return a.it_ == b.it_; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator!=(iterator a, iterator b) noexcept { return !(a == b); }
|
friend bool operator!=(iterator a, iterator b) noexcept { return !(a == b); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator<(iterator a, iterator b) noexcept { return a.it_ < b.it_; }
|
friend bool operator<(iterator a, iterator b) noexcept { return a.it_ < b.it_; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -375,7 +444,9 @@ class packed_share_output : public dynamic_packed_array<LaneT>
|
||||||
|
|
||||||
packed_share_output(const packed_share_output &) = delete;
|
packed_share_output(const packed_share_output &) = delete;
|
||||||
packed_share_output & operator=(const packed_share_output &) = delete;
|
packed_share_output & operator=(const packed_share_output &) = delete;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
packed_share_output(packed_share_output &&) noexcept = default;
|
packed_share_output(packed_share_output &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
packed_share_output & operator=(packed_share_output &&) noexcept = default;
|
packed_share_output & operator=(packed_share_output &&) noexcept = default;
|
||||||
~packed_share_output() = default;
|
~packed_share_output() = default;
|
||||||
|
|
||||||
|
|
@ -383,26 +454,34 @@ class packed_share_output : public dynamic_packed_array<LaneT>
|
||||||
using lanes::empty;
|
using lanes::empty;
|
||||||
using lanes::size;
|
using lanes::size;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference operator[](size_type i) noexcept
|
reference operator[](size_type i) noexcept
|
||||||
{
|
{
|
||||||
return reference{lanes::operator[](i)};
|
return reference{lanes::operator[](i)};
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
share_type operator[](size_type i) const noexcept
|
share_type operator[](size_type i) const noexcept
|
||||||
{
|
{
|
||||||
return share_type::from_raw(lanes::operator[](i));
|
return share_type::from_raw(lanes::operator[](i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator begin() noexcept { return iterator{lanes::begin()}; }
|
iterator begin() noexcept { return iterator{lanes::begin()}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator end() noexcept { return iterator{lanes::end()}; }
|
iterator end() noexcept { return iterator{lanes::end()}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator begin() const noexcept
|
iterator begin() const noexcept
|
||||||
{
|
{
|
||||||
return iterator{typename lanes::iterator{this->data(), 0}};
|
return iterator{typename lanes::iterator{this->data(), 0}};
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator end() const noexcept
|
iterator end() const noexcept
|
||||||
{
|
{
|
||||||
return iterator{typename lanes::iterator{this->data(), this->size()}};
|
return iterator{typename lanes::iterator{this->data(), this->size()}};
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator cbegin() const noexcept { return begin(); }
|
iterator cbegin() const noexcept { return begin(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
iterator cend() const noexcept { return end(); }
|
iterator cend() const noexcept { return end(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace detail
|
||||||
|
|
||||||
template <unsigned Bits>
|
template <unsigned Bits>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
unsigned lane_at(unsigned byte, unsigned shift) noexcept
|
unsigned lane_at(unsigned byte, unsigned shift) noexcept
|
||||||
{
|
{
|
||||||
constexpr unsigned mask = (1u << Bits) - 1u;
|
constexpr unsigned mask = (1u << Bits) - 1u;
|
||||||
|
|
@ -67,6 +68,7 @@ NodeT apply_bytes(const NodeT & a, const NodeT & b, Op op) noexcept
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m128i nibble_lut(const unsigned char lut[16]) noexcept
|
inline simde__m128i nibble_lut(const unsigned char lut[16]) noexcept
|
||||||
{
|
{
|
||||||
simde__m128i table;
|
simde__m128i table;
|
||||||
|
|
@ -74,6 +76,7 @@ inline simde__m128i nibble_lut(const unsigned char lut[16]) noexcept
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m256i nibble_lut256(const unsigned char lut[16]) noexcept
|
inline simde__m256i nibble_lut256(const unsigned char lut[16]) noexcept
|
||||||
{
|
{
|
||||||
alignas(32) unsigned char both[32];
|
alignas(32) unsigned char both[32];
|
||||||
|
|
@ -84,6 +87,7 @@ inline simde__m256i nibble_lut256(const unsigned char lut[16]) noexcept
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline void fill_epi2_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
inline void fill_epi2_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
||||||
{
|
{
|
||||||
k &= 3u;
|
k &= 3u;
|
||||||
|
|
@ -95,6 +99,7 @@ inline void fill_epi2_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline void fill_epi4_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
inline void fill_epi4_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
||||||
{
|
{
|
||||||
k &= 0x0fu;
|
k &= 0x0fu;
|
||||||
|
|
@ -104,6 +109,7 @@ inline void fill_epi4_mul_lut(unsigned k, unsigned char lut[16]) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m128i shuffle_nibbles(simde__m128i table, simde__m128i a) noexcept
|
inline simde__m128i shuffle_nibbles(simde__m128i table, simde__m128i a) noexcept
|
||||||
{
|
{
|
||||||
const auto m = simde_mm_set1_epi8(0x0f);
|
const auto m = simde_mm_set1_epi8(0x0f);
|
||||||
|
|
@ -114,6 +120,7 @@ inline simde__m128i shuffle_nibbles(simde__m128i table, simde__m128i a) noexcept
|
||||||
simde_mm_slli_epi16(simde_mm_and_si128(hi, m), 4));
|
simde_mm_slli_epi16(simde_mm_and_si128(hi, m), 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m256i shuffle_nibbles(simde__m256i table, simde__m256i a) noexcept
|
inline simde__m256i shuffle_nibbles(simde__m256i table, simde__m256i a) noexcept
|
||||||
{
|
{
|
||||||
const auto m = simde_mm256_set1_epi8(0x0f);
|
const auto m = simde_mm256_set1_epi8(0x0f);
|
||||||
|
|
@ -126,6 +133,7 @@ inline simde__m256i shuffle_nibbles(simde__m256i table, simde__m256i a) noexcept
|
||||||
|
|
||||||
/// Low nibble of every byte, product mod 16. Even and odd bytes are split
|
/// Low nibble of every byte, product mod 16. Even and odd bytes are split
|
||||||
/// so a product in one byte cannot land in the next.
|
/// so a product in one byte cannot land in the next.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m128i mul_low_nibbles(simde__m128i a, simde__m128i b) noexcept
|
inline simde__m128i mul_low_nibbles(simde__m128i a, simde__m128i b) noexcept
|
||||||
{
|
{
|
||||||
const auto lane = simde_mm_set1_epi16(0x000f);
|
const auto lane = simde_mm_set1_epi16(0x000f);
|
||||||
|
|
@ -138,6 +146,7 @@ inline simde__m128i mul_low_nibbles(simde__m128i a, simde__m128i b) noexcept
|
||||||
return simde_mm_or_si128(pe, simde_mm_slli_epi16(po, 8));
|
return simde_mm_or_si128(pe, simde_mm_slli_epi16(po, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline simde__m256i mul_low_nibbles(simde__m256i a, simde__m256i b) noexcept
|
inline simde__m256i mul_low_nibbles(simde__m256i a, simde__m256i b) noexcept
|
||||||
{
|
{
|
||||||
const auto lane = simde_mm256_set1_epi16(0x000f);
|
const auto lane = simde_mm256_set1_epi16(0x000f);
|
||||||
|
|
|
||||||
|
|
@ -143,9 +143,11 @@ class parallel_const_bit_iterator
|
||||||
using const_reference = const value_type &;
|
using const_reference = const value_type &;
|
||||||
using pointer = std::add_pointer_t<value_type>;
|
using pointer = std::add_pointer_t<value_type>;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr
|
constexpr
|
||||||
parallel_const_bit_iterator(parallel_const_bit_iterator &&) noexcept = default;
|
parallel_const_bit_iterator(parallel_const_bit_iterator &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr
|
constexpr
|
||||||
parallel_const_bit_iterator(const parallel_const_bit_iterator &) noexcept = default;
|
parallel_const_bit_iterator(const parallel_const_bit_iterator &) noexcept = default;
|
||||||
|
|
@ -273,6 +275,7 @@ class parallel_const_bit_iterator
|
||||||
std::make_index_sequence<batch_size>());
|
std::make_index_sequence<batch_size>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
explicit constexpr parallel_const_bit_iterator(
|
explicit constexpr parallel_const_bit_iterator(
|
||||||
const word_pointer_array & arr) noexcept
|
const word_pointer_array & arr) noexcept
|
||||||
: iter_{arr},
|
: iter_{arr},
|
||||||
|
|
@ -293,7 +296,9 @@ class parallel_const_bit_iterator
|
||||||
simde_type vec_mask_;
|
simde_type vec_mask_;
|
||||||
simde_array all_vecs_;
|
simde_array all_vecs_;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::begin() const noexcept;
|
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::begin() const noexcept;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::end() const noexcept;
|
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::end() const noexcept;
|
||||||
}; // class dpf::parallel_const_bit_iterator
|
}; // class dpf::parallel_const_bit_iterator
|
||||||
|
|
||||||
|
|
@ -302,6 +307,7 @@ template <std::size_t N,
|
||||||
typename Iter>
|
typename Iter>
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto batch_of(Iter it) noexcept
|
auto batch_of(Iter it) noexcept
|
||||||
{
|
{
|
||||||
return dpf::parallel_bit_iterable<N, ChildT>{it};
|
return dpf::parallel_bit_iterable<N, ChildT>{it};
|
||||||
|
|
@ -311,6 +317,7 @@ template <typename ChildT,
|
||||||
typename ...Ts>
|
typename ...Ts>
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto batch_of(const dpf::bit_array_base<ChildT> & t, const Ts & ...ts) noexcept
|
auto batch_of(const dpf::bit_array_base<ChildT> & t, const Ts & ...ts) noexcept
|
||||||
{
|
{
|
||||||
return dpf::parallel_bit_iterable<1+sizeof...(Ts), ChildT>{t, ts...};
|
return dpf::parallel_bit_iterable<1+sizeof...(Ts), ChildT>{t, ts...};
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
||||||
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
||||||
static constexpr auto bit_and = simde_mm256_and_si256;
|
static constexpr auto bit_and = simde_mm256_and_si256;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto get_mask() noexcept
|
static auto get_mask() noexcept
|
||||||
{
|
{
|
||||||
return simde_mm256_set1_epi64x(1);
|
return simde_mm256_set1_epi64x(1);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static simde_array build_vecs(const word_type * cur_word, std::size_t nwords) noexcept
|
static simde_array build_vecs(const word_type * cur_word, std::size_t nwords) noexcept
|
||||||
{
|
{
|
||||||
return { loadu_word_vec(cur_word, nwords, 0) };
|
return { loadu_word_vec(cur_word, nwords, 0) };
|
||||||
|
|
@ -104,10 +106,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
||||||
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
||||||
static constexpr auto bit_and = simde_mm256_and_si256;
|
static constexpr auto bit_and = simde_mm256_and_si256;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto get_mask() noexcept
|
static auto get_mask() noexcept
|
||||||
{
|
{
|
||||||
return simde_mm256_set1_epi32(1);
|
return simde_mm256_set1_epi32(1);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
||||||
std::size_t nwords) noexcept
|
std::size_t nwords) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -149,10 +153,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
||||||
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
||||||
static constexpr auto bit_and = simde_mm256_and_si256;
|
static constexpr auto bit_and = simde_mm256_and_si256;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto get_mask() noexcept
|
static auto get_mask() noexcept
|
||||||
{
|
{
|
||||||
return simde_mm256_set1_epi16(1);
|
return simde_mm256_set1_epi16(1);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
||||||
std::size_t nwords) noexcept
|
std::size_t nwords) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -215,10 +221,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
static constexpr auto left_shift = simde_mm256_slli_epi64;
|
||||||
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
static constexpr auto right_shift = simde_mm256_srli_epi64;
|
||||||
static constexpr auto bit_and = simde_mm256_and_si256;
|
static constexpr auto bit_and = simde_mm256_and_si256;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto get_mask() noexcept
|
static auto get_mask() noexcept
|
||||||
{
|
{
|
||||||
return simde_mm256_set1_epi8(1);
|
return simde_mm256_set1_epi8(1);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
|
||||||
std::size_t nwords) noexcept
|
std::size_t nwords) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
/// @file dpf/path_memoizer.hpp
|
/// @file dpf/path_memoizer.hpp
|
||||||
/// @brief
|
/// @brief Workspaces that resume a root-to-leaf DPF walk.
|
||||||
/// @details
|
/// @details `basic_path_memoizer` keeps one interior node per level. The next
|
||||||
|
/// `eval_point` recomputes the suffix after the common prefix with
|
||||||
|
/// the previous input. `nonmemoizing_path_memoizer` keeps a single
|
||||||
|
/// node and starts at the root on every call.
|
||||||
|
///
|
||||||
|
/// Pass a mutable lvalue to `eval_point`. The factories unwrap
|
||||||
|
/// `party_key`, so a memoizer built from either party's type
|
||||||
|
/// accepts both parties. A different root restarts the path.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -44,13 +51,21 @@ struct path_memoizer_base
|
||||||
using return_type = ReturnT;
|
using return_type = ReturnT;
|
||||||
using iterator_type = return_type;
|
using iterator_type = return_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual std::size_t assign_x(const dpf_type &, input_type) noexcept = 0;
|
virtual std::size_t assign_x(const dpf_type &, input_type) noexcept = 0;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual node_type & operator[](std::size_t) noexcept = 0;
|
virtual node_type & operator[](std::size_t) noexcept = 0;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type begin() const noexcept = 0;
|
virtual return_type begin() const noexcept = 0;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type end() const noexcept = 0;
|
virtual return_type end() const noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// One interior node per level. `assign_x` returns the first level that the
|
||||||
|
/// next walk must recompute. `filled_to` is the deepest level already
|
||||||
|
/// written for the current input. Callers pass this object to `eval_point`;
|
||||||
|
/// they do not call `assign_x` themselves.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
struct alignas(alignof(typename path_memoizer_key_t<DpfKey>::interior_node))
|
struct alignas(alignof(typename path_memoizer_key_t<DpfKey>::interior_node))
|
||||||
basic_path_memoizer final
|
basic_path_memoizer final
|
||||||
|
|
@ -69,12 +84,15 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
basic_path_memoizer()
|
basic_path_memoizer()
|
||||||
: dpf_{std::nullopt}, x_{std::nullopt}, filled_to_{0} { }
|
: dpf_{std::nullopt}, x_{std::nullopt}, filled_to_{0} { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
basic_path_memoizer(basic_path_memoizer &&) noexcept = default;
|
basic_path_memoizer(basic_path_memoizer &&) noexcept = default;
|
||||||
basic_path_memoizer(const basic_path_memoizer &) = default;
|
basic_path_memoizer(const basic_path_memoizer &) = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
basic_path_memoizer & operator=(basic_path_memoizer &&) noexcept = default;
|
basic_path_memoizer & operator=(basic_path_memoizer &&) noexcept = default;
|
||||||
basic_path_memoizer & operator=(const basic_path_memoizer &) = default;
|
basic_path_memoizer & operator=(const basic_path_memoizer &) = default;
|
||||||
~basic_path_memoizer() = default;
|
~basic_path_memoizer() = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t assign_x(const dpf_type & dpf, input_type new_x) noexcept override
|
std::size_t assign_x(const dpf_type & dpf, input_type new_x) noexcept override
|
||||||
{
|
{
|
||||||
static constexpr auto clz_xor = utils::countl_zero_symmetric_difference<input_type>{};
|
static constexpr auto clz_xor = utils::countl_zero_symmetric_difference<input_type>{};
|
||||||
|
|
@ -100,11 +118,13 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
node_type & operator[](std::size_t i) noexcept override
|
node_type & operator[](std::size_t i) noexcept override
|
||||||
{
|
{
|
||||||
return arr_[i];
|
return arr_[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
return_type begin() const noexcept override
|
return_type begin() const noexcept override
|
||||||
{
|
{
|
||||||
if (x_.has_value() == true)
|
if (x_.has_value() == true)
|
||||||
|
|
@ -117,14 +137,17 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
return_type end() const noexcept override
|
return_type end() const noexcept override
|
||||||
{
|
{
|
||||||
return std::addressof(arr_[depth+1]);
|
return std::addressof(arr_[depth+1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inclusive high-water: `arr_[0..filled_to_]` are valid for the current x.
|
/// Inclusive high-water: `arr_[0..filled_to_]` are valid for the current x.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t filled_to() const noexcept { return filled_to_; }
|
std::size_t filled_to() const noexcept { return filled_to_; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
void note_filled(std::size_t level) noexcept
|
void note_filled(std::size_t level) noexcept
|
||||||
{
|
{
|
||||||
if (level > filled_to_)
|
if (level > filled_to_)
|
||||||
|
|
@ -143,6 +166,7 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||||
HEDLEY_PRAGMA(GCC diagnostic pop)
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A single interior node. Every `assign_x` restarts at the root.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
struct nonmemoizing_path_memoizer final
|
struct nonmemoizing_path_memoizer final
|
||||||
: public path_memoizer_base<path_memoizer_key_t<DpfKey>>
|
: public path_memoizer_base<path_memoizer_key_t<DpfKey>>
|
||||||
|
|
@ -159,8 +183,10 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
nonmemoizing_path_memoizer()
|
nonmemoizing_path_memoizer()
|
||||||
: dpf_{std::nullopt} { }
|
: dpf_{std::nullopt} { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
nonmemoizing_path_memoizer(nonmemoizing_path_memoizer &&) noexcept = default;
|
nonmemoizing_path_memoizer(nonmemoizing_path_memoizer &&) noexcept = default;
|
||||||
nonmemoizing_path_memoizer(const nonmemoizing_path_memoizer &) = default;
|
nonmemoizing_path_memoizer(const nonmemoizing_path_memoizer &) = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
nonmemoizing_path_memoizer & operator=(nonmemoizing_path_memoizer &&) noexcept = default;
|
nonmemoizing_path_memoizer & operator=(nonmemoizing_path_memoizer &&) noexcept = default;
|
||||||
nonmemoizing_path_memoizer & operator=(const nonmemoizing_path_memoizer &) = default;
|
nonmemoizing_path_memoizer & operator=(const nonmemoizing_path_memoizer &) = default;
|
||||||
~nonmemoizing_path_memoizer() = default;
|
~nonmemoizing_path_memoizer() = default;
|
||||||
|
|
@ -186,6 +212,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
return std::addressof(v);
|
return std::addressof(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
return_type end() const noexcept override
|
return_type end() const noexcept override
|
||||||
{
|
{
|
||||||
return std::addressof(v) + 1;
|
return std::addressof(v) + 1;
|
||||||
|
|
@ -260,6 +287,9 @@ void ensure_level(const DpfKey & dpf, typename DpfKey::input_type x,
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Path workspace for `DpfKey`. A `party_key` argument is unwrapped, and the
|
||||||
|
/// result accepts both parties.
|
||||||
|
/// @snippet evaluation/memoizers.cpp path-memoizer
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
auto make_basic_path_memoizer()
|
auto make_basic_path_memoizer()
|
||||||
{
|
{
|
||||||
|
|
@ -272,6 +302,7 @@ auto make_basic_path_memoizer(const DpfKey &)
|
||||||
return make_basic_path_memoizer<DpfKey>();
|
return make_basic_path_memoizer<DpfKey>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Single-node path workspace. Suitable for one query.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
auto make_nonmemoizing_path_memoizer()
|
auto make_nonmemoizing_path_memoizer()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ template <typename T> inline constexpr bool is_at_v = is_at<T>::value;
|
||||||
/// `OutBits` is the comparison output group width (bits of the β payload),
|
/// `OutBits` is the comparison output group width (bits of the β payload),
|
||||||
/// so the value CWs / addend can be stored at group width instead of a full
|
/// so the value CWs / addend can be stored at group width instead of a full
|
||||||
/// padded `uint64_t` per level.
|
/// padded `uint64_t` per level.
|
||||||
template <std::size_t Depth, std::size_t OutBits = 0, bool Wild = false>
|
template <std::size_t Depth, std::size_t OutBits = 0, bool Wild = false,
|
||||||
|
std::size_t BlockWidth = 0, bool Incremental = false>
|
||||||
struct cmp_channel_tag
|
struct cmp_channel_tag
|
||||||
{
|
{
|
||||||
static constexpr std::size_t depth = Depth;
|
static constexpr std::size_t depth = Depth;
|
||||||
|
|
@ -73,11 +74,17 @@ struct cmp_channel_tag
|
||||||
/// after keygen. Concrete (non-wildcard) cmp keys keep `Wild == false`
|
/// after keygen. Concrete (non-wildcard) cmp keys keep `Wild == false`
|
||||||
/// so their layout / type name is unchanged.
|
/// so their layout / type name is unchanged.
|
||||||
static constexpr bool wild = Wild;
|
static constexpr bool wild = Wild;
|
||||||
|
/// 0 keeps the per-level path-sum. `B >= 1` selects blocked checkpoints
|
||||||
|
/// of target width `B`.
|
||||||
|
static constexpr std::size_t block_width = BlockWidth;
|
||||||
|
/// Save a final correction at every depth (`idcf`).
|
||||||
|
static constexpr bool incremental = Incremental;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct is_cmp_channel_tag : std::false_type {};
|
template <typename T> struct is_cmp_channel_tag : std::false_type {};
|
||||||
template <std::size_t Depth, std::size_t OutBits, bool Wild>
|
template <std::size_t Depth, std::size_t OutBits, bool Wild, std::size_t Block,
|
||||||
struct is_cmp_channel_tag<cmp_channel_tag<Depth, OutBits, Wild>>
|
bool Incremental>
|
||||||
|
struct is_cmp_channel_tag<cmp_channel_tag<Depth, OutBits, Wild, Block, Incremental>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline constexpr bool is_cmp_channel_tag_v =
|
inline constexpr bool is_cmp_channel_tag_v =
|
||||||
|
|
@ -107,6 +114,28 @@ struct is_placed<placed<N, O>> : std::true_type {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline constexpr bool is_placed_v = is_placed<std::decay_t<T>>::value;
|
inline constexpr bool is_placed_v = is_placed<std::decay_t<T>>::value;
|
||||||
|
|
||||||
|
/// Heavy-hitters incremental point function: one payload per prefix length.
|
||||||
|
/// `levels[i]` is the bit length of slot `i`.
|
||||||
|
template <typename LevelSeq, typename ...Betas>
|
||||||
|
struct idpf_pack;
|
||||||
|
|
||||||
|
template <std::size_t ...Levels, typename ...Betas>
|
||||||
|
struct idpf_pack<std::index_sequence<Levels...>, Betas...>
|
||||||
|
{
|
||||||
|
static constexpr bool is_idpf = true;
|
||||||
|
static constexpr std::size_t n = sizeof...(Levels);
|
||||||
|
static constexpr std::array<std::size_t, n == 0 ? 1 : n> levels{
|
||||||
|
Levels...};
|
||||||
|
std::tuple<Betas...> values;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct is_idpf : std::false_type {};
|
||||||
|
template <std::size_t ...Levels, typename ...Betas>
|
||||||
|
struct is_idpf<idpf_pack<std::index_sequence<Levels...>, Betas...>>
|
||||||
|
: std::true_type {};
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr bool is_idpf_v = is_idpf<std::decay_t<T>>::value;
|
||||||
|
|
||||||
template <typename NodeT, typename OutputT>
|
template <typename NodeT, typename OutputT>
|
||||||
inline constexpr std::size_t out_bits_v =
|
inline constexpr std::size_t out_bits_v =
|
||||||
utils::bitlength_of_output_v<concrete_type_t<OutputT>, NodeT>;
|
utils::bitlength_of_output_v<concrete_type_t<OutputT>, NodeT>;
|
||||||
|
|
@ -385,6 +414,8 @@ struct normalize_one
|
||||||
static constexpr std::size_t cmp_depth = 0;
|
static constexpr std::size_t cmp_depth = 0;
|
||||||
static constexpr std::size_t cmp_out_bits = 0;
|
static constexpr std::size_t cmp_out_bits = 0;
|
||||||
static constexpr bool cmp_wild = false;
|
static constexpr bool cmp_wild = false;
|
||||||
|
static constexpr std::size_t cmp_block = 0;
|
||||||
|
static constexpr bool cmp_idcf = false;
|
||||||
};
|
};
|
||||||
template <std::size_t BitLen, std::size_t N, typename T>
|
template <std::size_t BitLen, std::size_t N, typename T>
|
||||||
struct normalize_one<BitLen, placed<N, T>>
|
struct normalize_one<BitLen, placed<N, T>>
|
||||||
|
|
@ -393,14 +424,20 @@ struct normalize_one<BitLen, placed<N, T>>
|
||||||
static constexpr std::size_t cmp_depth = 0;
|
static constexpr std::size_t cmp_depth = 0;
|
||||||
static constexpr std::size_t cmp_out_bits = 0;
|
static constexpr std::size_t cmp_out_bits = 0;
|
||||||
static constexpr bool cmp_wild = false;
|
static constexpr bool cmp_wild = false;
|
||||||
|
static constexpr std::size_t cmp_block = 0;
|
||||||
|
static constexpr bool cmp_idcf = false;
|
||||||
};
|
};
|
||||||
template <std::size_t BitLen, std::size_t Depth, std::size_t OutBits, bool Wild>
|
template <std::size_t BitLen, std::size_t Depth, std::size_t OutBits, bool Wild,
|
||||||
struct normalize_one<BitLen, cmp_channel_tag<Depth, OutBits, Wild>>
|
std::size_t Block, bool Incremental>
|
||||||
|
struct normalize_one<BitLen,
|
||||||
|
cmp_channel_tag<Depth, OutBits, Wild, Block, Incremental>>
|
||||||
{
|
{
|
||||||
using placed_tuple = std::tuple<>;
|
using placed_tuple = std::tuple<>;
|
||||||
static constexpr std::size_t cmp_depth = Depth;
|
static constexpr std::size_t cmp_depth = Depth;
|
||||||
static constexpr std::size_t cmp_out_bits = OutBits;
|
static constexpr std::size_t cmp_out_bits = OutBits;
|
||||||
static constexpr bool cmp_wild = Wild;
|
static constexpr bool cmp_wild = Wild;
|
||||||
|
static constexpr std::size_t cmp_block = Block;
|
||||||
|
static constexpr bool cmp_idcf = Incremental;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t BitLen, typename ...Elems>
|
template <std::size_t BitLen, typename ...Elems>
|
||||||
|
|
@ -419,6 +456,10 @@ struct normalize_pack
|
||||||
// wildcard flag (false when there is no cmp channel).
|
// wildcard flag (false when there is no cmp channel).
|
||||||
static constexpr bool cmp_wild =
|
static constexpr bool cmp_wild =
|
||||||
(false || ... || normalize_one<BitLen, Elems>::cmp_wild);
|
(false || ... || normalize_one<BitLen, Elems>::cmp_wild);
|
||||||
|
static constexpr std::size_t cmp_block =
|
||||||
|
(std::size_t{0} + ... + normalize_one<BitLen, Elems>::cmp_block);
|
||||||
|
static constexpr bool cmp_idcf =
|
||||||
|
(false || ... || normalize_one<BitLen, Elems>::cmp_idcf);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t BitLen>
|
template <std::size_t BitLen>
|
||||||
|
|
@ -428,6 +469,8 @@ struct normalize_pack<BitLen>
|
||||||
static constexpr std::size_t cmp_depth = 0;
|
static constexpr std::size_t cmp_depth = 0;
|
||||||
static constexpr std::size_t cmp_out_bits = 0;
|
static constexpr std::size_t cmp_out_bits = 0;
|
||||||
static constexpr bool cmp_wild = false;
|
static constexpr bool cmp_wild = false;
|
||||||
|
static constexpr std::size_t cmp_block = 0;
|
||||||
|
static constexpr bool cmp_idcf = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// True iff the pack is "classic-shaped": every element is a bare output (no
|
/// True iff the pack is "classic-shaped": every element is a bare output (no
|
||||||
|
|
@ -439,6 +482,32 @@ inline constexpr bool is_classic_pack_v =
|
||||||
} // namespace incr
|
} // namespace incr
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Sparse heavy-hitters IDPF. Slot `i` is the point function on prefix
|
||||||
|
/// `Levels[i]`, evaluated with `out<i>`.
|
||||||
|
template <std::size_t ...Levels, typename ...Betas>
|
||||||
|
auto idpf_at(Betas ...betas)
|
||||||
|
{
|
||||||
|
static_assert(sizeof...(Levels) == sizeof...(Betas),
|
||||||
|
"idpf_at: one payload per prefix length");
|
||||||
|
return detail::incr::idpf_pack<std::index_sequence<Levels...>,
|
||||||
|
std::decay_t<Betas>...>{
|
||||||
|
std::tuple<std::decay_t<Betas>...>{std::move(betas)...}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t ...I, typename ...Betas>
|
||||||
|
auto idpf_from_seq(std::index_sequence<I...>, Betas ...betas)
|
||||||
|
{
|
||||||
|
return idpf_at<(I + 1)...>(std::move(betas)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consecutive prefixes of length 1, 2, …, `sizeof...(Betas)`.
|
||||||
|
template <typename ...Betas>
|
||||||
|
auto idpf(Betas ...betas)
|
||||||
|
{
|
||||||
|
return idpf_from_seq(std::make_index_sequence<sizeof...(Betas)>{},
|
||||||
|
std::move(betas)...);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dpf
|
} // namespace dpf
|
||||||
|
|
||||||
#endif // LIBDPF_INCLUDE_DPF_PLACEMENT_HPP__
|
#endif // LIBDPF_INCLUDE_DPF_PLACEMENT_HPP__
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "dpf/prg_aes.hpp"
|
#include "dpf/prg_aes.hpp"
|
||||||
|
#include "dpf/prg_chacha.hpp"
|
||||||
#include "dpf/prg_dummy.hpp"
|
#include "dpf/prg_dummy.hpp"
|
||||||
#include "dpf/prg_lowmc.hpp"
|
#include "dpf/prg_lowmc.hpp"
|
||||||
#include "dpf/secret_share.hpp"
|
#include "dpf/secret_share.hpp"
|
||||||
|
|
@ -63,23 +65,34 @@ auto expand_as_share(typename PRG::block_type seed,
|
||||||
|
|
||||||
template <typename AesKey>
|
template <typename AesKey>
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto aes<AesKey>::expand(block_type seed, psnip_uint32_t pos) noexcept
|
auto aes<AesKey>::expand(block_type seed, psnip_uint32_t pos) noexcept
|
||||||
{
|
{
|
||||||
return detail::expand_as_share<aes<AesKey>, T, Party>(seed, pos);
|
return detail::expand_as_share<aes<AesKey>, T, Party>(seed, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto dummy::expand(block_type seed, psnip_uint32_t pos) noexcept
|
auto dummy::expand(block_type seed, psnip_uint32_t pos) noexcept
|
||||||
{
|
{
|
||||||
return detail::expand_as_share<dummy, T, Party>(seed, pos);
|
return detail::expand_as_share<dummy, T, Party>(seed, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto lowmc128::expand(block_type seed, psnip_uint32_t pos) noexcept
|
auto lowmc128::expand(block_type seed, psnip_uint32_t pos) noexcept
|
||||||
{
|
{
|
||||||
return detail::expand_as_share<lowmc128, T, Party>(seed, pos);
|
return detail::expand_as_share<lowmc128, T, Party>(seed, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <unsigned Rounds>
|
||||||
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
auto chacha<Rounds>::expand(block_type seed, psnip_uint32_t pos) noexcept
|
||||||
|
{
|
||||||
|
return detail::expand_as_share<chacha<Rounds>, T, Party>(seed, pos);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename PRG>
|
template <typename PRG>
|
||||||
struct counter_wrapper final
|
struct counter_wrapper final
|
||||||
{
|
{
|
||||||
|
|
@ -101,17 +114,22 @@ struct counter_wrapper final
|
||||||
return PRG::eval01(seed);
|
return PRG::eval01(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
||||||
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
|
psnip_uint32_t count, psnip_uint32_t pos = 0)
|
||||||
{
|
{
|
||||||
|
if (count > 1 &&
|
||||||
|
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("prg lane index is out of range");
|
||||||
|
}
|
||||||
count_.fetch_add(count, std::memory_order::memory_order_relaxed);
|
count_.fetch_add(count, std::memory_order::memory_order_relaxed);
|
||||||
PRG::eval(seed, output, count, pos);
|
PRG::eval(seed, output, count, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2, 3)
|
||||||
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT left,
|
block_type * HEDLEY_RESTRICT left,
|
||||||
block_type * HEDLEY_RESTRICT right) noexcept
|
block_type * HEDLEY_RESTRICT right) noexcept
|
||||||
|
|
@ -122,6 +140,7 @@ struct counter_wrapper final
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -131,6 +150,7 @@ struct counter_wrapper final
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "hedley/hedley.h"
|
#include "hedley/hedley.h"
|
||||||
#include "simde/simde/x86/avx2.h"
|
#include "simde/simde/x86/avx2.h"
|
||||||
|
|
@ -103,15 +104,21 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// `eval` / `eval01` (`set_epi64x(0, pos)`). The first AddRoundKey
|
/// `eval` / `eval01` (`set_epi64x(0, pos)`). The first AddRoundKey
|
||||||
/// includes `rd_key[0]` so this matches the one-block `eval` for any
|
/// includes `rd_key[0]` so this matches the one-block `eval` for any
|
||||||
/// key, not only the all-zero key this PRG currently installs.
|
/// key, not only the all-zero key this PRG currently installs.
|
||||||
HEDLEY_NO_THROW
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
||||||
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
|
psnip_uint32_t count, psnip_uint32_t pos = 0)
|
||||||
{
|
{
|
||||||
if (HEDLEY_UNLIKELY(count == 0))
|
if (HEDLEY_UNLIKELY(count == 0))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// `pos + i` is a uint32 add. A span that passes UINT32_MAX must
|
||||||
|
// fail the same way buffered_prg does, rather than wrap to 0.
|
||||||
|
if (count > 1 &&
|
||||||
|
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("prg lane index is out of range");
|
||||||
|
}
|
||||||
|
|
||||||
require_block_aligned(output);
|
require_block_aligned(output);
|
||||||
block_type * HEDLEY_RESTRICT out =
|
block_type * HEDLEY_RESTRICT out =
|
||||||
|
|
@ -161,6 +168,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// `left[i] == eval(seeds[i], 0)`, `right[i] == eval(seeds[i], 1)`.
|
/// `left[i] == eval(seeds[i], 0)`, `right[i] == eval(seeds[i], 1)`.
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2, 3)
|
||||||
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT left,
|
block_type * HEDLEY_RESTRICT left,
|
||||||
block_type * HEDLEY_RESTRICT right) noexcept
|
block_type * HEDLEY_RESTRICT right) noexcept
|
||||||
|
|
@ -193,6 +201,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// Four independent `eval(seed, pos)` as one 4-block round-major AES.
|
/// Four independent `eval(seed, pos)` as one 4-block round-major AES.
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -223,6 +232,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// Eight independent `eval(seed, pos)` as one 8-block round-major AES.
|
/// Eight independent `eval(seed, pos)` as one 8-block round-major AES.
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -252,6 +262,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -261,6 +272,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
/// rounds 1..last and the MMO feed-forward `XOR seed[i]`.
|
/// rounds 1..last and the MMO feed-forward `XOR seed[i]`.
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void aes_mmo_rounds_x4(block_type * HEDLEY_RESTRICT blk,
|
static void aes_mmo_rounds_x4(block_type * HEDLEY_RESTRICT blk,
|
||||||
const block_type * HEDLEY_RESTRICT seed) noexcept
|
const block_type * HEDLEY_RESTRICT seed) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -283,6 +295,7 @@ HEDLEY_PRAGMA(GCC unroll(14))
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void aes_mmo_rounds_x8(block_type * HEDLEY_RESTRICT blk,
|
static void aes_mmo_rounds_x8(block_type * HEDLEY_RESTRICT blk,
|
||||||
const block_type * HEDLEY_RESTRICT seed) noexcept
|
const block_type * HEDLEY_RESTRICT seed) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
458
include/dpf/prg_chacha.hpp
Normal file
458
include/dpf/prg_chacha.hpp
Normal file
|
|
@ -0,0 +1,458 @@
|
||||||
|
/// @file dpf/prg_chacha.hpp
|
||||||
|
/// @brief ChaCha stream PRG. Same 128-bit block interface as `aes128`.
|
||||||
|
/// @details `eval(seed, pos)` is the `pos`-th 16-byte chunk of ChaCha
|
||||||
|
/// keystream (RFC 8439). The 256-bit key is the 128-bit seed
|
||||||
|
/// followed by the fixed domain separator `"dpf-chacha-prg\0\0"`.
|
||||||
|
/// The nonce is zero. The ChaCha block counter is `pos / 4`, and
|
||||||
|
/// the chunk inside that block is `pos % 4`.
|
||||||
|
///
|
||||||
|
/// `chacha20` is the RFC round count. `chacha12` and `chacha8` are
|
||||||
|
/// the same construction with fewer rounds. `chacha<R>` accepts any
|
||||||
|
/// positive even round count.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
|
/// see [LICENSE.md](@ref license) for details.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
#include "simde/simde/x86/avx2.h"
|
||||||
|
#include "portable-snippets/exact-int/exact-int.h"
|
||||||
|
|
||||||
|
namespace dpf
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace prg
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace chacha_detail
|
||||||
|
{
|
||||||
|
|
||||||
|
inline constexpr std::uint32_t zero_nonce[3] = {0, 0, 0};
|
||||||
|
|
||||||
|
/// ASCII `"dpf-chacha-prg"` plus two zero bytes. Public second half of the key.
|
||||||
|
inline constexpr std::uint8_t domain[16] = {
|
||||||
|
'd', 'p', 'f', '-', 'c', 'h', 'a', 'c',
|
||||||
|
'h', 'a', '-', 'p', 'r', 'g', 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NON_NULL(1)
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr std::uint32_t load_le32(const std::uint8_t * p) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(p[0])
|
||||||
|
| (static_cast<std::uint32_t>(p[1]) << 8)
|
||||||
|
| (static_cast<std::uint32_t>(p[2]) << 16)
|
||||||
|
| (static_cast<std::uint32_t>(p[3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NON_NULL(1)
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
void store_le32(std::uint8_t * p, std::uint32_t w) noexcept
|
||||||
|
{
|
||||||
|
p[0] = static_cast<std::uint8_t>(w);
|
||||||
|
p[1] = static_cast<std::uint8_t>(w >> 8);
|
||||||
|
p[2] = static_cast<std::uint8_t>(w >> 16);
|
||||||
|
p[3] = static_cast<std::uint8_t>(w >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NON_NULL(1)
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
simde__m128i load_block(const std::uint8_t * p) noexcept
|
||||||
|
{
|
||||||
|
simde__m128i out;
|
||||||
|
std::memcpy(&out, p, sizeof(out));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 128-bit seed in the low half, `domain` in the high half, both little-endian.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
void seed_key(simde__m128i seed, std::uint32_t key[8]) noexcept
|
||||||
|
{
|
||||||
|
std::uint8_t raw[16];
|
||||||
|
std::memcpy(raw, &seed, sizeof(raw));
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
key[i] = load_le32(raw + 4 * i);
|
||||||
|
key[4 + i] = load_le32(domain + 4 * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr std::uint32_t rotl(std::uint32_t x) noexcept
|
||||||
|
{
|
||||||
|
static_assert(N > 0 && N < 32, "ChaCha rotation is between 1 and 31");
|
||||||
|
return (x << N) | (x >> (32 - N));
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
void quarter(std::uint32_t & a, std::uint32_t & b,
|
||||||
|
std::uint32_t & c, std::uint32_t & d) noexcept
|
||||||
|
{
|
||||||
|
a += b; d ^= a; d = rotl<16>(d);
|
||||||
|
c += d; b ^= c; b = rotl<12>(b);
|
||||||
|
a += b; d ^= a; d = rotl<8>(d);
|
||||||
|
c += d; b ^= c; b = rotl<7>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
simde__m128i rotl_epi32(simde__m128i v) noexcept
|
||||||
|
{
|
||||||
|
static_assert(N > 0 && N < 32, "ChaCha rotation is between 1 and 31");
|
||||||
|
return simde_mm_or_si128(simde_mm_slli_epi32(v, N),
|
||||||
|
simde_mm_srli_epi32(v, 32 - N));
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
void quarter(simde__m128i x[], int a, int b, int c, int d) noexcept
|
||||||
|
{
|
||||||
|
x[a] = simde_mm_add_epi32(x[a], x[b]);
|
||||||
|
x[d] = rotl_epi32<16>(simde_mm_xor_si128(x[d], x[a]));
|
||||||
|
x[c] = simde_mm_add_epi32(x[c], x[d]);
|
||||||
|
x[b] = rotl_epi32<12>(simde_mm_xor_si128(x[b], x[c]));
|
||||||
|
x[a] = simde_mm_add_epi32(x[a], x[b]);
|
||||||
|
x[d] = rotl_epi32<8>(simde_mm_xor_si128(x[d], x[a]));
|
||||||
|
x[c] = simde_mm_add_epi32(x[c], x[d]);
|
||||||
|
x[b] = rotl_epi32<7>(simde_mm_xor_si128(x[b], x[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
std::uint32_t epi32_lane(simde__m128i v, int lane) noexcept
|
||||||
|
{
|
||||||
|
// Shuffle control is an immediate, so each lane is its own case.
|
||||||
|
switch (lane)
|
||||||
|
{
|
||||||
|
case 1: v = simde_mm_shuffle_epi32(v, 0x01); break;
|
||||||
|
case 2: v = simde_mm_shuffle_epi32(v, 0x02); break;
|
||||||
|
case 3: v = simde_mm_shuffle_epi32(v, 0x03); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return static_cast<std::uint32_t>(simde_mm_cvtsi128_si32(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One ChaCha block. `key` is 8 little-endian words. `nonce` is 3 words.
|
||||||
|
template <unsigned Rounds>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
void block(const std::uint32_t key[8], std::uint32_t counter,
|
||||||
|
const std::uint32_t nonce[3], std::uint8_t out[64]) noexcept
|
||||||
|
{
|
||||||
|
static_assert(Rounds >= 2 && Rounds % 2 == 0,
|
||||||
|
"ChaCha rounds must be a positive even number");
|
||||||
|
|
||||||
|
std::uint32_t s[16] = {
|
||||||
|
0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u,
|
||||||
|
key[0], key[1], key[2], key[3],
|
||||||
|
key[4], key[5], key[6], key[7],
|
||||||
|
counter, nonce[0], nonce[1], nonce[2]
|
||||||
|
};
|
||||||
|
std::uint32_t orig[16];
|
||||||
|
std::memcpy(orig, s, sizeof(orig));
|
||||||
|
|
||||||
|
HEDLEY_PRAGMA(GCC unroll 16)
|
||||||
|
for (unsigned r = 0; r < Rounds; r += 2)
|
||||||
|
{
|
||||||
|
quarter(s[0], s[4], s[8], s[12]);
|
||||||
|
quarter(s[1], s[5], s[9], s[13]);
|
||||||
|
quarter(s[2], s[6], s[10], s[14]);
|
||||||
|
quarter(s[3], s[7], s[11], s[15]);
|
||||||
|
quarter(s[0], s[5], s[10], s[15]);
|
||||||
|
quarter(s[1], s[6], s[11], s[12]);
|
||||||
|
quarter(s[2], s[7], s[8], s[13]);
|
||||||
|
quarter(s[3], s[4], s[9], s[14]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
store_le32(out + 4 * i, s[i] + orig[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Four independent ChaCha blocks. Lane `i` uses `key[i]` and `counter[i]`.
|
||||||
|
/// Nonce is zero. Each `out[i]` receives 64 bytes.
|
||||||
|
template <unsigned Rounds>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
void block4(const std::uint32_t key[][8], const std::uint32_t counter[4],
|
||||||
|
std::uint8_t out[][64]) noexcept
|
||||||
|
{
|
||||||
|
static_assert(Rounds >= 2 && Rounds % 2 == 0,
|
||||||
|
"ChaCha rounds must be a positive even number");
|
||||||
|
|
||||||
|
simde__m128i x[16];
|
||||||
|
x[0] = simde_mm_set1_epi32(static_cast<int>(0x61707865u));
|
||||||
|
x[1] = simde_mm_set1_epi32(static_cast<int>(0x3320646eu));
|
||||||
|
x[2] = simde_mm_set1_epi32(static_cast<int>(0x79622d32u));
|
||||||
|
x[3] = simde_mm_set1_epi32(static_cast<int>(0x6b206574u));
|
||||||
|
for (int w = 0; w < 8; ++w)
|
||||||
|
{
|
||||||
|
x[4 + w] = simde_mm_set_epi32(
|
||||||
|
static_cast<int>(key[3][w]),
|
||||||
|
static_cast<int>(key[2][w]),
|
||||||
|
static_cast<int>(key[1][w]),
|
||||||
|
static_cast<int>(key[0][w]));
|
||||||
|
}
|
||||||
|
x[12] = simde_mm_set_epi32(
|
||||||
|
static_cast<int>(counter[3]),
|
||||||
|
static_cast<int>(counter[2]),
|
||||||
|
static_cast<int>(counter[1]),
|
||||||
|
static_cast<int>(counter[0]));
|
||||||
|
x[13] = simde_mm_setzero_si128();
|
||||||
|
x[14] = simde_mm_setzero_si128();
|
||||||
|
x[15] = simde_mm_setzero_si128();
|
||||||
|
|
||||||
|
simde__m128i orig[16];
|
||||||
|
for (int i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
orig[i] = x[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_PRAGMA(GCC unroll 16)
|
||||||
|
for (unsigned r = 0; r < Rounds; r += 2)
|
||||||
|
{
|
||||||
|
quarter(x, 0, 4, 8, 12);
|
||||||
|
quarter(x, 1, 5, 9, 13);
|
||||||
|
quarter(x, 2, 6, 10, 14);
|
||||||
|
quarter(x, 3, 7, 11, 15);
|
||||||
|
quarter(x, 0, 5, 10, 15);
|
||||||
|
quarter(x, 1, 6, 11, 12);
|
||||||
|
quarter(x, 2, 7, 8, 13);
|
||||||
|
quarter(x, 3, 4, 9, 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
for (int w = 0; w < 16; ++w)
|
||||||
|
{
|
||||||
|
std::uint32_t sum = epi32_lane(x[w], lane) + epi32_lane(orig[w], lane);
|
||||||
|
store_le32(out[lane] + 4 * w, sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chacha_detail
|
||||||
|
|
||||||
|
/// ChaCha stream PRG with `Rounds` rounds (20 is RFC 8439).
|
||||||
|
template <unsigned Rounds = 20>
|
||||||
|
struct chacha final
|
||||||
|
{
|
||||||
|
static_assert(Rounds >= 2 && Rounds % 2 == 0,
|
||||||
|
"ChaCha rounds must be a positive even number");
|
||||||
|
|
||||||
|
using block_type = simde__m128i;
|
||||||
|
static constexpr unsigned rounds = Rounds;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
static block_type eval(block_type seed, psnip_uint32_t pos) noexcept
|
||||||
|
{
|
||||||
|
std::uint32_t key[8];
|
||||||
|
chacha_detail::seed_key(seed, key);
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
chacha_detail::block<Rounds>(key, pos >> 2,
|
||||||
|
chacha_detail::zero_nonce, buf);
|
||||||
|
return chacha_detail::load_block(buf + 16 * (pos & 3u));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Positions 0 and 1, one ChaCha block (the first 32 keystream bytes).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
static auto eval01(block_type seed) noexcept
|
||||||
|
{
|
||||||
|
std::uint32_t key[8];
|
||||||
|
chacha_detail::seed_key(seed, key);
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
chacha_detail::block<Rounds>(key, 0, chacha_detail::zero_nonce, buf);
|
||||||
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
||||||
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||||
|
return std::array<block_type, 2>{
|
||||||
|
chacha_detail::load_block(buf),
|
||||||
|
chacha_detail::load_block(buf + 16)
|
||||||
|
};
|
||||||
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
||||||
|
psnip_uint32_t count, psnip_uint32_t pos = 0)
|
||||||
|
{
|
||||||
|
if (HEDLEY_UNLIKELY(count == 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count > 1 &&
|
||||||
|
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("prg lane index is out of range");
|
||||||
|
}
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
output[0] = eval(seed, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count == 2 && pos == 0)
|
||||||
|
{
|
||||||
|
auto kids = eval01(seed);
|
||||||
|
output[0] = kids[0];
|
||||||
|
output[1] = kids[1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t key[8];
|
||||||
|
chacha_detail::seed_key(seed, key);
|
||||||
|
psnip_uint32_t i = 0;
|
||||||
|
|
||||||
|
// `pos` may begin mid-block. Those chunks share one ChaCha block.
|
||||||
|
if ((pos & 3u) != 0u)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
chacha_detail::block<Rounds>(key, pos >> 2,
|
||||||
|
chacha_detail::zero_nonce, buf);
|
||||||
|
while (i < count && ((pos + i) & 3u) != 0u)
|
||||||
|
{
|
||||||
|
output[i] = chacha_detail::load_block(
|
||||||
|
buf + 16 * ((pos + i) & 3u));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Four consecutive counters cover 16 output blocks.
|
||||||
|
while (i + 16u <= count)
|
||||||
|
{
|
||||||
|
std::uint32_t base = (pos + i) >> 2;
|
||||||
|
std::uint32_t keys[4][8];
|
||||||
|
std::uint32_t counters[4];
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
std::memcpy(keys[lane], key, sizeof(key));
|
||||||
|
counters[lane] = base + static_cast<std::uint32_t>(lane);
|
||||||
|
}
|
||||||
|
std::uint8_t buf[4][64];
|
||||||
|
chacha_detail::block4<Rounds>(keys, counters, buf);
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
for (int chunk = 0; chunk < 4; ++chunk)
|
||||||
|
{
|
||||||
|
output[i++] = chacha_detail::load_block(
|
||||||
|
buf[lane] + 16 * chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i + 4u <= count)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
chacha_detail::block<Rounds>(key, (pos + i) >> 2,
|
||||||
|
chacha_detail::zero_nonce, buf);
|
||||||
|
for (int chunk = 0; chunk < 4; ++chunk)
|
||||||
|
{
|
||||||
|
output[i++] = chacha_detail::load_block(buf + 16 * chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < count)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
chacha_detail::block<Rounds>(key, (pos + i) >> 2,
|
||||||
|
chacha_detail::zero_nonce, buf);
|
||||||
|
unsigned chunk = 0;
|
||||||
|
while (i < count)
|
||||||
|
{
|
||||||
|
output[i++] = chacha_detail::load_block(buf + 16 * chunk);
|
||||||
|
++chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2, 3)
|
||||||
|
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
|
block_type * HEDLEY_RESTRICT left,
|
||||||
|
block_type * HEDLEY_RESTRICT right) noexcept
|
||||||
|
{
|
||||||
|
std::uint32_t keys[4][8];
|
||||||
|
std::uint32_t counters[4] = {0, 0, 0, 0};
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
chacha_detail::seed_key(seeds[lane], keys[lane]);
|
||||||
|
}
|
||||||
|
std::uint8_t buf[4][64];
|
||||||
|
chacha_detail::block4<Rounds>(keys, counters, buf);
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
left[lane] = chacha_detail::load_block(buf[lane]);
|
||||||
|
right[lane] = chacha_detail::load_block(buf[lane] + 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
|
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
|
{
|
||||||
|
std::uint32_t keys[4][8];
|
||||||
|
std::uint32_t ctr = pos >> 2;
|
||||||
|
std::uint32_t counters[4] = {ctr, ctr, ctr, ctr};
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
chacha_detail::seed_key(seeds[lane], keys[lane]);
|
||||||
|
}
|
||||||
|
std::uint8_t buf[4][64];
|
||||||
|
chacha_detail::block4<Rounds>(keys, counters, buf);
|
||||||
|
unsigned chunk = pos & 3u;
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
output[lane] = chacha_detail::load_block(buf[lane] + 16 * chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
|
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
|
{
|
||||||
|
eval_x4(seeds, output, pos);
|
||||||
|
eval_x4(seeds + 4, output + 4, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
||||||
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
||||||
|
}; // struct chacha
|
||||||
|
|
||||||
|
/// RFC 8439 ChaCha20.
|
||||||
|
using chacha20 = chacha<20>;
|
||||||
|
|
||||||
|
/// ChaCha12. Same keying as `chacha20`, 12 rounds.
|
||||||
|
using chacha12 = chacha<12>;
|
||||||
|
|
||||||
|
/// ChaCha8. Same keying as `chacha20`, 8 rounds.
|
||||||
|
using chacha8 = chacha<8>;
|
||||||
|
|
||||||
|
} // namespace prg
|
||||||
|
|
||||||
|
} // namespace dpf
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
/// @file dpf/prg_dummy.hpp
|
/// @file dpf/prg_dummy.hpp
|
||||||
/// @brief
|
/// @brief Identity PRG. `eval` returns the seed and ignores the lane.
|
||||||
/// @details
|
/// @details Used where a PRG-shaped type is required and the block must stay
|
||||||
|
/// equal to the seed. The batched entry points write that seed into
|
||||||
|
/// every output lane.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
|
|
@ -57,6 +59,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2, 3)
|
||||||
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT left,
|
block_type * HEDLEY_RESTRICT left,
|
||||||
block_type * HEDLEY_RESTRICT right) noexcept
|
block_type * HEDLEY_RESTRICT right) noexcept
|
||||||
|
|
@ -69,6 +72,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -77,6 +81,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -85,6 +90,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
||||||
}; // struct dummy
|
}; // struct dummy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "hedley/hedley.h"
|
#include "hedley/hedley.h"
|
||||||
#include "simde/simde/x86/avx2.h"
|
#include "simde/simde/x86/avx2.h"
|
||||||
|
|
@ -49,11 +50,15 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||||
HEDLEY_PRAGMA(GCC diagnostic pop)
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
|
||||||
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
|
psnip_uint32_t count, psnip_uint32_t pos = 0)
|
||||||
{
|
{
|
||||||
|
if (count > 1 &&
|
||||||
|
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("prg lane index is out of range");
|
||||||
|
}
|
||||||
for (psnip_uint32_t i = 0; i < count; ++i)
|
for (psnip_uint32_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
output[i] = eval(seed, pos + i);
|
output[i] = eval(seed, pos + i);
|
||||||
|
|
@ -62,6 +67,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2, 3)
|
||||||
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT left,
|
block_type * HEDLEY_RESTRICT left,
|
||||||
block_type * HEDLEY_RESTRICT right) noexcept
|
block_type * HEDLEY_RESTRICT right) noexcept
|
||||||
|
|
@ -76,6 +82,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -87,6 +94,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NON_NULL(1, 2)
|
||||||
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
|
||||||
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -98,9 +106,11 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
|
|
||||||
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
|
||||||
template <typename T, std::size_t Party>
|
template <typename T, std::size_t Party>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static lowmc::block to_block(block_type x) noexcept
|
static lowmc::block to_block(block_type x) noexcept
|
||||||
{
|
{
|
||||||
std::uint64_t lane[2];
|
std::uint64_t lane[2];
|
||||||
|
|
@ -114,6 +124,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static block_type from_block(const lowmc::block & b) noexcept
|
static block_type from_block(const lowmc::block & b) noexcept
|
||||||
{
|
{
|
||||||
std::uint64_t lane[2] = {0, 0};
|
std::uint64_t lane[2] = {0, 0};
|
||||||
|
|
@ -127,6 +138,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static block_type permute(block_type x) noexcept
|
static block_type permute(block_type x) noexcept
|
||||||
{
|
{
|
||||||
static lowmc::LowMC cipher;
|
static lowmc::LowMC cipher;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ inline thread_local void (*uniform_bytes_hook)(void *, std::size_t) = nullptr;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
bool fill_from_hook(T & buf) noexcept
|
bool fill_from_hook(T & buf) noexcept
|
||||||
{
|
{
|
||||||
if (uniform_bytes_hook == nullptr)
|
if (uniform_bytes_hook == nullptr)
|
||||||
|
|
@ -52,6 +53,7 @@ bool fill_from_hook(T & buf) noexcept
|
||||||
/// `bool` and `enum : bool` (including `dpf::bit`) have only two valid
|
/// `bool` and `enum : bool` (including `dpf::bit`) have only two valid
|
||||||
/// representations. Filling them with a raw entropy byte is undefined.
|
/// representations. Filling them with a raw entropy byte is undefined.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool is_boolean_representation() noexcept
|
constexpr bool is_boolean_representation() noexcept
|
||||||
{
|
{
|
||||||
using U = std::remove_cv_t<T>;
|
using U = std::remove_cv_t<T>;
|
||||||
|
|
@ -94,7 +96,8 @@ struct entropy_source
|
||||||
entropy_source(entropy_source &&) = delete;
|
entropy_source(entropy_source &&) = delete;
|
||||||
entropy_source & operator=(entropy_source &&) = delete;
|
entropy_source & operator=(entropy_source &&) = delete;
|
||||||
|
|
||||||
~entropy_source()
|
HEDLEY_NO_THROW
|
||||||
|
~entropy_source() noexcept
|
||||||
{
|
{
|
||||||
if (fp != nullptr)
|
if (fp != nullptr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/// @file dpf/rotated_iterable.hpp
|
||||||
|
/// @brief Retired container rotation view. The live type is `rotation_iterable`.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
// /// @file dpf/rotated_iterable.hpp
|
// /// @file dpf/rotated_iterable.hpp
|
||||||
// /// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
// /// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
// /// @brief defines `dpf::rotated_iterable` and associated helpers
|
// /// @brief defines `dpf::rotated_iterable` and associated helpers
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
/// @file dpf/rotation_iterable.hpp
|
||||||
|
/// @brief A rotated view of an iterator range.
|
||||||
|
/// @details `begin()` starts `distance` elements into the wrapped range and
|
||||||
|
/// wraps back to the original begin. Indexing is O(1) when the
|
||||||
|
/// wrapped iterator is random-access.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
|
#define LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
|
||||||
|
|
||||||
|
|
@ -30,20 +38,14 @@ struct rotation_iterable
|
||||||
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
|
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
|
||||||
difference_type distance)
|
difference_type distance)
|
||||||
: size_{std::distance(begin, end)},
|
: size_{std::distance(begin, end)},
|
||||||
distance_{
|
distance_{normalize_distance(distance, size_)},
|
||||||
[this, &distance]()
|
begin_{size_ == 0 ? begin : std::next(begin, distance_)},
|
||||||
{
|
|
||||||
distance %= this->size_;
|
|
||||||
if (distance < 0)
|
|
||||||
{
|
|
||||||
distance += this->size_;
|
|
||||||
}
|
|
||||||
return distance;
|
|
||||||
}()},
|
|
||||||
begin_{std::next(begin, static_cast<difference_type>(distance_))},
|
|
||||||
wrap_to_{begin},
|
wrap_to_{begin},
|
||||||
wrap_after_{std::next(end, -difference_type(distance_ > 0))},
|
wrap_after_{size_ == 0 || distance_ == 0
|
||||||
end_after_{std::next(wrap_to_, static_cast<difference_type>(distance_-1))},
|
? end : std::next(end, difference_type{-1})},
|
||||||
|
end_after_{size_ == 0 ? begin
|
||||||
|
: (distance_ == 0 ? std::next(end, difference_type{-1})
|
||||||
|
: std::next(begin, distance_ - 1))},
|
||||||
end_{end}
|
end_{end}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
@ -52,11 +54,11 @@ struct rotation_iterable
|
||||||
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
|
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
|
||||||
wrapped_iterator middle)
|
wrapped_iterator middle)
|
||||||
: size_{std::distance(begin, end)},
|
: size_{std::distance(begin, end)},
|
||||||
distance_{std::distance(begin, middle)},
|
distance_{size_ == 0 ? difference_type{0} : std::distance(begin, middle)},
|
||||||
begin_{middle},
|
begin_{middle},
|
||||||
wrap_to_{begin},
|
wrap_to_{begin},
|
||||||
wrap_after_{std::next(end, difference_type(-1))},
|
wrap_after_{size_ == 0 ? end : std::next(end, difference_type{-1})},
|
||||||
end_after_{std::next(middle, difference_type(-1))},
|
end_after_{size_ == 0 ? begin : std::next(middle, difference_type{-1})},
|
||||||
end_{end}
|
end_{end}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
@ -136,6 +138,19 @@ struct rotation_iterable
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
static constexpr difference_type normalize_distance(difference_type distance,
|
||||||
|
difference_type size) noexcept
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return difference_type{0};
|
||||||
|
distance %= size;
|
||||||
|
if (distance < 0)
|
||||||
|
distance += size;
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
difference_type size_;
|
difference_type size_;
|
||||||
difference_type distance_;
|
difference_type distance_;
|
||||||
wrapped_iterator begin_;
|
wrapped_iterator begin_;
|
||||||
|
|
@ -163,6 +178,7 @@ struct rotation_iterator_base
|
||||||
std::add_const_t<reference>>;
|
std::add_const_t<reference>>;
|
||||||
using pointer = typename std::iterator_traits<WrappedIterator>::pointer;
|
using pointer = typename std::iterator_traits<WrappedIterator>::pointer;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
rotation_iterator_base(const rotation_iterable<wrapped_iterator> & iterable_,
|
rotation_iterator_base(const rotation_iterable<wrapped_iterator> & iterable_,
|
||||||
wrapped_iterator iterator) noexcept
|
wrapped_iterator iterator) noexcept
|
||||||
: iterable{iterable_}, it{iterator} { }
|
: iterable{iterable_}, it{iterator} { }
|
||||||
|
|
@ -256,6 +272,7 @@ struct rotation_iterator final
|
||||||
using wrapped_iterator = WrappedIterator;
|
using wrapped_iterator = WrappedIterator;
|
||||||
using reference = typename base::reference;
|
using reference = typename base::reference;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
rotation_iterator(const rotation_iterable<WrappedIterator> & iterable,
|
rotation_iterator(const rotation_iterable<WrappedIterator> & iterable,
|
||||||
wrapped_iterator iterator) noexcept
|
wrapped_iterator iterator) noexcept
|
||||||
: base{iterable, iterator} {}
|
: base{iterable, iterator} {}
|
||||||
|
|
@ -277,6 +294,7 @@ struct rotation_const_iterator final
|
||||||
using wrapped_iterator = WrappedIterator;
|
using wrapped_iterator = WrappedIterator;
|
||||||
using const_reference = typename base::const_reference;
|
using const_reference = typename base::const_reference;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
rotation_const_iterator(const rotation_iterable<WrappedIterator> & iterable,
|
rotation_const_iterator(const rotation_iterable<WrappedIterator> & iterable,
|
||||||
wrapped_iterator iterator) noexcept
|
wrapped_iterator iterator) noexcept
|
||||||
: base{iterable, iterator} {}
|
: base{iterable, iterator} {}
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,19 @@ namespace detail
|
||||||
template <sharing Scheme, std::size_t Party, typename T>
|
template <sharing Scheme, std::size_t Party, typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T party_coeff_times(const T & v) noexcept
|
constexpr T party_coeff_times(const T & v) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (Scheme == sharing::additive || Party == 0)
|
if constexpr (Scheme == sharing::additive || Party == 0)
|
||||||
return v;
|
return v;
|
||||||
|
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
|
||||||
|
{
|
||||||
|
// Signed negation of the minimum is undefined. The two's-complement
|
||||||
|
// negation is well-defined on the unsigned width.
|
||||||
|
using unsigned_type = std::make_unsigned_t<T>;
|
||||||
|
return static_cast<T>(static_cast<unsigned_type>(0)
|
||||||
|
- static_cast<unsigned_type>(v));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return static_cast<T>(-v);
|
return static_cast<T>(-v);
|
||||||
}
|
}
|
||||||
|
|
@ -122,13 +131,18 @@ struct secret_share
|
||||||
T value{};
|
T value{};
|
||||||
|
|
||||||
secret_share() = default;
|
secret_share() = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
secret_share(const secret_share &) noexcept = default;
|
secret_share(const secret_share &) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
secret_share(secret_share &&) noexcept = default;
|
secret_share(secret_share &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
secret_share & operator=(const secret_share &) noexcept = default;
|
secret_share & operator=(const secret_share &) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
secret_share & operator=(secret_share &&) noexcept = default;
|
secret_share & operator=(secret_share &&) noexcept = default;
|
||||||
~secret_share() = default;
|
~secret_share() = default;
|
||||||
|
|
||||||
/// Bit-preserving construction. Does not apply a party coefficient.
|
/// Bit-preserving construction. Does not apply a party coefficient.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
static constexpr secret_share from_raw(T v) noexcept
|
static constexpr secret_share from_raw(T v) noexcept
|
||||||
|
|
@ -138,15 +152,18 @@ struct secret_share
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
constexpr const T & raw() const noexcept { return value; }
|
constexpr const T & raw() const noexcept { return value; }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
constexpr T & raw() noexcept { return value; }
|
constexpr T & raw() noexcept { return value; }
|
||||||
|
|
||||||
/// Secret-preserving conversion to an additive share of the same party.
|
/// Secret-preserving conversion to an additive share of the same party.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
constexpr additive_share<T, Party> as_additive() const noexcept
|
constexpr additive_share<T, Party> as_additive() const noexcept
|
||||||
|
|
@ -159,6 +176,7 @@ struct secret_share
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Secret-preserving conversion to a subtractive share of the same party.
|
/// Secret-preserving conversion to a subtractive share of the same party.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
constexpr subtractive_share<T, Party> as_subtractive() const noexcept
|
constexpr subtractive_share<T, Party> as_subtractive() const noexcept
|
||||||
|
|
@ -174,18 +192,28 @@ struct secret_share
|
||||||
template <sharing NewScheme, std::size_t NewParty = Party>
|
template <sharing NewScheme, std::size_t NewParty = Party>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, NewParty, NewScheme> retag() const noexcept
|
constexpr secret_share<T, NewParty, NewScheme> retag() const noexcept
|
||||||
{
|
{
|
||||||
return secret_share<T, NewParty, NewScheme>::from_raw(value);
|
return secret_share<T, NewParty, NewScheme>::from_raw(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
constexpr secret_share operator-() const noexcept
|
constexpr secret_share operator-() const noexcept
|
||||||
{
|
{
|
||||||
|
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
|
||||||
|
{
|
||||||
|
using unsigned_type = std::make_unsigned_t<T>;
|
||||||
|
return from_raw(static_cast<T>(static_cast<unsigned_type>(0)
|
||||||
|
- static_cast<unsigned_type>(value)));
|
||||||
|
}
|
||||||
|
else
|
||||||
return from_raw(static_cast<T>(-value));
|
return from_raw(static_cast<T>(-value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr secret_share & operator+=(const secret_share & rhs) noexcept
|
constexpr secret_share & operator+=(const secret_share & rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -193,6 +221,7 @@ struct secret_share
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr secret_share & operator-=(const secret_share & rhs) noexcept
|
constexpr secret_share & operator-=(const secret_share & rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -203,6 +232,7 @@ struct secret_share
|
||||||
template <typename Scalar,
|
template <typename Scalar,
|
||||||
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share & operator*=(const Scalar & c) noexcept
|
constexpr secret_share & operator*=(const Scalar & c) noexcept
|
||||||
{
|
{
|
||||||
value = static_cast<T>(value * static_cast<T>(c));
|
value = static_cast<T>(value * static_cast<T>(c));
|
||||||
|
|
@ -214,6 +244,7 @@ struct secret_share
|
||||||
std::enable_if_t<!is_secret_share_v<Plain>
|
std::enable_if_t<!is_secret_share_v<Plain>
|
||||||
&& std::is_convertible_v<Plain, T>, int> = 0>
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share & operator+=(const Plain & c) noexcept
|
constexpr secret_share & operator+=(const Plain & c) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (Party == 0)
|
if constexpr (Party == 0)
|
||||||
|
|
@ -225,6 +256,7 @@ struct secret_share
|
||||||
std::enable_if_t<!is_secret_share_v<Plain>
|
std::enable_if_t<!is_secret_share_v<Plain>
|
||||||
&& std::is_convertible_v<Plain, T>, int> = 0>
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share & operator-=(const Plain & c) noexcept
|
constexpr secret_share & operator-=(const Plain & c) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (Party == 0)
|
if constexpr (Party == 0)
|
||||||
|
|
@ -240,6 +272,7 @@ struct secret_share
|
||||||
template <typename T, std::size_t Party, sharing Scheme>
|
template <typename T, std::size_t Party, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator+(
|
constexpr secret_share<T, Party, Scheme> operator+(
|
||||||
secret_share<T, Party, Scheme> lhs,
|
secret_share<T, Party, Scheme> lhs,
|
||||||
const secret_share<T, Party, Scheme> & rhs) noexcept
|
const secret_share<T, Party, Scheme> & rhs) noexcept
|
||||||
|
|
@ -251,6 +284,7 @@ constexpr secret_share<T, Party, Scheme> operator+(
|
||||||
template <typename T, std::size_t Party, sharing Scheme>
|
template <typename T, std::size_t Party, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator-(
|
constexpr secret_share<T, Party, Scheme> operator-(
|
||||||
secret_share<T, Party, Scheme> lhs,
|
secret_share<T, Party, Scheme> lhs,
|
||||||
const secret_share<T, Party, Scheme> & rhs) noexcept
|
const secret_share<T, Party, Scheme> & rhs) noexcept
|
||||||
|
|
@ -263,6 +297,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
|
||||||
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator*(
|
constexpr secret_share<T, Party, Scheme> operator*(
|
||||||
secret_share<T, Party, Scheme> lhs, const Scalar & c) noexcept
|
secret_share<T, Party, Scheme> lhs, const Scalar & c) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -274,6 +309,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
|
||||||
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator*(
|
constexpr secret_share<T, Party, Scheme> operator*(
|
||||||
const Scalar & c, secret_share<T, Party, Scheme> rhs) noexcept
|
const Scalar & c, secret_share<T, Party, Scheme> rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -290,6 +326,7 @@ template <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
|
||||||
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
|
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, LhsScheme> operator+(
|
constexpr secret_share<T, Party, LhsScheme> operator+(
|
||||||
const secret_share<T, Party, LhsScheme> & lhs,
|
const secret_share<T, Party, LhsScheme> & lhs,
|
||||||
const secret_share<T, Party, RhsScheme> & rhs) noexcept
|
const secret_share<T, Party, RhsScheme> & rhs) noexcept
|
||||||
|
|
@ -306,6 +343,7 @@ template <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
|
||||||
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
|
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, LhsScheme> operator-(
|
constexpr secret_share<T, Party, LhsScheme> operator-(
|
||||||
const secret_share<T, Party, LhsScheme> & lhs,
|
const secret_share<T, Party, LhsScheme> & lhs,
|
||||||
const secret_share<T, Party, RhsScheme> & rhs) noexcept
|
const secret_share<T, Party, RhsScheme> & rhs) noexcept
|
||||||
|
|
@ -327,6 +365,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
|
||||||
&& std::is_convertible_v<Plain, T>, int> = 0>
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator+(
|
constexpr secret_share<T, Party, Scheme> operator+(
|
||||||
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
|
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -339,6 +378,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
|
||||||
&& std::is_convertible_v<Plain, T>, int> = 0>
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator+(
|
constexpr secret_share<T, Party, Scheme> operator+(
|
||||||
const Plain & c, secret_share<T, Party, Scheme> rhs) noexcept
|
const Plain & c, secret_share<T, Party, Scheme> rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -351,6 +391,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
|
||||||
&& std::is_convertible_v<Plain, T>, int> = 0>
|
&& std::is_convertible_v<Plain, T>, int> = 0>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr secret_share<T, Party, Scheme> operator-(
|
constexpr secret_share<T, Party, Scheme> operator-(
|
||||||
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
|
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -365,6 +406,7 @@ constexpr secret_share<T, Party, Scheme> operator-(
|
||||||
template <typename T, std::size_t Party, sharing Scheme>
|
template <typename T, std::size_t Party, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
|
constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
|
||||||
const secret_share<T, Party, Scheme> & rhs) noexcept
|
const secret_share<T, Party, Scheme> & rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -374,6 +416,7 @@ constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
|
||||||
template <typename T, std::size_t Party, sharing Scheme>
|
template <typename T, std::size_t Party, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
|
constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
|
||||||
const secret_share<T, Party, Scheme> & rhs) noexcept
|
const secret_share<T, Party, Scheme> & rhs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -387,10 +430,21 @@ constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
|
||||||
template <typename T, sharing Scheme>
|
template <typename T, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
|
constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
|
||||||
const secret_share<T, 1, Scheme> & s1) noexcept
|
const secret_share<T, 1, Scheme> & s1) noexcept
|
||||||
{
|
{
|
||||||
|
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
|
||||||
|
{
|
||||||
|
using unsigned_type = std::make_unsigned_t<T>;
|
||||||
if constexpr (Scheme == sharing::additive)
|
if constexpr (Scheme == sharing::additive)
|
||||||
|
return static_cast<T>(static_cast<unsigned_type>(s0.raw())
|
||||||
|
+ static_cast<unsigned_type>(s1.raw()));
|
||||||
|
else
|
||||||
|
return static_cast<T>(static_cast<unsigned_type>(s0.raw())
|
||||||
|
- static_cast<unsigned_type>(s1.raw()));
|
||||||
|
}
|
||||||
|
else if constexpr (Scheme == sharing::additive)
|
||||||
return static_cast<T>(s0.raw() + s1.raw());
|
return static_cast<T>(s0.raw() + s1.raw());
|
||||||
else
|
else
|
||||||
return static_cast<T>(s0.raw() - s1.raw());
|
return static_cast<T>(s0.raw() - s1.raw());
|
||||||
|
|
@ -399,6 +453,7 @@ constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
|
||||||
template <typename T, sharing Scheme>
|
template <typename T, sharing Scheme>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
|
constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
|
||||||
const secret_share<T, 0, Scheme> & s0) noexcept
|
const secret_share<T, 0, Scheme> & s0) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -412,6 +467,7 @@ constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
|
||||||
template <typename T>
|
template <typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr auto make_additive_shares(T secret) noexcept
|
constexpr auto make_additive_shares(T secret) noexcept
|
||||||
{
|
{
|
||||||
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
|
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
@ -423,6 +479,7 @@ constexpr auto make_additive_shares(T secret) noexcept
|
||||||
template <typename T>
|
template <typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr auto make_subtractive_shares(T secret) noexcept
|
constexpr auto make_subtractive_shares(T secret) noexcept
|
||||||
{
|
{
|
||||||
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
|
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
@ -460,13 +517,16 @@ struct party_key : Key
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
Key & key() noexcept { return static_cast<Key &>(*this); }
|
Key & key() noexcept { return static_cast<Key &>(*this); }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
const Key & key() const noexcept { return static_cast<const Key &>(*this); }
|
const Key & key() const noexcept { return static_cast<const Key &>(*this); }
|
||||||
|
|
||||||
/// Party-tagged additive share of the comparison absorb addend.
|
/// Party-tagged additive share of the comparison absorb addend.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
auto cmp_addend() const noexcept
|
auto cmp_addend() const noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
/// @file dpf/sequence_memoizer.hpp
|
/// @file dpf/sequence_memoizer.hpp
|
||||||
/// @brief
|
/// @brief Workspaces for a `sequence_recipe` traversal.
|
||||||
/// @details
|
/// @details The memoizer stores a reference to the recipe it was built from
|
||||||
|
/// and later calls must pass that same object (`std::logic_error`
|
||||||
|
/// otherwise). The factories unwrap `party_key`.
|
||||||
|
///
|
||||||
|
/// `inplace_reversing_sequence_memoizer` keeps one level.
|
||||||
|
/// `double_space_sequence_memoizer` keeps two and is the default
|
||||||
|
/// inside `eval_sequence(key, recipe, buffer)`.
|
||||||
|
/// `full_tree_sequence_memoizer` keeps every level.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -22,6 +29,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "dpf/secret_share.hpp"
|
||||||
#include "dpf/sequence_recipe.hpp"
|
#include "dpf/sequence_recipe.hpp"
|
||||||
|
|
||||||
namespace dpf
|
namespace dpf
|
||||||
|
|
@ -42,10 +50,13 @@ struct sequence_recipe_memoizer_base : public sequence_memoizer_tag_
|
||||||
|
|
||||||
// level 0 should access the root
|
// level 0 should access the root
|
||||||
// level goes up to (and including) depth
|
// level goes up to (and including) depth
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type operator[](std::size_t) const noexcept = 0;
|
virtual return_type operator[](std::size_t) const noexcept = 0;
|
||||||
|
|
||||||
// iterators should access most recently completed level
|
// iterators should access most recently completed level
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type begin() const noexcept = 0;
|
virtual return_type begin() const noexcept = 0;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
virtual return_type end() const noexcept = 0;
|
virtual return_type end() const noexcept = 0;
|
||||||
|
|
||||||
virtual std::size_t assign_dpf(const dpf_type & dpf, const sequence_recipe & r)
|
virtual std::size_t assign_dpf(const dpf_type & dpf, const sequence_recipe & r)
|
||||||
|
|
@ -210,11 +221,13 @@ struct pointer_facade
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
pointer_facade operator+(std::size_t n) const noexcept
|
pointer_facade operator+(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return pointer_facade(flip_, it_ + n, rit_ + n);
|
return pointer_facade(flip_, it_ + n, rit_ + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
pointer_facade & operator-=(std::size_t n) noexcept
|
pointer_facade & operator-=(std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
it_ -= n;
|
it_ -= n;
|
||||||
|
|
@ -222,11 +235,13 @@ struct pointer_facade
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
pointer_facade operator-(std::size_t n) const noexcept
|
pointer_facade operator-(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return pointer_facade(flip_, it_ - n, rit_ - n);
|
return pointer_facade(flip_, it_ - n, rit_ - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
difference_type operator-(pointer_facade rhs) const noexcept
|
difference_type operator-(pointer_facade rhs) const noexcept
|
||||||
{
|
{
|
||||||
return std::make_pair(it_ - rhs.it_, rit_ - rhs.rit_);
|
return std::make_pair(it_ - rhs.it_, rit_ - rhs.rit_);
|
||||||
|
|
@ -235,7 +250,7 @@ struct pointer_facade
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
reference operator[](std::size_t i)
|
reference operator[](std::size_t i) noexcept
|
||||||
{
|
{
|
||||||
return flip_ ? rit_[i] : it_[i];
|
return flip_ ? rit_[i] : it_[i];
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +258,7 @@ struct pointer_facade
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
const_reference operator[](std::size_t i) const
|
const_reference operator[](std::size_t i) const noexcept
|
||||||
{
|
{
|
||||||
return flip_ ? rit_[i] : it_[i];
|
return flip_ ? rit_[i] : it_[i];
|
||||||
}
|
}
|
||||||
|
|
@ -270,6 +285,8 @@ struct pointer_facade
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
/// One level. The buffer is traversed in the opposite direction on
|
||||||
|
/// alternate levels.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
||||||
struct inplace_reversing_sequence_memoizer final
|
struct inplace_reversing_sequence_memoizer final
|
||||||
|
|
@ -364,6 +381,8 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
unique_ptr buf;
|
unique_ptr buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Two levels, so a level can be built while the previous level is still
|
||||||
|
/// intact. Default workspace for `eval_sequence` on a recipe.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
||||||
struct double_space_sequence_memoizer final
|
struct double_space_sequence_memoizer final
|
||||||
|
|
@ -416,6 +435,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||||
unique_ptr buf;
|
unique_ptr buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Every level of the recipe's traversal.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
|
||||||
struct full_tree_sequence_memoizer final
|
struct full_tree_sequence_memoizer final
|
||||||
|
|
@ -482,10 +502,15 @@ auto make_sequence_memoizer(const sequence_recipe & recipe)
|
||||||
|
|
||||||
HEDLEY_PRAGMA(GCC diagnostic push)
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
||||||
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||||
|
/// One-level sequence workspace bound to `recipe`.
|
||||||
|
/// @param recipe The object later passed to `eval_sequence`. The memoizer
|
||||||
|
/// holds a reference to it.
|
||||||
|
/// @snippet evaluation/memoizers.cpp sequence-memoizer
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
inline auto make_inplace_reversing_sequence_memoizer(const sequence_recipe & recipe)
|
inline auto make_inplace_reversing_sequence_memoizer(const sequence_recipe & recipe)
|
||||||
{
|
{
|
||||||
return detail::make_sequence_memoizer<inplace_reversing_sequence_memoizer<DpfKey>>(recipe);
|
using key_t = unwrap_party_key_t<DpfKey>;
|
||||||
|
return detail::make_sequence_memoizer<inplace_reversing_sequence_memoizer<key_t>>(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
|
|
@ -494,10 +519,13 @@ inline auto make_inplace_reversing_sequence_memoizer(const DpfKey &, const seque
|
||||||
return make_inplace_reversing_sequence_memoizer<DpfKey>(recipe);
|
return make_inplace_reversing_sequence_memoizer<DpfKey>(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Two-level sequence workspace bound to `recipe`.
|
||||||
|
/// @snippet evaluation/eval_sequence.cpp eval-sequence-recipe
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
inline auto make_double_space_sequence_memoizer(const sequence_recipe & recipe)
|
inline auto make_double_space_sequence_memoizer(const sequence_recipe & recipe)
|
||||||
{
|
{
|
||||||
return detail::make_sequence_memoizer<double_space_sequence_memoizer<DpfKey>>(recipe);
|
using key_t = unwrap_party_key_t<DpfKey>;
|
||||||
|
return detail::make_sequence_memoizer<double_space_sequence_memoizer<key_t>>(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
|
|
@ -506,10 +534,12 @@ inline auto make_double_space_sequence_memoizer(const DpfKey &, const sequence_r
|
||||||
return make_double_space_sequence_memoizer<DpfKey>(recipe);
|
return make_double_space_sequence_memoizer<DpfKey>(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Full-tree sequence workspace bound to `recipe`.
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
inline auto make_full_tree_sequence_memoizer(const sequence_recipe & recipe)
|
inline auto make_full_tree_sequence_memoizer(const sequence_recipe & recipe)
|
||||||
{
|
{
|
||||||
return detail::make_sequence_memoizer<full_tree_sequence_memoizer<DpfKey>>(recipe);
|
using key_t = unwrap_party_key_t<DpfKey>;
|
||||||
|
return detail::make_sequence_memoizer<full_tree_sequence_memoizer<key_t>>(recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DpfKey>
|
template <typename DpfKey>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
/// @file dpf/sequence_recipe.hpp
|
/// @file dpf/sequence_recipe.hpp
|
||||||
/// @brief
|
/// @brief Compiled traversal of a sorted DPF point list.
|
||||||
/// @details
|
/// @details `make_sequence_recipe` requires a nondecreasing range and throws
|
||||||
|
/// `std::runtime_error` otherwise. The recipe is independent of
|
||||||
|
/// correction words, so one recipe serves every key of that input
|
||||||
|
/// type. A sequence memoizer is bound to a particular recipe object.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||||
|
|
@ -10,6 +13,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
|
#define LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -21,6 +26,7 @@
|
||||||
namespace dpf
|
namespace dpf
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Steps, leaf count, and per-level endpoints for one sorted point list.
|
||||||
struct sequence_recipe
|
struct sequence_recipe
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -34,10 +40,21 @@ struct sequence_recipe
|
||||||
level_endpoints_{level_endpoints}
|
level_endpoints_{level_endpoints}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const std::vector<int8_t> & recipe_steps() const noexcept { return recipe_steps_; }
|
constexpr const std::vector<int8_t> & recipe_steps() const noexcept { return recipe_steps_; }
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const std::vector<std::size_t> & output_indices() const noexcept { return output_indices_; }
|
constexpr const std::vector<std::size_t> & output_indices() const noexcept { return output_indices_; }
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr std::size_t num_leaf_nodes() const noexcept { return num_leaf_nodes_; }
|
constexpr std::size_t num_leaf_nodes() const noexcept { return num_leaf_nodes_; }
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr const std::vector<std::size_t> & level_endpoints() const noexcept { return level_endpoints_; }
|
constexpr const std::vector<std::size_t> & level_endpoints() const noexcept { return level_endpoints_; }
|
||||||
|
/// `level_endpoints().size() - 1`. Not `constexpr`: `std::vector::size` is not a constant expression in C++17.
|
||||||
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t depth() const noexcept { return level_endpoints_.size()-1; }
|
std::size_t depth() const noexcept { return level_endpoints_.size()-1; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -122,6 +139,10 @@ auto make_sequence_recipe(ForwardIterator begin, ForwardIterator end)
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Compile `[begin, end)` into a recipe for `DpfKey`'s input type.
|
||||||
|
/// @tparam DpfKey Key type, or a `party_key` of that key. Only the input
|
||||||
|
/// type and depth are used.
|
||||||
|
/// @throws std::runtime_error if the range is not sorted nondecreasing.
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename ForwardIterator>
|
typename ForwardIterator>
|
||||||
auto make_sequence_recipe(ForwardIterator begin, ForwardIterator end)
|
auto make_sequence_recipe(ForwardIterator begin, ForwardIterator end)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
|
/// @file dpf/sequence_utils.hpp
|
||||||
|
/// @brief Tags that select how `eval_sequence` stores each visited leaf.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
|
#define LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
|
||||||
|
|
||||||
namespace dpf
|
namespace dpf
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Tag base for `eval_sequence` storage layout.
|
||||||
struct return_type_tag_{};
|
struct return_type_tag_{};
|
||||||
|
|
||||||
|
/// Store whole leaves. Default for `eval_sequence`. The iterable still
|
||||||
|
/// yields one share per listed point.
|
||||||
struct return_entire_node_tag_ final : public return_type_tag_ {};
|
struct return_entire_node_tag_ final : public return_type_tag_ {};
|
||||||
// static constexpr auto return_entire_node_tag = return_entire_node_tag_{};
|
// static constexpr auto return_entire_node_tag = return_entire_node_tag_{};
|
||||||
|
|
||||||
|
/// Store one share per listed point.
|
||||||
struct return_output_only_tag_ final : public return_type_tag_ {};
|
struct return_output_only_tag_ final : public return_type_tag_ {};
|
||||||
// static constexpr auto return_output_only_tag = return_output_only_tag_{};
|
// static constexpr auto return_output_only_tag = return_output_only_tag_{};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,10 @@ class setbit_index_iterable
|
||||||
explicit setbit_index_iterable(iter it) noexcept
|
explicit setbit_index_iterable(iter it) noexcept
|
||||||
: it_{std::move(it)},
|
: it_{std::move(it)},
|
||||||
begin_{it_.it_.word_ptr_},
|
begin_{it_.it_.word_ptr_},
|
||||||
end_{begin_ + it_.buf_size_},
|
// `buf_size_` is a bit count (`bit_array::size()`). The sentinel word
|
||||||
|
// sits one past the last data word, and the end iterator has to land
|
||||||
|
// on it rather than `buf_size_` words later.
|
||||||
|
end_{begin_ + utils::quotient_ceiling(it_.buf_size_, bits_per_word)},
|
||||||
base_index_{calc_base_index(it_)},
|
base_index_{calc_base_index(it_)},
|
||||||
length_{calc_length(it_)}
|
length_{calc_length(it_)}
|
||||||
{
|
{
|
||||||
|
|
@ -89,6 +92,7 @@ class setbit_index_iterable
|
||||||
return it.outputs_ == 0 ? it.from_ : utils::quotient_floor(it.from_, it.outputs_) * it.outputs_;
|
return it.outputs_ == 0 ? it.from_ : utils::quotient_floor(it.from_, it.outputs_) * it.outputs_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr size_type calc_length(const iter & it) noexcept
|
static constexpr size_type calc_length(const iter & it) noexcept
|
||||||
{
|
{
|
||||||
return it.outputs_ == 0 ? utils::quotient_ceiling(it.to_, bits_per_word) * bits_per_word : utils::quotient_ceiling(it.to_, it.outputs_) * it.outputs_;
|
return it.outputs_ == 0 ? utils::quotient_ceiling(it.to_, bits_per_word) * bits_per_word : utils::quotient_ceiling(it.to_, it.outputs_) * it.outputs_;
|
||||||
|
|
@ -102,7 +106,7 @@ class setbit_index_iterable
|
||||||
// while other words just need some bits to be masked out
|
// while other words just need some bits to be masked out
|
||||||
constexpr void update_bit_array()
|
constexpr void update_bit_array()
|
||||||
{
|
{
|
||||||
if (it_.outputs_ == 0)
|
if (it_.outputs_ == 0 || begin_ == end_)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -124,12 +128,17 @@ class setbit_index_iterable
|
||||||
cur = end_-1;
|
cur = end_-1;
|
||||||
loc = it_.to_ % it_.outputs_;
|
loc = it_.to_ % it_.outputs_;
|
||||||
|
|
||||||
|
// `outputs_ - bits_per_word` underflows when a leaf is narrower than
|
||||||
|
// a word, and the loop then walks off the front of the buffer.
|
||||||
|
if (it_.outputs_ > bits_per_word)
|
||||||
|
{
|
||||||
while (loc < it_.outputs_ - bits_per_word)
|
while (loc < it_.outputs_ - bits_per_word)
|
||||||
{
|
{
|
||||||
*cur = zero;
|
*cur = zero;
|
||||||
--cur;
|
--cur;
|
||||||
loc += bits_per_word;
|
loc += bits_per_word;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
*cur &= mask;
|
*cur &= mask;
|
||||||
}
|
}
|
||||||
}; // class dpf::setbit_index_iterable
|
}; // class dpf::setbit_index_iterable
|
||||||
|
|
@ -151,12 +160,16 @@ class const_setbit_iterator
|
||||||
using difference_type = std::ptrdiff_t;
|
using difference_type = std::ptrdiff_t;
|
||||||
static constexpr auto bits_per_word = array_type::bits_per_word;
|
static constexpr auto bits_per_word = array_type::bits_per_word;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_setbit_iterator(const_setbit_iterator &&) noexcept = default;
|
constexpr const_setbit_iterator(const_setbit_iterator &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_setbit_iterator(const const_setbit_iterator &) noexcept = default;
|
constexpr const_setbit_iterator(const const_setbit_iterator &) noexcept = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
~const_setbit_iterator() noexcept = default;
|
~const_setbit_iterator() noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_setbit_iterator & operator=(const_setbit_iterator &&) noexcept = default;
|
const_setbit_iterator & operator=(const_setbit_iterator &&) noexcept = default;
|
||||||
const_setbit_iterator & operator=(const const_setbit_iterator &) = default;
|
const_setbit_iterator & operator=(const const_setbit_iterator &) = default;
|
||||||
|
|
||||||
|
|
@ -279,6 +292,7 @@ class const_setbit_iterator
|
||||||
struct const_iterator_end_tag final {};
|
struct const_iterator_end_tag final {};
|
||||||
struct const_iterator_begin_tag final {};
|
struct const_iterator_begin_tag final {};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NON_NULL()
|
HEDLEY_NON_NULL()
|
||||||
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
|
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
|
||||||
|
|
@ -290,6 +304,7 @@ class const_setbit_iterator
|
||||||
seek_to_next_bit();
|
seek_to_next_bit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NON_NULL()
|
HEDLEY_NON_NULL()
|
||||||
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
|
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
|
||||||
|
|
@ -303,13 +318,16 @@ class const_setbit_iterator
|
||||||
word_type current_word_;
|
word_type current_word_;
|
||||||
size_type base_index_;
|
size_type base_index_;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::begin() const noexcept;
|
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::begin() const noexcept;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::end() const noexcept;
|
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::end() const noexcept;
|
||||||
}; // class dpf::const_setbit_iterator
|
}; // class dpf::const_setbit_iterator
|
||||||
|
|
||||||
template <typename ChildT,
|
template <typename ChildT,
|
||||||
typename WordT>
|
typename WordT>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(const subinterval_iterable<bit_iterator<ChildT, WordT>> & iter) noexcept
|
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(const subinterval_iterable<bit_iterator<ChildT, WordT>> & iter) noexcept
|
||||||
{
|
{
|
||||||
return dpf::setbit_index_iterable<ChildT, WordT>{iter};
|
return dpf::setbit_index_iterable<ChildT, WordT>{iter};
|
||||||
|
|
@ -318,6 +336,7 @@ dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(const subinterval_itera
|
||||||
template <typename ChildT,
|
template <typename ChildT,
|
||||||
typename WordT>
|
typename WordT>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(subinterval_iterable<bit_iterator<ChildT, WordT>> && iter) noexcept
|
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(subinterval_iterable<bit_iterator<ChildT, WordT>> && iter) noexcept
|
||||||
{
|
{
|
||||||
return dpf::setbit_index_iterable<ChildT, WordT>{std::forward<subinterval_iterable<bit_iterator<ChildT, WordT>>>(iter)};
|
return dpf::setbit_index_iterable<ChildT, WordT>{std::forward<subinterval_iterable<bit_iterator<ChildT, WordT>>>(iter)};
|
||||||
|
|
|
||||||
|
|
@ -80,16 +80,20 @@ class subsequence_iterable
|
||||||
|
|
||||||
using subsequence_iterator_type = points_iterator;
|
using subsequence_iterator_type = points_iterator;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
|
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
|
||||||
: out_it_{out_it}, it_{it}
|
: out_it_{out_it}, it_{it}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(const_iterator &&) noexcept = default;
|
constexpr const_iterator(const_iterator &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(const const_iterator &) noexcept = default;
|
constexpr const_iterator(const const_iterator &) noexcept = default;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator & operator=(const_iterator &&) noexcept = default;
|
const_iterator & operator=(const_iterator &&) noexcept = default;
|
||||||
const_iterator & operator=(const const_iterator &) = default;
|
const_iterator & operator=(const const_iterator &) = default;
|
||||||
~const_iterator() = default;
|
~const_iterator() = default;
|
||||||
|
|
@ -143,11 +147,13 @@ class subsequence_iterable
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator operator+(std::size_t n) const noexcept
|
const_iterator operator+(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(out_it_ + outputs_per_leaf*n, it_ + n);
|
return const_iterator(out_it_ + outputs_per_leaf*n, it_ + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator & operator-=(std::size_t n) noexcept
|
const_iterator & operator-=(std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
it_ -= n;
|
it_ -= n;
|
||||||
|
|
@ -155,16 +161,19 @@ class subsequence_iterable
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator operator-(std::size_t n) const noexcept
|
const_iterator operator-(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(out_it_ - outputs_per_leaf*n, it_ - n);
|
return const_iterator(out_it_ - outputs_per_leaf*n, it_ - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
difference_type operator-(const_iterator rhs) const noexcept
|
difference_type operator-(const_iterator rhs) const noexcept
|
||||||
{
|
{
|
||||||
return it_ - rhs.it_;
|
return it_ - rhs.it_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference operator[](std::size_t i) const noexcept
|
reference operator[](std::size_t i) const noexcept
|
||||||
{
|
{
|
||||||
return out_it_[i*outputs_per_leaf + mod(it_[i], lg_outputs_per_leaf)];
|
return out_it_[i*outputs_per_leaf + mod(it_[i], lg_outputs_per_leaf)];
|
||||||
|
|
@ -279,17 +288,21 @@ class recipe_subsequence_iterable
|
||||||
|
|
||||||
using subsequence_iterator_type = typename std::vector<std::size_t>::const_iterator;
|
using subsequence_iterator_type = typename std::vector<std::size_t>::const_iterator;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
|
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
|
||||||
: out_it_{out_it}, it_{it}
|
: out_it_{out_it}, it_{it}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(const_iterator &&) noexcept = default;
|
constexpr const_iterator(const_iterator &&) noexcept = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr const_iterator(const const_iterator &) noexcept = default;
|
constexpr const_iterator(const const_iterator &) noexcept = default;
|
||||||
|
|
||||||
const_iterator & operator=(const const_iterator &) = default;
|
const_iterator & operator=(const const_iterator &) = default;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator & operator=(const_iterator &&) noexcept = default;
|
const_iterator & operator=(const_iterator &&) noexcept = default;
|
||||||
~const_iterator() = default;
|
~const_iterator() = default;
|
||||||
|
|
||||||
|
|
@ -339,27 +352,32 @@ class recipe_subsequence_iterable
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator operator+(std::size_t n) const noexcept
|
const_iterator operator+(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(out_it_, it_ + n);
|
return const_iterator(out_it_, it_ + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator & operator-=(std::size_t n) noexcept
|
const_iterator & operator-=(std::size_t n) noexcept
|
||||||
{
|
{
|
||||||
it_ -= n;
|
it_ -= n;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
const_iterator operator-(std::size_t n) const noexcept
|
const_iterator operator-(std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(out_it_, it_ - n);
|
return const_iterator(out_it_, it_ - n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
difference_type operator-(const_iterator rhs) const noexcept
|
difference_type operator-(const_iterator rhs) const noexcept
|
||||||
{
|
{
|
||||||
return it_ - rhs.it_;
|
return it_ - rhs.it_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
reference operator[](std::size_t i) const noexcept
|
reference operator[](std::size_t i) const noexcept
|
||||||
{
|
{
|
||||||
return out_it_[it_[i]];
|
return out_it_[it_[i]];
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ template <>
|
||||||
struct make_from_integral_value<dpf::twobit>
|
struct make_from_integral_value<dpf::twobit>
|
||||||
{
|
{
|
||||||
using integral_type = std::uint8_t;
|
using integral_type = std::uint8_t;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr dpf::twobit operator()(integral_type val) const noexcept
|
constexpr dpf::twobit operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
return dpf::to_twobit(val);
|
return dpf::to_twobit(val);
|
||||||
|
|
@ -184,8 +185,11 @@ class numeric_limits<dpf::twobit> : public numeric_limits<std::uint8_t>
|
||||||
public:
|
public:
|
||||||
static constexpr int digits = 2;
|
static constexpr int digits = 2;
|
||||||
static constexpr int digits10 = 0;
|
static constexpr int digits10 = 0;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::twobit min() noexcept { return dpf::twobit::zero; }
|
static constexpr dpf::twobit min() noexcept { return dpf::twobit::zero; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::twobit max() noexcept { return dpf::twobit::three; }
|
static constexpr dpf::twobit max() noexcept { return dpf::twobit::three; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr dpf::twobit lowest() noexcept { return min(); }
|
static constexpr dpf::twobit lowest() noexcept { return min(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/// @file dpf/uint256_t.hpp
|
||||||
|
/// @brief Leaf arithmetic for `uint256_t` and the 128-bit SIMD lanes.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
|
||||||
#define LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
|
#define LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
|
||||||
|
|
||||||
|
|
@ -205,6 +210,7 @@ struct msb_of<uint256_t>
|
||||||
template <>
|
template <>
|
||||||
struct mod_pow_2<uint128_t>
|
struct mod_pow_2<uint128_t>
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(uint128_t val, std::size_t n) const noexcept
|
std::size_t operator()(uint128_t val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod_pow_2<uint64_t>{}(static_cast<uint64_t>(val.lower()), n);
|
return mod_pow_2<uint64_t>{}(static_cast<uint64_t>(val.lower()), n);
|
||||||
|
|
@ -214,6 +220,7 @@ struct mod_pow_2<uint128_t>
|
||||||
template <>
|
template <>
|
||||||
struct mod_pow_2<uint256_t>
|
struct mod_pow_2<uint256_t>
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(uint256_t val, std::size_t n) const noexcept
|
std::size_t operator()(uint256_t val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod_pow_2<uint128_t>{}(val.lower(), n);
|
return mod_pow_2<uint128_t>{}(val.lower(), n);
|
||||||
|
|
@ -227,6 +234,7 @@ struct to_integral_type<uint128_t>
|
||||||
using parent = to_integral_type_base<uint128_t>;
|
using parent = to_integral_type_base<uint128_t>;
|
||||||
using typename parent::integral_type;
|
using typename parent::integral_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr integral_type operator()(uint128_t val) const noexcept
|
constexpr integral_type operator()(uint128_t val) const noexcept
|
||||||
{
|
{
|
||||||
return (simde_uint128(val.upper()) << 64) | simde_uint128(val.lower());
|
return (simde_uint128(val.upper()) << 64) | simde_uint128(val.lower());
|
||||||
|
|
@ -240,6 +248,7 @@ struct to_integral_type<uint256_t>
|
||||||
using parent = to_integral_type_base<uint256_t>;
|
using parent = to_integral_type_base<uint256_t>;
|
||||||
using typename parent::integral_type;
|
using typename parent::integral_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr integral_type operator()(uint256_t val) const noexcept
|
constexpr integral_type operator()(uint256_t val) const noexcept
|
||||||
{
|
{
|
||||||
return val;
|
return val;
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,23 @@ class numeric_limits<::uint128_t>
|
||||||
static constexpr bool traps = false;
|
static constexpr bool traps = false;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t min() noexcept { return uint128_t{0ul, 0ul}; }
|
static constexpr uint128_t min() noexcept { return uint128_t{0ul, 0ul}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t lowest() noexcept { return uint128_t{0ul, 0ul}; }
|
static constexpr uint128_t lowest() noexcept { return uint128_t{0ul, 0ul}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t max() noexcept { return uint128_t{-1ul, -1ul}; }
|
static constexpr uint128_t max() noexcept { return uint128_t{-1ul, -1ul}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t epsilon() noexcept { return 0; }
|
static constexpr uint128_t epsilon() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t round_error() noexcept { return 0; }
|
static constexpr uint128_t round_error() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t infinity() noexcept { return 0; }
|
static constexpr uint128_t infinity() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t quiet_NaN() noexcept { return 0; }
|
static constexpr uint128_t quiet_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t signaling_NaN() noexcept { return 0; }
|
static constexpr uint128_t signaling_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint128_t denorm_min() noexcept { return 0; }
|
static constexpr uint128_t denorm_min() noexcept { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -127,14 +136,23 @@ class numeric_limits<::uint256_t>
|
||||||
static constexpr bool traps = false;
|
static constexpr bool traps = false;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t min() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
|
static constexpr uint256_t min() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t lowest() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
|
static constexpr uint256_t lowest() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t max() noexcept { return uint256_t{uint128_t{-1ul, -1ul}, uint128_t{-1ul, -1ul}}; }
|
static constexpr uint256_t max() noexcept { return uint256_t{uint128_t{-1ul, -1ul}, uint128_t{-1ul, -1ul}}; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t epsilon() noexcept { return 0; }
|
static constexpr uint256_t epsilon() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t round_error() noexcept { return 0; }
|
static constexpr uint256_t round_error() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t infinity() noexcept { return 0; }
|
static constexpr uint256_t infinity() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t quiet_NaN() noexcept { return 0; }
|
static constexpr uint256_t quiet_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t signaling_NaN() noexcept { return 0; }
|
static constexpr uint256_t signaling_NaN() noexcept { return 0; }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr uint256_t denorm_min() noexcept { return 0; }
|
static constexpr uint256_t denorm_min() noexcept { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -266,6 +284,7 @@ auto make_bitset(Bools ...bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename NodeT>
|
template <typename NodeT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static NodeT single_bit_mask(std::size_t i) noexcept;
|
static NodeT single_bit_mask(std::size_t i) noexcept;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -449,6 +468,7 @@ struct make_from_integral_value
|
||||||
// for `unsigned __int128`.
|
// for `unsigned __int128`.
|
||||||
using integral_type = typename make_signed_if<S_integral_type,
|
using integral_type = typename make_signed_if<S_integral_type,
|
||||||
std::is_signed_v<T> && sizeof(S_integral_type) <= 8>::type;
|
std::is_signed_v<T> && sizeof(S_integral_type) <= 8>::type;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T operator()(integral_type val) const noexcept
|
constexpr T operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<T>(val);
|
return static_cast<T>(val);
|
||||||
|
|
@ -459,6 +479,7 @@ struct make_from_integral_value
|
||||||
/// `static_cast<T>(x0 ^ x1)`: for `keyword`, `operator^` yields the parent
|
/// `static_cast<T>(x0 ^ x1)`: for `keyword`, `operator^` yields the parent
|
||||||
/// `modint`, which cannot convert back through the private keyword ctor.
|
/// `modint`, which cannot convert back through the private keyword ctor.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T xor_input_shares(T x0, T x1) noexcept
|
constexpr T xor_input_shares(T x0, T x1) noexcept
|
||||||
{
|
{
|
||||||
constexpr auto to_int = to_integral_type<T>{};
|
constexpr auto to_int = to_integral_type<T>{};
|
||||||
|
|
@ -490,6 +511,7 @@ static constexpr IntegralT get_node_mask(InputT mask, std::size_t level_index)
|
||||||
/// width is undefined for the native unsigned types).
|
/// width is undefined for the native unsigned types).
|
||||||
template <typename IntegralT>
|
template <typename IntegralT>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralT shift_right(IntegralT value, std::size_t offset) noexcept
|
constexpr IntegralT shift_right(IntegralT value, std::size_t offset) noexcept
|
||||||
{
|
{
|
||||||
if (offset >= bitlength_of_v<IntegralT>)
|
if (offset >= bitlength_of_v<IntegralT>)
|
||||||
|
|
@ -500,6 +522,7 @@ constexpr IntegralT shift_right(IntegralT value, std::size_t offset) noexcept
|
||||||
/// Floor of `from_inclusive / 2^lg_opl`. `lg_opl` is `log2(outputs_per_leaf)`.
|
/// Floor of `from_inclusive / 2^lg_opl`. `lg_opl` is `log2(outputs_per_leaf)`.
|
||||||
template <typename IntegralT>
|
template <typename IntegralT>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralT leaf_node_floor(IntegralT from_inclusive, std::size_t lg_opl) noexcept
|
constexpr IntegralT leaf_node_floor(IntegralT from_inclusive, std::size_t lg_opl) noexcept
|
||||||
{
|
{
|
||||||
if (lg_opl == 0)
|
if (lg_opl == 0)
|
||||||
|
|
@ -514,6 +537,7 @@ constexpr IntegralT leaf_node_floor(IntegralT from_inclusive, std::size_t lg_opl
|
||||||
/// end (`[from, 2^width)`), which `split_leaf_nodes` interprets.
|
/// end (`[from, 2^width)`), which `split_leaf_nodes` interprets.
|
||||||
template <typename IntegralT>
|
template <typename IntegralT>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralT leaf_node_ceil_exclusive(IntegralT to_inclusive, std::size_t lg_opl) noexcept
|
constexpr IntegralT leaf_node_ceil_exclusive(IntegralT to_inclusive, std::size_t lg_opl) noexcept
|
||||||
{
|
{
|
||||||
constexpr std::size_t width = bitlength_of_v<IntegralT>;
|
constexpr std::size_t width = bitlength_of_v<IntegralT>;
|
||||||
|
|
@ -585,25 +609,87 @@ struct node_segments
|
||||||
std::size_t total = 0;
|
std::size_t total = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// True when the inclusive walk `[from, to]` wraps the low `bits` of the
|
||||||
|
/// domain. Comparison is on the post-MSB-flip bit pattern. Leaf ids alone
|
||||||
|
/// cannot carry this: packing can put a wrapping pair into `from_node <= to_node`.
|
||||||
|
template <typename IntegralT>
|
||||||
|
inline bool interval_wraps(IntegralT from, IntegralT to, std::size_t bits)
|
||||||
|
{
|
||||||
|
if (bits == 0)
|
||||||
|
return false;
|
||||||
|
if (bits < bitlength_of_v<IntegralT>)
|
||||||
|
{
|
||||||
|
const IntegralT mask = static_cast<IntegralT>(
|
||||||
|
(IntegralT{1} << bits) - IntegralT{1});
|
||||||
|
return (from & mask) > (to & mask);
|
||||||
|
}
|
||||||
|
return from > to;
|
||||||
|
}
|
||||||
|
|
||||||
/// Split an inclusive output interval, already reduced to leaf ids, into one
|
/// Split an inclusive output interval, already reduced to leaf ids, into one
|
||||||
/// or two half-open walks. A linearized `from_node > to_node` wraps the node
|
/// or two half-open walks. A linearized `from_node > to_node` wraps the node
|
||||||
/// id space `[0, 2^depth)`. A saturated `to_node == 0` means the exclusive end
|
/// id space `[0, 2^depth)`. A saturated `to_node == 0` means the exclusive end
|
||||||
/// is `2^{bitwidth(IntegralT)}`, which is the whole id space when `depth` is
|
/// is `2^{bitwidth(IntegralT)}`, which is the whole id space when `depth` is
|
||||||
/// that width.
|
/// that width.
|
||||||
|
///
|
||||||
|
/// `input_wraps` is the order of the original inputs, before leaf coarsening.
|
||||||
|
/// The buffer is still two runs, `[from_node, 2^depth)` then `[0, to_node)`,
|
||||||
|
/// even when packing makes `from_node <= to_node`. In that case the runs
|
||||||
|
/// overlap on the shared leaf: the iterable's preclip consumes the start of
|
||||||
|
/// the first copy and its length stops inside the second. Collapsing the
|
||||||
|
/// overlap into one forward segment writes the wrong leaves.
|
||||||
template <typename IntegralT>
|
template <typename IntegralT>
|
||||||
inline node_segments<IntegralT> split_leaf_nodes(IntegralT from_node,
|
inline node_segments<IntegralT> split_leaf_nodes(IntegralT from_node,
|
||||||
IntegralT to_node, std::size_t depth)
|
IntegralT to_node, std::size_t depth, bool input_wraps = false)
|
||||||
{
|
{
|
||||||
node_segments<IntegralT> out;
|
node_segments<IntegralT> out;
|
||||||
|
constexpr std::size_t width = bitlength_of_v<IntegralT>;
|
||||||
|
constexpr std::size_t size_digits = bitlength_of_v<std::size_t>;
|
||||||
auto push = [&](IntegralT lo, IntegralT hi)
|
auto push = [&](IntegralT lo, IntegralT hi)
|
||||||
{
|
{
|
||||||
const std::size_t count = static_cast<std::size_t>(hi - lo);
|
std::size_t count = 0;
|
||||||
|
if (hi == IntegralT{0} && lo != IntegralT{0})
|
||||||
|
{
|
||||||
|
// Exclusive end is 2^width. The count fits in size_t only when
|
||||||
|
// that power is one past size_t's maximum and lo is nonzero.
|
||||||
|
if (width > size_digits)
|
||||||
|
throw std::length_error("DPF leaf domain does not fit in size_t");
|
||||||
|
count = static_cast<std::size_t>(0) - static_cast<std::size_t>(lo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto wide = hi - lo;
|
||||||
|
if (wide > IntegralT(std::numeric_limits<std::size_t>::max()))
|
||||||
|
throw std::length_error("DPF leaf domain does not fit in size_t");
|
||||||
|
count = static_cast<std::size_t>(wide);
|
||||||
|
}
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
|
if (out.total > std::numeric_limits<std::size_t>::max() - count)
|
||||||
|
throw std::length_error("DPF leaf domain does not fit in size_t");
|
||||||
out.seg[out.n++] = node_segment<IntegralT>{lo, hi, count};
|
out.seg[out.n++] = node_segment<IntegralT>{lo, hi, count};
|
||||||
out.total += count;
|
out.total += count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (input_wraps)
|
||||||
|
{
|
||||||
|
if (depth >= width)
|
||||||
|
{
|
||||||
|
if (from_node == IntegralT{0})
|
||||||
|
throw std::length_error("DPF leaf domain does not fit in size_t");
|
||||||
|
push(from_node, IntegralT{0});
|
||||||
|
if (to_node != IntegralT{0})
|
||||||
|
push(IntegralT{0}, to_node);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
const IntegralT domain_end = static_cast<IntegralT>(IntegralT{1} << depth);
|
||||||
|
if (from_node < domain_end)
|
||||||
|
push(from_node, domain_end);
|
||||||
|
if (to_node != IntegralT{0})
|
||||||
|
push(IntegralT{0}, to_node);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
if (to_node != IntegralT{0} && from_node < to_node)
|
if (to_node != IntegralT{0} && from_node < to_node)
|
||||||
{
|
{
|
||||||
push(from_node, to_node);
|
push(from_node, to_node);
|
||||||
|
|
@ -612,7 +698,6 @@ inline node_segments<IntegralT> split_leaf_nodes(IntegralT from_node,
|
||||||
if (to_node != IntegralT{0} && from_node == to_node)
|
if (to_node != IntegralT{0} && from_node == to_node)
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
constexpr std::size_t width = bitlength_of_v<IntegralT>;
|
|
||||||
if (from_node == IntegralT{0} && to_node == IntegralT{0})
|
if (from_node == IntegralT{0} && to_node == IntegralT{0})
|
||||||
throw std::length_error("DPF leaf domain does not fit in size_t");
|
throw std::length_error("DPF leaf domain does not fit in size_t");
|
||||||
|
|
||||||
|
|
@ -638,14 +723,26 @@ static constexpr std::size_t get_leafnodes_in_node_interval(IntegralT from_node,
|
||||||
return static_cast<std::size_t>(to_node - from_node);
|
return static_cast<std::size_t>(to_node - from_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void flip_msb_if_signed_integral(T & x);
|
||||||
|
|
||||||
template <typename DpfKey,
|
template <typename DpfKey,
|
||||||
typename InputT = typename DpfKey::input_type,
|
typename InputT = typename DpfKey::input_type,
|
||||||
typename IntegralT = typename DpfKey::integral_type>
|
typename IntegralT = typename DpfKey::integral_type>
|
||||||
static std::size_t get_leafnodes_in_output_interval(InputT from, InputT to)
|
static std::size_t get_leafnodes_in_output_interval(InputT from, InputT to)
|
||||||
{
|
{
|
||||||
return split_leaf_nodes(get_from_node<DpfKey, InputT, IntegralT>(from),
|
// Match eval: the walk order is the bit pattern after the sign flip.
|
||||||
get_to_node<DpfKey, InputT, IntegralT>(to),
|
InputT flipped_from = from;
|
||||||
static_cast<std::size_t>(DpfKey::depth)).total;
|
InputT flipped_to = to;
|
||||||
|
flip_msb_if_signed_integral(flipped_from);
|
||||||
|
flip_msb_if_signed_integral(flipped_to);
|
||||||
|
constexpr auto to_int = to_integral_type<InputT>{};
|
||||||
|
const auto from_i = static_cast<IntegralT>(to_int(flipped_from));
|
||||||
|
const auto to_i = static_cast<IntegralT>(to_int(flipped_to));
|
||||||
|
const bool wraps = interval_wraps(from_i, to_i, bitlength_of_v<InputT>);
|
||||||
|
return split_leaf_nodes(get_from_node<DpfKey, InputT, IntegralT>(flipped_from),
|
||||||
|
get_to_node<DpfKey, InputT, IntegralT>(flipped_to),
|
||||||
|
static_cast<std::size_t>(DpfKey::depth), wraps).total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Historical name used by the test suite.
|
/// Historical name used by the test suite.
|
||||||
|
|
@ -660,6 +757,7 @@ static std::size_t get_nodes_in_interval(InputT from, InputT to)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct mod_pow_2
|
struct mod_pow_2
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(T val, std::size_t n) const noexcept
|
std::size_t operator()(T val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
|
|
@ -696,6 +794,7 @@ static constexpr auto msb_of_v = msb_of<T>::value;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct countl_zero
|
struct countl_zero
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(T val) const noexcept
|
constexpr std::size_t operator()(T val) const noexcept
|
||||||
|
|
@ -724,6 +823,7 @@ struct countl_zero
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct countr_zero
|
struct countr_zero
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(T val) const noexcept
|
constexpr std::size_t operator()(T val) const noexcept
|
||||||
|
|
@ -756,6 +856,7 @@ struct countr_zero
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct countl_zero_symmetric_difference
|
struct countl_zero_symmetric_difference
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
|
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
|
||||||
|
|
@ -773,6 +874,7 @@ struct countl_zero<simde_int128>
|
||||||
{
|
{
|
||||||
using T = simde_int128;
|
using T = simde_int128;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -795,6 +897,7 @@ struct countl_zero<simde_uint128>
|
||||||
{
|
{
|
||||||
using T = simde_uint128;
|
using T = simde_uint128;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -812,6 +915,7 @@ struct countl_zero<uint128_t>
|
||||||
{
|
{
|
||||||
using T = uint128_t;
|
using T = uint128_t;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -828,6 +932,7 @@ struct countl_zero<uint256_t>
|
||||||
{
|
{
|
||||||
using T = uint256_t;
|
using T = uint256_t;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -844,6 +949,7 @@ struct countl_zero<simde__m128i>
|
||||||
{
|
{
|
||||||
using T = simde__m128i;
|
using T = simde__m128i;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
std::size_t operator()(const T & val) const noexcept
|
std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -860,6 +966,7 @@ template <>
|
||||||
struct countl_zero<simde__m256i>
|
struct countl_zero<simde__m256i>
|
||||||
{
|
{
|
||||||
using T = simde__m256i;
|
using T = simde__m256i;
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -882,6 +989,7 @@ struct countr_zero<simde_int128>
|
||||||
{
|
{
|
||||||
using T = simde_int128;
|
using T = simde_int128;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -898,6 +1006,7 @@ struct countr_zero<simde_uint128>
|
||||||
{
|
{
|
||||||
using T = simde_uint128;
|
using T = simde_uint128;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -914,6 +1023,7 @@ struct countr_zero<uint128_t>
|
||||||
{
|
{
|
||||||
using T = uint128_t;
|
using T = uint128_t;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -930,6 +1040,7 @@ struct countr_zero<uint256_t>
|
||||||
{
|
{
|
||||||
using T = uint256_t;
|
using T = uint256_t;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -946,6 +1057,7 @@ struct countr_zero<simde__m128i>
|
||||||
{
|
{
|
||||||
using T = simde__m128i;
|
using T = simde__m128i;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
std::size_t operator()(const T & val) const noexcept
|
std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -963,6 +1075,7 @@ struct countr_zero<simde__m256i>
|
||||||
{
|
{
|
||||||
using T = simde__m256i;
|
using T = simde__m256i;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & val) const noexcept
|
constexpr std::size_t operator()(const T & val) const noexcept
|
||||||
|
|
@ -1026,13 +1139,19 @@ static constexpr std::size_t packed_lane_bits_v
|
||||||
= packed_lane_bits<std::remove_cv_t<T>>::value;
|
= packed_lane_bits<std::remove_cv_t<T>>::value;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto data(T & bar) // NOLINT(runtime/references)
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr auto data(T & bar) noexcept // NOLINT(runtime/references)
|
||||||
{
|
{
|
||||||
return std::data(bar);
|
return std::data(bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pointer overload. Constness of `bar` is the constness of `T`.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto data(T * bar)
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr T * data(T * bar) noexcept
|
||||||
{
|
{
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
@ -1063,7 +1182,10 @@ template <typename T>
|
||||||
static constexpr bool is_tuple_v = is_tuple<T>::value;
|
static constexpr bool is_tuple_v = is_tuple<T>::value;
|
||||||
|
|
||||||
template <std::size_t I, typename T>
|
template <std::size_t I, typename T>
|
||||||
auto & get(T & t) // NOLINT(runtime/references)
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr auto & get(T & t) noexcept // NOLINT(runtime/references)
|
||||||
{
|
{
|
||||||
if constexpr(I == 0 && is_tuple_v<T> == false)
|
if constexpr(I == 0 && is_tuple_v<T> == false)
|
||||||
{
|
{
|
||||||
|
|
@ -1085,7 +1207,10 @@ template <typename T>
|
||||||
static constexpr bool is_bit_array_v = is_bit_array<T>::value;
|
static constexpr bool is_bit_array_v = is_bit_array<T>::value;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto size(const T & t)
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
constexpr auto size(const T & t) noexcept
|
||||||
{
|
{
|
||||||
if constexpr(is_bit_array_v<T> == false)
|
if constexpr(is_bit_array_v<T> == false)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,11 @@ struct wildcard_value
|
||||||
static_assert(std::numeric_limits<T>::is_iec559 ||
|
static_assert(std::numeric_limits<T>::is_iec559 ||
|
||||||
!(std::is_same_v<T, float> || std::is_same_v<T, double>),
|
!(std::is_same_v<T, float> || std::is_same_v<T, double>),
|
||||||
"floating point types only supported for iec559");
|
"floating point types only supported for iec559");
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr wildcard_value() noexcept : val{std::nullopt} { }
|
inline constexpr wildcard_value() noexcept : val{std::nullopt} { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr wildcard_value(const T & t) noexcept : val{t} { }
|
inline constexpr wildcard_value(const T & t) noexcept : val{t} { }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr wildcard_value(T && t) noexcept : val{std::move(t)} { }
|
inline constexpr wildcard_value(T && t) noexcept : val{std::move(t)} { }
|
||||||
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
|
@ -93,6 +96,7 @@ template <typename T> using concrete_type_t = typename concrete_type<T>::type;
|
||||||
|
|
||||||
template <typename T> struct concrete_value
|
template <typename T> struct concrete_value
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
constexpr std::optional<T> operator()(T y) const noexcept
|
constexpr std::optional<T> operator()(T y) const noexcept
|
||||||
|
|
@ -102,6 +106,7 @@ template <typename T> struct concrete_value
|
||||||
};
|
};
|
||||||
template <typename T> struct concrete_value<wildcard_value<T>>
|
template <typename T> struct concrete_value<wildcard_value<T>>
|
||||||
{
|
{
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
constexpr std::optional<T> operator()(wildcard_value<T>) const noexcept
|
constexpr std::optional<T> operator()(wildcard_value<T>) const noexcept
|
||||||
|
|
|
||||||
|
|
@ -49,13 +49,16 @@ struct xor_wrapper
|
||||||
constexpr xor_wrapper() = default;
|
constexpr xor_wrapper() = default;
|
||||||
|
|
||||||
/// @brief Copy c'tor
|
/// @brief Copy c'tor
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr xor_wrapper(const xor_wrapper &) noexcept = default;
|
constexpr xor_wrapper(const xor_wrapper &) noexcept = default;
|
||||||
|
|
||||||
/// @brief Move c'tor
|
/// @brief Move c'tor
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr xor_wrapper(xor_wrapper &&) noexcept = default;
|
constexpr xor_wrapper(xor_wrapper &&) noexcept = default;
|
||||||
|
|
||||||
/// @brief Value c'tor
|
/// @brief Value c'tor
|
||||||
// cppcheck-suppress noExplicitConstructor
|
// cppcheck-suppress noExplicitConstructor
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr xor_wrapper(value_type v) noexcept : value{v} { } // NOLINT(runtime/explicit)
|
constexpr xor_wrapper(value_type v) noexcept : value{v} { } // NOLINT(runtime/explicit)
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
@ -163,7 +166,7 @@ struct xor_wrapper
|
||||||
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
constexpr xor_wrapper & operator<<=(std::size_t amount)
|
constexpr xor_wrapper & operator<<=(std::size_t amount) noexcept
|
||||||
{
|
{
|
||||||
this->value <<= amount;
|
this->value <<= amount;
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -171,7 +174,7 @@ struct xor_wrapper
|
||||||
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
constexpr xor_wrapper & operator>>=(std::size_t amount)
|
constexpr xor_wrapper & operator>>=(std::size_t amount) noexcept
|
||||||
{
|
{
|
||||||
this->value >>= amount;
|
this->value >>= amount;
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -274,14 +277,14 @@ struct xor_wrapper
|
||||||
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
friend constexpr xor_wrapper operator<<(const xor_wrapper & val, std::size_t amount)
|
friend constexpr xor_wrapper operator<<(const xor_wrapper & val, std::size_t amount) noexcept
|
||||||
{
|
{
|
||||||
return xor_wrapper(val.value << amount);
|
return xor_wrapper(val.value << amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_NO_THROW
|
HEDLEY_NO_THROW
|
||||||
friend constexpr xor_wrapper operator>>(const xor_wrapper & val, std::size_t amount)
|
friend constexpr xor_wrapper operator>>(const xor_wrapper & val, std::size_t amount) noexcept
|
||||||
{
|
{
|
||||||
return xor_wrapper(val.value >> amount);
|
return xor_wrapper(val.value >> amount);
|
||||||
}
|
}
|
||||||
|
|
@ -670,217 +673,217 @@ constexpr static auto operator "" _x61(unsigned long long int x) { return dpf::x
|
||||||
constexpr static auto operator "" _x62(unsigned long long int x) { return dpf::xints::xint62_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _x62(unsigned long long int x) { return dpf::xints::xint62_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
constexpr static auto operator "" _x63(unsigned long long int x) { return dpf::xints::xint63_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _x63(unsigned long long int x) { return dpf::xints::xint63_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
constexpr static auto operator "" _x64(unsigned long long int x) { return dpf::xints::xint64_t{static_cast<psnip_uint64_t>(x)}; }
|
constexpr static auto operator "" _x64(unsigned long long int x) { return dpf::xints::xint64_t{static_cast<psnip_uint64_t>(x)}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint65_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint65_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint66_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint66_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint67_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint67_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint68_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint68_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint69_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint69_t{x}; }
|
||||||
// 70--79
|
// 70--79
|
||||||
template <char ...digits> constexpr static auto operator "" _x70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint70_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint70_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint71_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint71_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint72_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint72_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint73_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint73_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint74_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint74_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint75_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint75_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint76_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint76_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint77_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint77_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint78_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint78_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint79_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint79_t{x}; }
|
||||||
// 80--89
|
// 80--89
|
||||||
template <char ...digits> constexpr static auto operator "" _x80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint80_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint80_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint81_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint81_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint82_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint82_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint83_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint83_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint84_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint84_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint85_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint85_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint86_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint86_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint87_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint87_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint88_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint88_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint89_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint89_t{x}; }
|
||||||
// 90--99
|
// 90--99
|
||||||
template <char ...digits> constexpr static auto operator "" _x90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint90_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint90_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint91_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint91_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint92_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint92_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint93_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint93_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint94_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint94_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint95_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint95_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint96_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint96_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint97_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint97_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint98_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint98_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint99_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint99_t{x}; }
|
||||||
// 100--109
|
// 100--109
|
||||||
template <char ...digits> constexpr static auto operator "" _x100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint100_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint100_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint101_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint101_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint102_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint102_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint103_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint103_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint104_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint104_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint105_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint105_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint106_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint106_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint107_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint107_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint108_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint108_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint109_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint109_t{x}; }
|
||||||
// 110--119
|
// 110--119
|
||||||
template <char ...digits> constexpr static auto operator "" _x110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint110_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint110_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint111_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint111_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint112_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint112_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint113_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint113_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint114_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint114_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint115_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint115_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint116_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint116_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint117_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint117_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint118_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint118_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint119_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint119_t{x}; }
|
||||||
// 120--129
|
// 120--129
|
||||||
template <char ...digits> constexpr static auto operator "" _x120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint120_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint120_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint121_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint121_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint122_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint122_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint123_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint123_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint124_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint124_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint125_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint125_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint126_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint126_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint127_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint127_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint128_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint128_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint129_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint129_t{x}; }
|
||||||
// 130--139
|
// 130--139
|
||||||
template <char ...digits> constexpr static auto operator "" _x130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint130_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint130_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint131_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint131_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint132_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint132_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint133_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint133_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint134_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint134_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint135_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint135_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint136_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint136_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint137_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint137_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint138_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint138_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint139_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint139_t{x}; }
|
||||||
// 140--149
|
// 140--149
|
||||||
template <char ...digits> constexpr static auto operator "" _x140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint140_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint140_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint141_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint141_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint142_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint142_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint143_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint143_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint144_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint144_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint145_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint145_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint146_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint146_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint147_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint147_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint148_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint148_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint149_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint149_t{x}; }
|
||||||
// 150--159
|
// 150--159
|
||||||
template <char ...digits> constexpr static auto operator "" _x150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint150_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint150_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint151_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint151_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint152_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint152_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint153_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint153_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint154_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint154_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint155_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint155_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint156_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint156_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint157_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint157_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint158_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint158_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint159_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint159_t{x}; }
|
||||||
// 160--169
|
// 160--169
|
||||||
template <char ...digits> constexpr static auto operator "" _x160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint160_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint160_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint161_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint161_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint162_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint162_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint163_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint163_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint164_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint164_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint165_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint165_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint166_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint166_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint167_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint167_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint168_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint168_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint169_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint169_t{x}; }
|
||||||
// 170--179
|
// 170--179
|
||||||
template <char ...digits> constexpr static auto operator "" _x170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint170_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint170_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint171_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint171_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint172_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint172_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint173_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint173_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint174_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint174_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint175_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint175_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint176_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint176_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint177_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint177_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint178_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint178_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint179_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint179_t{x}; }
|
||||||
// 180--189
|
// 180--189
|
||||||
template <char ...digits> constexpr static auto operator "" _x180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint180_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint180_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint181_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint181_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint182_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint182_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint183_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint183_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint184_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint184_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint185_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint185_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint186_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint186_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint187_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint187_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint188_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint188_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint189_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint189_t{x}; }
|
||||||
// 190--199
|
// 190--199
|
||||||
template <char ...digits> constexpr static auto operator "" _x190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint190_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint190_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint191_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint191_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint192_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint192_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint193_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint193_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint194_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint194_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint195_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint195_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint196_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint196_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint197_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint197_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint198_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint198_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint199_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint199_t{x}; }
|
||||||
// 200--209
|
// 200--209
|
||||||
template <char ...digits> constexpr static auto operator "" _x200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint200_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint200_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint201_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint201_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint202_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint202_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint203_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint203_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint204_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint204_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint205_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint205_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint206_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint206_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint207_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint207_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint208_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint208_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint209_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint209_t{x}; }
|
||||||
// 210--219
|
// 210--219
|
||||||
template <char ...digits> constexpr static auto operator "" _x210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint210_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint210_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint211_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint211_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint212_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint212_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint213_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint213_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint214_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint214_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint215_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint215_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint216_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint216_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint217_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint217_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint218_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint218_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint219_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint219_t{x}; }
|
||||||
// 220--229
|
// 220--229
|
||||||
template <char ...digits> constexpr static auto operator "" _x220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint220_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint220_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint221_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint221_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint222_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint222_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint223_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint223_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint224_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint224_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint225_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint225_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint226_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint226_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint227_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint227_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint228_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint228_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint229_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint229_t{x}; }
|
||||||
// 230--239
|
// 230--239
|
||||||
template <char ...digits> constexpr static auto operator "" _x230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint230_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint230_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint231_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint231_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint232_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint232_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint233_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint233_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint234_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint234_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint235_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint235_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint236_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint236_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint237_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint237_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint238_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint238_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint239_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint239_t{x}; }
|
||||||
// 240--249
|
// 240--249
|
||||||
template <char ...digits> constexpr static auto operator "" _x240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint240_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint240_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint241_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint241_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint242_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint242_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint243_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint243_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint244_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint244_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint245_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint245_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint246_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint246_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint247_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint247_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint248_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint248_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint249_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint249_t{x}; }
|
||||||
// 250--259
|
// 250--259
|
||||||
template <char ...digits> constexpr static auto operator "" _x250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint250_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint250_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint251_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint251_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint252_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint252_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint253_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint253_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint254_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint254_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint255_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint255_t{x}; }
|
||||||
template <char ...digits> constexpr static auto operator "" _x256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint256_t{x}; }
|
template <char ...digits> constexpr static auto operator "" _x256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint256_t{x}; }
|
||||||
|
|
||||||
} // namespace xints
|
} // namespace xints
|
||||||
|
|
||||||
|
|
@ -929,6 +932,7 @@ template <typename T>
|
||||||
struct mod_pow_2<xor_wrapper<T>>
|
struct mod_pow_2<xor_wrapper<T>>
|
||||||
{
|
{
|
||||||
static constexpr auto mod = mod_pow_2<T>{};
|
static constexpr auto mod = mod_pow_2<T>{};
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(xor_wrapper<T> val, std::size_t n) const noexcept
|
std::size_t operator()(xor_wrapper<T> val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod(val.value, n);
|
return mod(val.value, n);
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ struct zip_iterator
|
||||||
|
|
||||||
template <typename ...Iterables>
|
template <typename ...Iterables>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
zip_iterable<Iterables...> tuple_as_zip(std::tuple<Iterables...> & tup) noexcept
|
zip_iterable<Iterables...> tuple_as_zip(std::tuple<Iterables...> & tup) noexcept
|
||||||
{
|
{
|
||||||
return zip_iterable<Iterables...>(
|
return zip_iterable<Iterables...>(
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include "grotto/fixedpoint.hpp"
|
#include "grotto/fixedpoint.hpp"
|
||||||
|
|
||||||
|
#include "grotto/nmod.hpp"
|
||||||
|
|
||||||
#include "grotto/gadget_hints.hpp"
|
#include "grotto/gadget_hints.hpp"
|
||||||
|
|
||||||
#include "grotto/gadgets.hpp"
|
#include "grotto/gadgets.hpp"
|
||||||
|
|
@ -24,6 +26,8 @@
|
||||||
|
|
||||||
#include "grotto/principal_lut.hpp"
|
#include "grotto/principal_lut.hpp"
|
||||||
|
|
||||||
|
#include "grotto/range_lut.hpp"
|
||||||
|
|
||||||
#include "grotto/window_lut.hpp"
|
#include "grotto/window_lut.hpp"
|
||||||
|
|
||||||
#include "grotto/prefix_parity.hpp"
|
#include "grotto/prefix_parity.hpp"
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
@ -57,11 +59,13 @@ struct constant_lut
|
||||||
/// `bounds[i + 1]` or one past `numeric_limits<Raw>::max()` for the last piece.
|
/// `bounds[i + 1]` or one past `numeric_limits<Raw>::max()` for the last piece.
|
||||||
std::vector<std::int64_t> values;
|
std::vector<std::int64_t> values;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t linear_parts() const noexcept { return values.size(); }
|
std::size_t linear_parts() const noexcept { return values.size(); }
|
||||||
|
|
||||||
/// Pieces after joining the first and last when they carry the same value.
|
/// Pieces after joining the first and last when they carry the same value.
|
||||||
/// Those two meet across the signed wrap, which is how the paper counts
|
/// Those two meet across the signed wrap, which is how the paper counts
|
||||||
/// parts for `zero` and `nonzero` (2, not 3).
|
/// parts for `zero` and `nonzero` (2, not 3).
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t wrapped_parts() const noexcept
|
std::size_t wrapped_parts() const noexcept
|
||||||
{
|
{
|
||||||
if (values.size() >= 2 && values.front() == values.back())
|
if (values.size() >= 2 && values.front() == values.back())
|
||||||
|
|
@ -69,6 +73,7 @@ struct constant_lut
|
||||||
return values.size();
|
return values.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::int64_t operator()(Raw x) const noexcept
|
std::int64_t operator()(Raw x) const noexcept
|
||||||
{
|
{
|
||||||
const auto it = std::upper_bound(bounds.begin(), bounds.end(), x);
|
const auto it = std::upper_bound(bounds.begin(), bounds.end(), x);
|
||||||
|
|
@ -86,6 +91,7 @@ namespace detail
|
||||||
using u128 = unsigned __int128;
|
using u128 = unsigned __int128;
|
||||||
|
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr u128 magnitude(Raw raw) noexcept
|
constexpr u128 magnitude(Raw raw) noexcept
|
||||||
{
|
{
|
||||||
if (raw >= 0)
|
if (raw >= 0)
|
||||||
|
|
@ -95,6 +101,7 @@ constexpr u128 magnitude(Raw raw) noexcept
|
||||||
return static_cast<u128>(-static_cast<__int128>(raw));
|
return static_cast<u128>(-static_cast<__int128>(raw));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr int floor_log2(u128 mag) noexcept
|
constexpr int floor_log2(u128 mag) noexcept
|
||||||
{
|
{
|
||||||
if (mag <= std::uint64_t(-1))
|
if (mag <= std::uint64_t(-1))
|
||||||
|
|
@ -102,6 +109,7 @@ constexpr int floor_log2(u128 mag) noexcept
|
||||||
return 127 - __builtin_clzll(static_cast<std::uint64_t>(mag >> 64));
|
return 127 - __builtin_clzll(static_cast<std::uint64_t>(mag >> 64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool shift_fits(u128 value, unsigned shift) noexcept
|
constexpr bool shift_fits(u128 value, unsigned shift) noexcept
|
||||||
{
|
{
|
||||||
return shift < 128 && value <= (~u128{0} >> shift);
|
return shift < 128 && value <= (~u128{0} >> shift);
|
||||||
|
|
@ -131,6 +139,7 @@ inline constexpr std::uint64_t pow10[] = {
|
||||||
10000000000000000000ull,
|
10000000000000000000ull,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexcept
|
inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
if (mag == 0 || fractional_bits >= 128)
|
if (mag == 0 || fractional_bits >= 128)
|
||||||
|
|
@ -151,6 +160,7 @@ inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexce
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Smallest positive magnitude whose base-10 log is at least `k`.
|
/// Smallest positive magnitude whose base-10 log is at least `k`.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline u128 first_magnitude_at_least_pow10(int k, unsigned fractional_bits) noexcept
|
inline u128 first_magnitude_at_least_pow10(int k, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
if (fractional_bits >= 128)
|
if (fractional_bits >= 128)
|
||||||
|
|
@ -253,6 +263,7 @@ struct sign_program
|
||||||
static constexpr std::int64_t canonical[] = { Neg, Zero, Pos };
|
static constexpr std::int64_t canonical[] = { Neg, Zero, Pos };
|
||||||
|
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned) noexcept
|
static std::int64_t eval(Raw raw, unsigned) noexcept
|
||||||
{
|
{
|
||||||
if (raw < 0)
|
if (raw < 0)
|
||||||
|
|
@ -310,6 +321,7 @@ template <>
|
||||||
struct exact_lut<exact_constant::ilogb>
|
struct exact_lut<exact_constant::ilogb>
|
||||||
{
|
{
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
const detail::u128 mag = detail::magnitude(raw);
|
const detail::u128 mag = detail::magnitude(raw);
|
||||||
|
|
@ -341,6 +353,7 @@ template <>
|
||||||
struct exact_lut<exact_constant::ceil_ilogb>
|
struct exact_lut<exact_constant::ceil_ilogb>
|
||||||
{
|
{
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
const detail::u128 mag = detail::magnitude(raw);
|
const detail::u128 mag = detail::magnitude(raw);
|
||||||
|
|
@ -379,6 +392,7 @@ template <>
|
||||||
struct exact_lut<exact_constant::ilog10>
|
struct exact_lut<exact_constant::ilog10>
|
||||||
{
|
{
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
const detail::u128 mag = detail::magnitude(raw);
|
const detail::u128 mag = detail::magnitude(raw);
|
||||||
|
|
@ -416,6 +430,7 @@ template <>
|
||||||
struct exact_lut<exact_constant::clz>
|
struct exact_lut<exact_constant::clz>
|
||||||
{
|
{
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
if (raw < 0)
|
if (raw < 0)
|
||||||
|
|
@ -452,6 +467,7 @@ template <>
|
||||||
struct exact_lut<exact_constant::clrsb>
|
struct exact_lut<exact_constant::clrsb>
|
||||||
{
|
{
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
const detail::u128 mag = detail::magnitude(raw);
|
const detail::u128 mag = detail::magnitude(raw);
|
||||||
|
|
@ -559,6 +575,7 @@ enum class threshold_cmp
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Raw>
|
template <typename Raw>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::int64_t evaluate_threshold(Raw raw, Raw bound, threshold_cmp kind) noexcept
|
std::int64_t evaluate_threshold(Raw raw, Raw bound, threshold_cmp kind) noexcept
|
||||||
{
|
{
|
||||||
switch (kind)
|
switch (kind)
|
||||||
|
|
|
||||||
433
include/grotto/dyadic_lut.hpp
Normal file
433
include/grotto/dyadic_lut.hpp
Normal file
|
|
@ -0,0 +1,433 @@
|
||||||
|
/// @file grotto/dyadic_lut.hpp
|
||||||
|
/// @brief Exact dyadic step functions from the Grotto gadget list.
|
||||||
|
/// @details Integer results and boolean 1s are raw fixed-point values:
|
||||||
|
/// an integer n is stored as `n << fractional_bits`. `ilogb(0)` and
|
||||||
|
/// `ilog10(0)` return `ilog_of_zero`.
|
||||||
|
///
|
||||||
|
/// `make_msb_lut(i)` is bit `i` counting from the most significant
|
||||||
|
/// bit. It is constant on `2^{i+1}` intervals, so `i` must be less
|
||||||
|
/// than `msb_bit_limit`.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_GROTTO_DYADIC_LUT_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_GROTTO_DYADIC_LUT_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
|
#include "grotto/easy_lut.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace grotto
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Sentinel raw value for `ilogb(0)` and `ilog10(0)`.
|
||||||
|
inline constexpr std::int64_t ilog_of_zero =
|
||||||
|
std::numeric_limits<std::int64_t>::min();
|
||||||
|
|
||||||
|
/// `make_msb_lut(i)` allows `i` in `[0, msb_bit_limit)`.
|
||||||
|
/// Bit 0 is two intervals; bit 7 is 256.
|
||||||
|
inline constexpr unsigned msb_bit_limit = 8;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr int raw_width() noexcept
|
||||||
|
{
|
||||||
|
return std::numeric_limits<Raw>::digits + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t encode_units(std::int64_t units, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
if (fractional_bits >= 63)
|
||||||
|
throw std::invalid_argument("dyadic lut: fractional width does not fit");
|
||||||
|
const __int128 scaled = static_cast<__int128>(units) << fractional_bits;
|
||||||
|
if (scaled > std::numeric_limits<std::int64_t>::max()
|
||||||
|
|| scaled < std::numeric_limits<std::int64_t>::min())
|
||||||
|
throw std::overflow_error("dyadic lut: encoded value does not fit int64");
|
||||||
|
return static_cast<std::int64_t>(scaled);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline easy_poly unit_poly(std::int64_t units, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return easy_poly{encode_units(units, fractional_bits), 0, 0, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline easy_poly indicator_poly(bool on, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return unit_poly(on ? 1 : 0, fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr std::uint64_t raw_bits(std::int64_t raw) noexcept
|
||||||
|
{
|
||||||
|
constexpr int width = raw_width<Raw>();
|
||||||
|
const auto masked = static_cast<std::uint64_t>(raw);
|
||||||
|
if constexpr (width >= 64)
|
||||||
|
return masked;
|
||||||
|
else
|
||||||
|
return masked & ((std::uint64_t{1} << width) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int countl_zero_width(std::uint64_t bits, int width)
|
||||||
|
{
|
||||||
|
if (width <= 0 || width > 64)
|
||||||
|
throw std::invalid_argument("dyadic lut: width must be 1..64");
|
||||||
|
if (width < 64)
|
||||||
|
bits &= (std::uint64_t{1} << width) - 1;
|
||||||
|
if (bits == 0)
|
||||||
|
return width;
|
||||||
|
return __builtin_clzll(bits) - (64 - width);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int countl_one_width(std::uint64_t bits, int width)
|
||||||
|
{
|
||||||
|
const std::uint64_t flipped = width >= 64 ? ~bits : (~bits & ((std::uint64_t{1} << width) - 1));
|
||||||
|
return countl_zero_width(flipped, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
int clz_of(std::int64_t raw)
|
||||||
|
{
|
||||||
|
return countl_zero_width(raw_bits<Raw>(raw), raw_width<Raw>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
int clrsb_of(std::int64_t raw)
|
||||||
|
{
|
||||||
|
constexpr int width = raw_width<Raw>();
|
||||||
|
const std::uint64_t bits = raw_bits<Raw>(raw);
|
||||||
|
const bool neg = ((bits >> (width - 1)) & 1u) != 0;
|
||||||
|
const int matched = neg ? countl_one_width(bits, width)
|
||||||
|
: countl_zero_width(bits, width);
|
||||||
|
return matched - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr int floor_log2_u128(u128 mag) noexcept
|
||||||
|
{
|
||||||
|
if (mag == 0)
|
||||||
|
return -1;
|
||||||
|
int n = 0;
|
||||||
|
while (mag > 1)
|
||||||
|
{
|
||||||
|
mag >>= 1;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr u128 magnitude(std::int64_t raw) noexcept
|
||||||
|
{
|
||||||
|
using lim = std::numeric_limits<Raw>;
|
||||||
|
if (raw == static_cast<std::int64_t>(lim::min()))
|
||||||
|
return u128{1} << (raw_width<Raw>() - 1);
|
||||||
|
const auto abs = raw < 0 ? -raw : raw;
|
||||||
|
return static_cast<u128>(abs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr int ilogb_units(std::int64_t raw, unsigned fractional_bits) noexcept
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
return floor_log2_u128(magnitude<Raw>(raw)) - static_cast<int>(fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u128 pow10_u128(int exponent)
|
||||||
|
{
|
||||||
|
if (exponent < 0 || exponent > 38)
|
||||||
|
throw std::invalid_argument("dyadic lut: power of ten is out of range");
|
||||||
|
u128 p = 1;
|
||||||
|
for (int i = 0; i < exponent; ++i)
|
||||||
|
p *= 10;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `mag / 2^k >= 10^e`.
|
||||||
|
inline bool magnitude_ge_pow10(u128 mag, unsigned fractional_bits, int exponent)
|
||||||
|
{
|
||||||
|
if (mag == 0)
|
||||||
|
return false;
|
||||||
|
if (exponent >= 0)
|
||||||
|
{
|
||||||
|
const u128 decade = pow10_u128(exponent);
|
||||||
|
if (fractional_bits > 0 && decade > (~u128{0} >> fractional_bits))
|
||||||
|
return false;
|
||||||
|
return mag >= (decade << fractional_bits);
|
||||||
|
}
|
||||||
|
const u128 decade = pow10_u128(-exponent);
|
||||||
|
const u128 scale = u128{1} << fractional_bits;
|
||||||
|
u128 threshold = scale / decade;
|
||||||
|
if (scale % decade != 0)
|
||||||
|
++threshold;
|
||||||
|
return mag >= threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
int ilog10_units(std::int64_t raw, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
const u128 mag = magnitude<Raw>(raw);
|
||||||
|
int lo = -static_cast<int>(fractional_bits) - 2;
|
||||||
|
int hi = raw_width<Raw>();
|
||||||
|
while (lo < hi)
|
||||||
|
{
|
||||||
|
const int mid = lo + (hi - lo + 1) / 2;
|
||||||
|
if (magnitude_ge_pow10(mag, fractional_bits, mid))
|
||||||
|
lo = mid;
|
||||||
|
else
|
||||||
|
hi = mid - 1;
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw, typename At>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> steps_from_cuts(std::vector<std::int64_t> cuts, At && at)
|
||||||
|
{
|
||||||
|
return assemble_easy<Raw>(std::move(cuts), [&](std::int64_t raw) {
|
||||||
|
return at(raw);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> predicate_lut(unsigned fractional_bits, bool at_or_below_zero,
|
||||||
|
bool at_zero, bool above_zero)
|
||||||
|
{
|
||||||
|
std::vector<std::int64_t> cuts{0, 1};
|
||||||
|
return steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
const bool on = raw < 0 ? at_or_below_zero : (raw == 0 ? at_zero : above_zero);
|
||||||
|
return indicator_poly(on, fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_positive_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_negative_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, true, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_nonnegative_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, false, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_nonpositive_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_zero_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, false, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_nonzero_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
return detail::predicate_lut<Raw>(fractional_bits, true, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_signum_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
const std::int64_t one = detail::encode_units(1, fractional_bits);
|
||||||
|
return detail::steps_from_cuts<Raw>({0, 1}, [=](std::int64_t raw) {
|
||||||
|
if (raw < 0)
|
||||||
|
return detail::easy_poly{-one, 0, 0, 1};
|
||||||
|
if (raw == 0)
|
||||||
|
return detail::kZero;
|
||||||
|
return detail::easy_poly{one, 0, 0, 1};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_clz_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
constexpr int width = detail::raw_width<Raw>();
|
||||||
|
std::vector<std::int64_t> cuts{0, 1};
|
||||||
|
for (int b = 1; b <= width - 2; ++b)
|
||||||
|
cuts.push_back(std::int64_t{1} << b);
|
||||||
|
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
return detail::unit_poly(detail::clz_of<Raw>(raw), fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_clrsb_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
constexpr int width = detail::raw_width<Raw>();
|
||||||
|
std::vector<std::int64_t> cuts{0, -1, -2};
|
||||||
|
for (int exp = 0; exp <= width - 2; ++exp)
|
||||||
|
cuts.push_back(std::int64_t{1} << exp);
|
||||||
|
for (int exp = 2; exp <= width - 1; ++exp)
|
||||||
|
{
|
||||||
|
if (exp >= 63)
|
||||||
|
cuts.push_back(static_cast<std::int64_t>(std::numeric_limits<Raw>::min()));
|
||||||
|
else
|
||||||
|
cuts.push_back(-(std::int64_t{1} << exp));
|
||||||
|
}
|
||||||
|
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
return detail::unit_poly(detail::clrsb_of<Raw>(raw), fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_ilogb_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
constexpr int width = detail::raw_width<Raw>();
|
||||||
|
std::vector<std::int64_t> cuts{0, 1};
|
||||||
|
for (int b = 1; b <= width - 2; ++b)
|
||||||
|
cuts.push_back(std::int64_t{1} << b);
|
||||||
|
for (int exp = 1; exp <= width - 1; ++exp)
|
||||||
|
{
|
||||||
|
if (exp >= width - 1)
|
||||||
|
cuts.push_back(static_cast<std::int64_t>(std::numeric_limits<Raw>::min()) + 1);
|
||||||
|
else
|
||||||
|
cuts.push_back(-(std::int64_t{1} << exp) + 1);
|
||||||
|
}
|
||||||
|
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
if (raw == 0)
|
||||||
|
return detail::easy_poly{ilog_of_zero, 0, 0, 1};
|
||||||
|
return detail::unit_poly(detail::ilogb_units<Raw>(raw, fractional_bits),
|
||||||
|
fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_ilog10_lut(unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
using lim = std::numeric_limits<Raw>;
|
||||||
|
const std::int64_t maxv = static_cast<std::int64_t>(lim::max());
|
||||||
|
const int lo = detail::ilog10_units<Raw>(1, fractional_bits);
|
||||||
|
const int hi_pos = detail::ilog10_units<Raw>(maxv, fractional_bits);
|
||||||
|
const int hi_neg = detail::ilog10_units<Raw>(static_cast<std::int64_t>(lim::min()),
|
||||||
|
fractional_bits);
|
||||||
|
const int hi = hi_pos > hi_neg ? hi_pos : hi_neg;
|
||||||
|
|
||||||
|
std::vector<std::int64_t> cuts{0, 1};
|
||||||
|
std::int64_t prev = 0;
|
||||||
|
for (int e = lo; e <= hi; ++e)
|
||||||
|
{
|
||||||
|
std::int64_t left = 1;
|
||||||
|
std::int64_t right = maxv;
|
||||||
|
while (left < right)
|
||||||
|
{
|
||||||
|
const std::int64_t mid = left + (right - left) / 2;
|
||||||
|
if (detail::ilog10_units<Raw>(mid, fractional_bits) >= e)
|
||||||
|
right = mid;
|
||||||
|
else
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
if (left == prev)
|
||||||
|
continue;
|
||||||
|
prev = left;
|
||||||
|
cuts.push_back(left);
|
||||||
|
if (left < maxv)
|
||||||
|
{
|
||||||
|
std::int64_t end = left;
|
||||||
|
std::int64_t scan_left = left;
|
||||||
|
std::int64_t scan_right = maxv;
|
||||||
|
while (scan_left < scan_right)
|
||||||
|
{
|
||||||
|
const std::int64_t mid = scan_left + (scan_right - scan_left + 1) / 2;
|
||||||
|
if (detail::ilog10_units<Raw>(mid, fractional_bits) == e)
|
||||||
|
scan_left = mid;
|
||||||
|
else
|
||||||
|
scan_right = mid - 1;
|
||||||
|
}
|
||||||
|
end = scan_left;
|
||||||
|
const std::int64_t neg = -end;
|
||||||
|
if (neg > static_cast<std::int64_t>(lim::min()))
|
||||||
|
cuts.push_back(neg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
if (raw == 0)
|
||||||
|
return detail::easy_poly{ilog_of_zero, 0, 0, 1};
|
||||||
|
return detail::unit_poly(detail::ilog10_units<Raw>(raw, fractional_bits),
|
||||||
|
fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bit `index` counting down from the most significant bit of `Raw`.
|
||||||
|
/// Index 0 is the sign bit. Larger indexes are refused: the bit is constant
|
||||||
|
/// on `2^{index+1}` intervals.
|
||||||
|
template <typename Raw>
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
easy_lut<Raw> make_msb_lut(unsigned index, unsigned fractional_bits = 0)
|
||||||
|
{
|
||||||
|
constexpr int width = detail::raw_width<Raw>();
|
||||||
|
if (index >= msb_bit_limit || static_cast<int>(index) >= width)
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"msb lut: only the most significant bits are piecewise-cheap");
|
||||||
|
const int shift = width - 1 - static_cast<int>(index);
|
||||||
|
if (shift >= 63)
|
||||||
|
{
|
||||||
|
return detail::steps_from_cuts<Raw>({0}, [=](std::int64_t raw) {
|
||||||
|
const bool on = ((detail::raw_bits<Raw>(raw) >> shift) & 1u) != 0;
|
||||||
|
return detail::indicator_poly(on, fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const std::int64_t step = std::int64_t{1} << shift;
|
||||||
|
std::vector<std::int64_t> cuts;
|
||||||
|
const auto minv = static_cast<std::int64_t>(std::numeric_limits<Raw>::min());
|
||||||
|
for (std::int64_t boundary = minv; ; )
|
||||||
|
{
|
||||||
|
cuts.push_back(boundary);
|
||||||
|
if (boundary > static_cast<std::int64_t>(std::numeric_limits<Raw>::max()) - step)
|
||||||
|
break;
|
||||||
|
boundary += step;
|
||||||
|
}
|
||||||
|
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||||
|
const bool on = ((detail::raw_bits<Raw>(raw) >> shift) & 1u) != 0;
|
||||||
|
return detail::indicator_poly(on, fractional_bits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace grotto
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_GROTTO_DYADIC_LUT_HPP__
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
@ -35,6 +37,7 @@ struct easy_lut
|
||||||
std::vector<std::int64_t> c2;
|
std::vector<std::int64_t> c2;
|
||||||
std::vector<std::int64_t> den;
|
std::vector<std::int64_t> den;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t parts() const noexcept { return c0.size(); }
|
std::size_t parts() const noexcept { return c0.size(); }
|
||||||
|
|
||||||
std::int64_t operator()(Raw x) const
|
std::int64_t operator()(Raw x) const
|
||||||
|
|
@ -82,6 +85,7 @@ struct easy_poly
|
||||||
std::int64_t den = 1;
|
std::int64_t den = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline bool operator==(easy_poly a, easy_poly b) noexcept
|
inline bool operator==(easy_poly a, easy_poly b) noexcept
|
||||||
{
|
{
|
||||||
return a.c0 == b.c0 && a.c1 == b.c1 && a.c2 == b.c2 && a.den == b.den;
|
return a.c0 == b.c0 && a.c1 == b.c1 && a.c2 == b.c2 && a.den == b.den;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ namespace detail
|
||||||
|
|
||||||
/// @brief Integer value of an already-rounded finite double, as a 256-bit word.
|
/// @brief Integer value of an already-rounded finite double, as a 256-bit word.
|
||||||
/// Values that do not fit saturate to all-ones.
|
/// Values that do not fit saturate to all-ones.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline uint256_t uint256_from_rounded_double(double rounded) noexcept
|
inline uint256_t uint256_from_rounded_double(double rounded) noexcept
|
||||||
{
|
{
|
||||||
if (!(rounded > 0.0) || !std::isfinite(rounded))
|
if (!(rounded > 0.0) || !std::isfinite(rounded))
|
||||||
|
|
@ -99,6 +100,7 @@ struct is_static_castable<To, From,
|
||||||
|
|
||||||
/// Low `bits` of `wide`, saturated to all-ones when `wide` does not fit.
|
/// Low `bits` of `wide`, saturated to all-ones when `wide` does not fit.
|
||||||
template <typename Raw, std::size_t Bits>
|
template <typename Raw, std::size_t Bits>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
Raw saturate_low_bits(uint256_t wide) noexcept
|
Raw saturate_low_bits(uint256_t wide) noexcept
|
||||||
{
|
{
|
||||||
static_assert(Bits > 0 && Bits <= 256);
|
static_assert(Bits > 0 && Bits <= 256);
|
||||||
|
|
@ -151,6 +153,7 @@ Raw saturate_low_bits(uint256_t wide) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntegralType>
|
template <typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline IntegralType rounded_double_to_integral(double rounded) noexcept
|
inline IntegralType rounded_double_to_integral(double rounded) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_integral_v<IntegralType>
|
if constexpr (std::is_integral_v<IntegralType>
|
||||||
|
|
@ -206,6 +209,7 @@ template <typename IntegralType,
|
||||||
typename T>
|
typename T>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralType scale_integer_to_fixed_raw(T integer_value) noexcept
|
constexpr IntegralType scale_integer_to_fixed_raw(T integer_value) noexcept
|
||||||
{
|
{
|
||||||
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||||||
|
|
@ -228,6 +232,7 @@ inline constexpr bool is_signed_rep_v =
|
||||||
template <typename IntegralType>
|
template <typename IntegralType>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralType raw_neg(IntegralType x) noexcept
|
constexpr IntegralType raw_neg(IntegralType x) noexcept
|
||||||
{
|
{
|
||||||
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||||||
|
|
@ -238,6 +243,7 @@ constexpr IntegralType raw_neg(IntegralType x) noexcept
|
||||||
template <typename IntegralType>
|
template <typename IntegralType>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralType raw_abs(IntegralType x) noexcept
|
constexpr IntegralType raw_abs(IntegralType x) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (is_signed_rep_v<IntegralType>)
|
if constexpr (is_signed_rep_v<IntegralType>)
|
||||||
|
|
@ -252,6 +258,7 @@ constexpr IntegralType raw_abs(IntegralType x) noexcept
|
||||||
template <typename IntegralType>
|
template <typename IntegralType>
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_CONST
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
||||||
{
|
{
|
||||||
if (b == IntegralType{})
|
if (b == IntegralType{})
|
||||||
|
|
@ -265,6 +272,7 @@ constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
||||||
|
|
||||||
template <unsigned FractionalBits,
|
template <unsigned FractionalBits,
|
||||||
typename IntegralType>
|
typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept;
|
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept;
|
||||||
|
|
||||||
/// @tparam FractionalBits Number of fractional bits used in the fixed-point
|
/// @tparam FractionalBits Number of fractional bits used in the fixed-point
|
||||||
|
|
@ -378,6 +386,7 @@ public:
|
||||||
~fixedpoint() = default;
|
~fixedpoint() = default;
|
||||||
|
|
||||||
/// @brief Cast to `double`
|
/// @brief Cast to `double`
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
explicit constexpr operator double() const noexcept
|
explicit constexpr operator double() const noexcept
|
||||||
|
|
@ -728,6 +737,7 @@ public:
|
||||||
template <unsigned FractionalBits,
|
template <unsigned FractionalBits,
|
||||||
typename IntegralType,
|
typename IntegralType,
|
||||||
typename Mask>
|
typename Mask>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool operator&(const Mask & mask,
|
constexpr bool operator&(const Mask & mask,
|
||||||
const fixedpoint<FractionalBits, IntegralType> & x) noexcept
|
const fixedpoint<FractionalBits, IntegralType> & x) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -739,6 +749,7 @@ template <class CharT,
|
||||||
class Traits,
|
class Traits,
|
||||||
unsigned FractionalBits,
|
unsigned FractionalBits,
|
||||||
typename IntegralType>
|
typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::basic_ostream<CharT, Traits> &
|
std::basic_ostream<CharT, Traits> &
|
||||||
operator<<(std::basic_ostream<CharT, Traits> & os,
|
operator<<(std::basic_ostream<CharT, Traits> & os,
|
||||||
const fixedpoint<FractionalBits, IntegralType> & f) noexcept
|
const fixedpoint<FractionalBits, IntegralType> & f) noexcept
|
||||||
|
|
@ -762,6 +773,7 @@ operator>>(std::basic_istream<CharT, Traits> & is,
|
||||||
|
|
||||||
template <unsigned FractionalBits,
|
template <unsigned FractionalBits,
|
||||||
typename IntegralType>
|
typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept
|
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept
|
||||||
{
|
{
|
||||||
return fixedpoint<FractionalBits, IntegralType>::from_raw(value);
|
return fixedpoint<FractionalBits, IntegralType>::from_raw(value);
|
||||||
|
|
@ -811,6 +823,7 @@ static auto make_fixed_safe(double d)
|
||||||
template <unsigned ToFractionalBits,
|
template <unsigned ToFractionalBits,
|
||||||
unsigned FromFractionalBits,
|
unsigned FromFractionalBits,
|
||||||
typename IntegralType>
|
typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType> & f) noexcept
|
constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType> & f) noexcept
|
||||||
{
|
{
|
||||||
auto value = f.integral_representation();
|
auto value = f.integral_representation();
|
||||||
|
|
@ -826,6 +839,7 @@ constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType>
|
||||||
|
|
||||||
template <unsigned FractionalBits,
|
template <unsigned FractionalBits,
|
||||||
typename IntegralType>
|
typename IntegralType>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr auto precision_of(fixedpoint<FractionalBits, IntegralType>) noexcept
|
static constexpr auto precision_of(fixedpoint<FractionalBits, IntegralType>) noexcept
|
||||||
{
|
{
|
||||||
return FractionalBits;
|
return FractionalBits;
|
||||||
|
|
@ -1466,6 +1480,7 @@ struct countl_zero_symmetric_difference<grotto::fixedpoint<FractionalBits, Integ
|
||||||
using T = grotto::fixedpoint<FractionalBits, IntegralType>;
|
using T = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||||
static constexpr auto clz = dpf::utils::countl_zero_symmetric_difference<typename T::integral_type>{};
|
static constexpr auto clz = dpf::utils::countl_zero_symmetric_difference<typename T::integral_type>{};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
HEDLEY_PURE
|
HEDLEY_PURE
|
||||||
HEDLEY_ALWAYS_INLINE
|
HEDLEY_ALWAYS_INLINE
|
||||||
constexpr std::size_t operator()(const T & lhs, const T & rhs) const noexcept
|
constexpr std::size_t operator()(const T & lhs, const T & rhs) const noexcept
|
||||||
|
|
@ -1499,6 +1514,7 @@ struct mod_pow_2<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||||||
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||||
static constexpr auto mod = mod_pow_2<IntegralType>{};
|
static constexpr auto mod = mod_pow_2<IntegralType>{};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::size_t operator()(fixed_type val, std::size_t n) const noexcept
|
std::size_t operator()(fixed_type val, std::size_t n) const noexcept
|
||||||
{
|
{
|
||||||
return mod(val.integral_representation(), n);
|
return mod(val.integral_representation(), n);
|
||||||
|
|
@ -1512,6 +1528,7 @@ struct make_from_integral_value<grotto::fixedpoint<FractionalBits, IntegralType>
|
||||||
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||||
using integral_type = typename to_integral_type<fixed_type>::integral_type;
|
using integral_type = typename to_integral_type<fixed_type>::integral_type;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr fixed_type operator()(integral_type val) const noexcept
|
constexpr fixed_type operator()(integral_type val) const noexcept
|
||||||
{
|
{
|
||||||
return grotto::make_fixed_from_integral_type<FractionalBits, IntegralType>(
|
return grotto::make_fixed_from_integral_type<FractionalBits, IntegralType>(
|
||||||
|
|
@ -1617,6 +1634,7 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||||||
static constexpr int bitwidth =
|
static constexpr int bitwidth =
|
||||||
static_cast<int>(dpf::utils::bitlength_of_v<I>);
|
static_cast<int>(dpf::utils::bitlength_of_v<I>);
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr I raw_lowest() noexcept
|
static constexpr I raw_lowest() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (signed_rep)
|
if constexpr (signed_rep)
|
||||||
|
|
@ -1627,6 +1645,7 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||||||
return I{};
|
return I{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr I raw_max() noexcept
|
static constexpr I raw_max() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (signed_rep)
|
if constexpr (signed_rep)
|
||||||
|
|
@ -1664,8 +1683,11 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||||||
static constexpr bool traps = false;
|
static constexpr bool traps = false;
|
||||||
static constexpr bool tinyness_before = false;
|
static constexpr bool tinyness_before = false;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T lowest() noexcept { return T::from_raw(raw_lowest()); }
|
static constexpr T lowest() noexcept { return T::from_raw(raw_lowest()); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T max() noexcept { return T::from_raw(raw_max()); }
|
static constexpr T max() noexcept { return T::from_raw(raw_max()); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T min() noexcept
|
static constexpr T min() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (FractionalBits == 0)
|
if constexpr (FractionalBits == 0)
|
||||||
|
|
@ -1674,17 +1696,23 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||||||
}
|
}
|
||||||
return T::from_raw(I{1});
|
return T::from_raw(I{1});
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T epsilon() noexcept
|
static constexpr T epsilon() noexcept
|
||||||
{
|
{
|
||||||
return FractionalBits ? T::from_raw(I{1}) : T::from_raw(I{});
|
return FractionalBits ? T::from_raw(I{1}) : T::from_raw(I{});
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T round_error() noexcept
|
static constexpr T round_error() noexcept
|
||||||
{
|
{
|
||||||
return FractionalBits ? T(0.5) : T(0);
|
return FractionalBits ? T(0.5) : T(0);
|
||||||
}
|
}
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T infinity() noexcept { return max(); }
|
static constexpr T infinity() noexcept { return max(); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T quiet_NaN() noexcept { return T::from_raw(I{}); }
|
static constexpr T quiet_NaN() noexcept { return T::from_raw(I{}); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T signalling_NaN() noexcept { return T::from_raw(I{}); }
|
static constexpr T signalling_NaN() noexcept { return T::from_raw(I{}); }
|
||||||
|
HEDLEY_NO_THROW
|
||||||
static constexpr T denorm_min() noexcept { return min(); }
|
static constexpr T denorm_min() noexcept { return min(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_DPF_FIXEDPOINT_HPP__
|
#ifndef LIBDPF_INCLUDE_DPF_FIXEDPOINT_HPP__
|
||||||
#include "grotto/fixedpoint.hpp"
|
#include "grotto/fixedpoint.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -106,6 +108,7 @@ namespace detail
|
||||||
|
|
||||||
inline constexpr std::size_t fixed_mul_buf_limbs = 12;
|
inline constexpr std::size_t fixed_mul_buf_limbs = 12;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void mask_to_bits(std::uint64_t * limbs, std::size_t nlimbs, unsigned bits) noexcept
|
constexpr void mask_to_bits(std::uint64_t * limbs, std::size_t nlimbs, unsigned bits) noexcept
|
||||||
{
|
{
|
||||||
if (bits >= nlimbs * 64u)
|
if (bits >= nlimbs * 64u)
|
||||||
|
|
@ -129,11 +132,13 @@ constexpr void mask_to_bits(std::uint64_t * limbs, std::size_t nlimbs, unsigned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool test_bit(const std::uint64_t * limbs, unsigned bit) noexcept
|
constexpr bool test_bit(const std::uint64_t * limbs, unsigned bit) noexcept
|
||||||
{
|
{
|
||||||
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void fill_ones(std::uint64_t * limbs, unsigned from, unsigned to) noexcept
|
constexpr void fill_ones(std::uint64_t * limbs, unsigned from, unsigned to) noexcept
|
||||||
{
|
{
|
||||||
for (unsigned bit = from; bit < to; )
|
for (unsigned bit = from; bit < to; )
|
||||||
|
|
@ -149,6 +154,7 @@ constexpr void fill_ones(std::uint64_t * limbs, unsigned from, unsigned to) noex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void sign_extend_range(std::uint64_t * limbs, unsigned from_bits, unsigned to_bits) noexcept
|
constexpr void sign_extend_range(std::uint64_t * limbs, unsigned from_bits, unsigned to_bits) noexcept
|
||||||
{
|
{
|
||||||
if (to_bits <= from_bits || from_bits == 0u)
|
if (to_bits <= from_bits || from_bits == 0u)
|
||||||
|
|
@ -162,6 +168,7 @@ constexpr void sign_extend_range(std::uint64_t * limbs, unsigned from_bits, unsi
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void store_raw_limbs(const T & value, std::uint64_t out[4]) noexcept
|
constexpr void store_raw_limbs(const T & value, std::uint64_t out[4]) noexcept
|
||||||
{
|
{
|
||||||
out[0] = out[1] = out[2] = out[3] = 0;
|
out[0] = out[1] = out[2] = out[3] = 0;
|
||||||
|
|
@ -192,6 +199,7 @@ constexpr void store_raw_limbs(const T & value, std::uint64_t out[4]) noexcept
|
||||||
|
|
||||||
/// Low `dest_bits` of `value`, sign-extended when `value` is a narrower signed integer.
|
/// Low `dest_bits` of `value`, sign-extended when `value` is a narrower signed integer.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void reduce_operand(const T & value, unsigned src_bits, bool is_signed,
|
constexpr void reduce_operand(const T & value, unsigned src_bits, bool is_signed,
|
||||||
unsigned dest_bits, std::uint64_t * dest, unsigned nlimbs) noexcept
|
unsigned dest_bits, std::uint64_t * dest, unsigned nlimbs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -215,6 +223,7 @@ constexpr void reduce_operand(const T & value, unsigned src_bits, bool is_signed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Product modulo `2^(64*nlimbs)`, using exactly `nlimbs` limbs of each operand.
|
/// Product modulo `2^(64*nlimbs)`, using exactly `nlimbs` limbs of each operand.
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void mul_low_limbs(std::uint64_t * out, const std::uint64_t * lhs,
|
constexpr void mul_low_limbs(std::uint64_t * out, const std::uint64_t * lhs,
|
||||||
const std::uint64_t * rhs, unsigned nlimbs) noexcept
|
const std::uint64_t * rhs, unsigned nlimbs) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -235,6 +244,7 @@ constexpr void mul_low_limbs(std::uint64_t * out, const std::uint64_t * lhs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void shift_left_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
constexpr void shift_left_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
||||||
{
|
{
|
||||||
if (shift == 0u)
|
if (shift == 0u)
|
||||||
|
|
@ -261,6 +271,7 @@ constexpr void shift_left_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void shift_right_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
constexpr void shift_right_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
||||||
{
|
{
|
||||||
if (shift == 0u)
|
if (shift == 0u)
|
||||||
|
|
@ -288,6 +299,7 @@ constexpr void shift_right_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsi
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T limbs_to_integral(const std::uint64_t * limbs) noexcept
|
constexpr T limbs_to_integral(const std::uint64_t * limbs) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<T, uint256_t>)
|
if constexpr (std::is_same_v<T, uint256_t>)
|
||||||
|
|
@ -331,6 +343,7 @@ template <unsigned IntegerBits,
|
||||||
typename LhsIntegral,
|
typename LhsIntegral,
|
||||||
unsigned RhsFractionalBits,
|
unsigned RhsFractionalBits,
|
||||||
typename RhsIntegral>
|
typename RhsIntegral>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr auto fixed_mul(
|
constexpr auto fixed_mul(
|
||||||
fixedpoint<LhsFractionalBits, LhsIntegral> lhs,
|
fixedpoint<LhsFractionalBits, LhsIntegral> lhs,
|
||||||
fixedpoint<RhsFractionalBits, RhsIntegral> rhs) noexcept
|
fixedpoint<RhsFractionalBits, RhsIntegral> rhs) noexcept
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
@ -109,6 +111,7 @@ constexpr bool is_hexfloat_fixedpoint = false;
|
||||||
template <unsigned FractionalBits, typename Integral>
|
template <unsigned FractionalBits, typename Integral>
|
||||||
constexpr bool is_hexfloat_fixedpoint<fixedpoint<FractionalBits, Integral>> = true;
|
constexpr bool is_hexfloat_fixedpoint<fixedpoint<FractionalBits, Integral>> = true;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void mask_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
constexpr void mask_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||||
{
|
{
|
||||||
if (width >= 256u)
|
if (width >= 256u)
|
||||||
|
|
@ -132,11 +135,13 @@ constexpr void mask_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr bool bit_is_set(const std::uint64_t * limbs, unsigned bit) noexcept
|
constexpr bool bit_is_set(const std::uint64_t * limbs, unsigned bit) noexcept
|
||||||
{
|
{
|
||||||
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void negate_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
constexpr void negate_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < 4u; ++i)
|
for (unsigned i = 0; i < 4u; ++i)
|
||||||
|
|
@ -154,6 +159,7 @@ constexpr void negate_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr void store_integer_bits(const T & value, std::uint64_t out[4]) noexcept
|
constexpr void store_integer_bits(const T & value, std::uint64_t out[4]) noexcept
|
||||||
{
|
{
|
||||||
out[0] = out[1] = out[2] = out[3] = 0;
|
out[0] = out[1] = out[2] = out[3] = 0;
|
||||||
|
|
@ -183,6 +189,7 @@ constexpr void store_integer_bits(const T & value, std::uint64_t out[4]) noexcep
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr T load_integer_bits(const std::uint64_t * limbs) noexcept
|
constexpr T load_integer_bits(const std::uint64_t * limbs) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<T, uint256_t>)
|
if constexpr (std::is_same_v<T, uint256_t>)
|
||||||
|
|
@ -205,6 +212,7 @@ constexpr T load_integer_bits(const std::uint64_t * limbs) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline int highest_bit(const std::uint64_t * limbs) noexcept
|
inline int highest_bit(const std::uint64_t * limbs) noexcept
|
||||||
{
|
{
|
||||||
for (int i = 3; i >= 0; --i)
|
for (int i = 3; i >= 0; --i)
|
||||||
|
|
@ -268,6 +276,7 @@ std::string format_hexfloat(const T & value, int fractional_bits)
|
||||||
return std::string(negative ? "-" : "+") + "0x1." + fraction + "p" + expbuf;
|
return std::string(negative ? "-" : "+") + "0x1." + fraction + "p" + expbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline int hex_value(char c) noexcept
|
inline int hex_value(char c) noexcept
|
||||||
{
|
{
|
||||||
if (c >= '0' && c <= '9') return c - '0';
|
if (c >= '0' && c <= '9') return c - '0';
|
||||||
|
|
|
||||||
194
include/grotto/nmod.hpp
Normal file
194
include/grotto/nmod.hpp
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
/// @file grotto/nmod.hpp
|
||||||
|
/// @brief Reduction modulo an arbitrary public modulus.
|
||||||
|
/// @details `nmod` multiplies by a rounded reciprocal `1/M` and splits that
|
||||||
|
/// product with a floor. The quotient is `floor(x/M)`. The residue
|
||||||
|
/// is `{x/M}` truncated onto `residue_bits` fractional bits, so it
|
||||||
|
/// lies in `[0, 2^residue_bits)`. A negative product borrows, which
|
||||||
|
/// keeps the residue non-negative.
|
||||||
|
///
|
||||||
|
/// The reciprocal magnitude is 128 bits, so `1/M` can be carried
|
||||||
|
/// wider than the input. A power-of-two modulus is the same split
|
||||||
|
/// with an exact shift (`nmod_pow2`).
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_GROTTO_NMOD_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_GROTTO_NMOD_HPP__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
|
namespace grotto
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace nmod_detail
|
||||||
|
{
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nmod_result
|
||||||
|
{
|
||||||
|
/// `floor({x/M} * 2^residue_bits)`, in `[0, 2^residue_bits)`.
|
||||||
|
std::int64_t residue = 0;
|
||||||
|
/// `floor(x/M)`.
|
||||||
|
std::int64_t quotient = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `x_raw / 2^x_bits` modulo `M`, with `1/M ≈ recip_raw / 2^recip_bits`.
|
||||||
|
/// `recip_raw` is a positive magnitude of at most 128 bits.
|
||||||
|
/// @throws std::invalid_argument if the reciprocal is zero or a width is illegal.
|
||||||
|
/// @throws std::overflow_error if the quotient does not fit in `int64_t`.
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
inline nmod_result nmod(std::int64_t x_raw, unsigned x_bits,
|
||||||
|
unsigned __int128 recip_raw, unsigned recip_bits, unsigned residue_bits)
|
||||||
|
{
|
||||||
|
if (recip_raw == 0)
|
||||||
|
throw std::invalid_argument("nmod: reciprocal must be positive");
|
||||||
|
if (residue_bits > 63)
|
||||||
|
throw std::invalid_argument("nmod: residue must fit in int64");
|
||||||
|
if (x_bits > 100000u || recip_bits > 100000u)
|
||||||
|
throw std::invalid_argument("nmod: fractional width is too large");
|
||||||
|
|
||||||
|
const bool neg = x_raw < 0;
|
||||||
|
const auto x_mag = static_cast<std::uint64_t>(
|
||||||
|
neg ? -static_cast<__int128>(x_raw) : x_raw);
|
||||||
|
const auto recip_lo = static_cast<std::uint64_t>(recip_raw);
|
||||||
|
const auto recip_hi = static_cast<std::uint64_t>(recip_raw >> 64);
|
||||||
|
|
||||||
|
unsigned __int128 low = static_cast<unsigned __int128>(x_mag) * recip_lo;
|
||||||
|
unsigned __int128 high = static_cast<unsigned __int128>(x_mag) * recip_hi;
|
||||||
|
std::uint64_t limb[4] = {};
|
||||||
|
limb[0] = static_cast<std::uint64_t>(low);
|
||||||
|
const unsigned __int128 mid = (low >> 64) + static_cast<std::uint64_t>(high);
|
||||||
|
limb[1] = static_cast<std::uint64_t>(mid);
|
||||||
|
const unsigned __int128 top = (high >> 64) + (mid >> 64);
|
||||||
|
limb[2] = static_cast<std::uint64_t>(top);
|
||||||
|
limb[3] = static_cast<std::uint64_t>(top >> 64);
|
||||||
|
|
||||||
|
const auto bit_set_at_or_above = [&](unsigned bit) {
|
||||||
|
if (bit >= 256)
|
||||||
|
return false;
|
||||||
|
const unsigned index = bit / 64u;
|
||||||
|
const unsigned offset = bit % 64u;
|
||||||
|
if ((limb[index] >> offset) != 0)
|
||||||
|
return true;
|
||||||
|
for (unsigned i = index + 1; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (limb[i] != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const auto extract = [&](unsigned low_bit, unsigned count) -> std::uint64_t {
|
||||||
|
if (count == 0 || low_bit >= 256)
|
||||||
|
return 0;
|
||||||
|
const unsigned index = low_bit / 64u;
|
||||||
|
const unsigned offset = low_bit % 64u;
|
||||||
|
unsigned __int128 chunk = limb[index];
|
||||||
|
if (index + 1 < 4)
|
||||||
|
chunk |= static_cast<unsigned __int128>(limb[index + 1]) << 64;
|
||||||
|
chunk >>= offset;
|
||||||
|
if (count == 64)
|
||||||
|
return static_cast<std::uint64_t>(chunk);
|
||||||
|
return static_cast<std::uint64_t>(chunk) & ((std::uint64_t{1} << count) - 1);
|
||||||
|
};
|
||||||
|
const auto low_bits_set = [&](unsigned width) {
|
||||||
|
if (width == 0)
|
||||||
|
return false;
|
||||||
|
if (width >= 256)
|
||||||
|
return limb[0] != 0 || limb[1] != 0 || limb[2] != 0 || limb[3] != 0;
|
||||||
|
const unsigned index = width / 64u;
|
||||||
|
const unsigned offset = width % 64u;
|
||||||
|
for (unsigned i = 0; i < index; ++i)
|
||||||
|
{
|
||||||
|
if (limb[i] != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (offset == 0)
|
||||||
|
return false;
|
||||||
|
const std::uint64_t mask = (std::uint64_t{1} << offset) - 1;
|
||||||
|
return (limb[index] & mask) != 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned scale = x_bits + recip_bits;
|
||||||
|
const bool remainder = low_bits_set(scale);
|
||||||
|
std::uint64_t quotient_mag = 0;
|
||||||
|
if (scale < 256)
|
||||||
|
{
|
||||||
|
if (bit_set_at_or_above(scale + 64))
|
||||||
|
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||||
|
quotient_mag = extract(scale, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
nmod_result out;
|
||||||
|
if (!neg)
|
||||||
|
{
|
||||||
|
if (quotient_mag > static_cast<std::uint64_t>(INT64_MAX))
|
||||||
|
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||||
|
out.quotient = static_cast<std::int64_t>(quotient_mag);
|
||||||
|
}
|
||||||
|
else if (!remainder)
|
||||||
|
{
|
||||||
|
if (quotient_mag > (static_cast<std::uint64_t>(INT64_MAX) + 1))
|
||||||
|
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||||
|
out.quotient = quotient_mag == (std::uint64_t{1} << 63)
|
||||||
|
? INT64_MIN
|
||||||
|
: -static_cast<std::int64_t>(quotient_mag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (quotient_mag > static_cast<std::uint64_t>(INT64_MAX))
|
||||||
|
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||||
|
out.quotient = -static_cast<std::int64_t>(quotient_mag) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (residue_bits == 0 || (!neg && !remainder) || (neg && !remainder))
|
||||||
|
return out;
|
||||||
|
|
||||||
|
// A 128-bit reciprocal times an int64 magnitude stays under 2^192.
|
||||||
|
// With a residue of at most 63 bits, a scale at or above 256 puts every
|
||||||
|
// product bit strictly below the residue window.
|
||||||
|
if (scale >= 256)
|
||||||
|
{
|
||||||
|
if (neg)
|
||||||
|
out.residue = static_cast<std::int64_t>((std::uint64_t{1} << residue_bits) - 1);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t field = 0;
|
||||||
|
if (scale >= residue_bits)
|
||||||
|
field = extract(scale - residue_bits, residue_bits);
|
||||||
|
else
|
||||||
|
field = extract(0, scale) << (residue_bits - scale);
|
||||||
|
|
||||||
|
if (neg)
|
||||||
|
{
|
||||||
|
const std::uint64_t unit = std::uint64_t{1} << residue_bits;
|
||||||
|
const unsigned discarded = scale >= residue_bits ? scale - residue_bits : 0;
|
||||||
|
const bool borrow = discarded > 0 && low_bits_set(discarded);
|
||||||
|
field = borrow ? unit - field - 1 : unit - field;
|
||||||
|
}
|
||||||
|
out.residue = static_cast<std::int64_t>(field);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modulus `2^{-exp}` by an exact shift. Positive `exp` multiplies by
|
||||||
|
/// `2^exp` (`exp`'s `2^{-13}` split). Zero splits at the integer (`2^x`).
|
||||||
|
/// Negative `exp` is a modulus above one.
|
||||||
|
/// @throws std::overflow_error if `exp` does not fit the reciprocal width.
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
inline nmod_result nmod_pow2(std::int64_t x_raw, unsigned x_bits, int exp,
|
||||||
|
unsigned residue_bits)
|
||||||
|
{
|
||||||
|
if (exp >= 128 || exp < -100000)
|
||||||
|
throw std::overflow_error("nmod: power-of-two reciprocal does not fit");
|
||||||
|
if (exp >= 0)
|
||||||
|
return nmod(x_raw, x_bits, nmod_detail::u128{1} << static_cast<unsigned>(exp), 0, residue_bits);
|
||||||
|
return nmod(x_raw, x_bits, 1, static_cast<unsigned>(-exp), residue_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace grotto
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_GROTTO_NMOD_HPP__
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
@ -43,6 +45,7 @@ namespace grotto
|
||||||
inline constexpr std::size_t offset_horner_max_degree = 3;
|
inline constexpr std::size_t offset_horner_max_degree = 3;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
T offset_horner_group_add(T a, T b) noexcept
|
T offset_horner_group_add(T a, T b) noexcept
|
||||||
{
|
{
|
||||||
using u = std::make_unsigned_t<T>;
|
using u = std::make_unsigned_t<T>;
|
||||||
|
|
@ -50,6 +53,7 @@ T offset_horner_group_add(T a, T b) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
T offset_horner_group_sub(T a, T b) noexcept
|
T offset_horner_group_sub(T a, T b) noexcept
|
||||||
{
|
{
|
||||||
using u = std::make_unsigned_t<T>;
|
using u = std::make_unsigned_t<T>;
|
||||||
|
|
@ -65,6 +69,7 @@ struct offset_horner_x_plus_r
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
offset_horner_x_plus_r<T> offset_horner_at_x_plus_r(T x, T r) noexcept
|
offset_horner_x_plus_r<T> offset_horner_at_x_plus_r(T x, T r) noexcept
|
||||||
{
|
{
|
||||||
return offset_horner_x_plus_r<T>{
|
return offset_horner_x_plus_r<T>{
|
||||||
|
|
@ -86,6 +91,7 @@ inline constexpr uint64_t binom[4][4] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t lift(T v) noexcept
|
uint64_t lift(T v) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_signed_v<T>)
|
if constexpr (std::is_signed_v<T>)
|
||||||
|
|
@ -95,6 +101,7 @@ uint64_t lift(T v) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t Degree>
|
template <std::size_t Degree>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t horner_at(const std::array<uint64_t, Degree + 1> & coeff, uint64_t point) noexcept
|
uint64_t horner_at(const std::array<uint64_t, Degree + 1> & coeff, uint64_t point) noexcept
|
||||||
{
|
{
|
||||||
uint64_t acc = coeff[Degree];
|
uint64_t acc = coeff[Degree];
|
||||||
|
|
@ -104,6 +111,7 @@ uint64_t horner_at(const std::array<uint64_t, Degree + 1> & coeff, uint64_t poin
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t Degree>
|
template <std::size_t Degree>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
void fill_payloads(uint64_t base, uint64_t (&payload)[Degree + 1]) noexcept
|
void fill_payloads(uint64_t base, uint64_t (&payload)[Degree + 1]) noexcept
|
||||||
{
|
{
|
||||||
uint64_t pow = 1;
|
uint64_t pow = 1;
|
||||||
|
|
@ -191,6 +199,7 @@ std::vector<uint64_t> segments_of(const Key & key, const std::vector<InputT> & k
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
int64_t math_lift(T value) noexcept
|
int64_t math_lift(T value) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_signed_v<T>)
|
if constexpr (std::is_signed_v<T>)
|
||||||
|
|
@ -200,6 +209,7 @@ int64_t math_lift(T value) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
T domain_min() noexcept
|
T domain_min() noexcept
|
||||||
{
|
{
|
||||||
if constexpr (std::is_signed_v<T>)
|
if constexpr (std::is_signed_v<T>)
|
||||||
|
|
@ -211,6 +221,7 @@ T domain_min() noexcept
|
||||||
/// Public center-space cut where `center + eta` crosses the domain end.
|
/// Public center-space cut where `center + eta` crosses the domain end.
|
||||||
/// Empty when that cut is outside the domain, including `eta == 0`.
|
/// Empty when that cut is outside the domain, including `eta == 0`.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
std::optional<T> carry_threshold(T eta) noexcept
|
std::optional<T> carry_threshold(T eta) noexcept
|
||||||
{
|
{
|
||||||
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
||||||
|
|
@ -237,6 +248,7 @@ std::optional<T> carry_threshold(T eta) noexcept
|
||||||
|
|
||||||
/// `center + kappa` is the wrapped representative, as a mathematical integer.
|
/// `center + kappa` is the wrapped representative, as a mathematical integer.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
int64_t kappa_for(T left, T eta) noexcept
|
int64_t kappa_for(T left, T eta) noexcept
|
||||||
{
|
{
|
||||||
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
||||||
|
|
@ -528,7 +540,7 @@ namespace offset_horner_detail
|
||||||
|
|
||||||
template <std::size_t Degree, typename InputT, typename Rng>
|
template <std::size_t Degree, typename InputT, typename Rng>
|
||||||
geneval_offset_horner_result<Degree, InputT> geneval_at(
|
geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||||
InputT center0, InputT center1, InputT center, InputT eta,
|
bool arith, InputT center0, InputT center1, InputT center, InputT eta,
|
||||||
const std::vector<InputT> & knots,
|
const std::vector<InputT> & knots,
|
||||||
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||||
Rng rng)
|
Rng rng)
|
||||||
|
|
@ -553,7 +565,10 @@ geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||||
std::array<std::vector<uint64_t>, Degree + 1> seg1;
|
std::array<std::vector<uint64_t>, Degree + 1> seg1;
|
||||||
for (std::size_t m = 0; m <= Degree; ++m)
|
for (std::size_t m = 0; m <= Degree; ++m)
|
||||||
{
|
{
|
||||||
const auto opened = dpf::geneval_cmp(center0, center1,
|
const auto opened = arith
|
||||||
|
? dpf::geneval_cmp(dpf::arith_input, center0, center1,
|
||||||
|
shifted.begin(), shifted.end(), rng, payload[m])
|
||||||
|
: dpf::geneval_cmp(center0, center1,
|
||||||
shifted.begin(), shifted.end(), rng, payload[m]);
|
shifted.begin(), shifted.end(), rng, payload[m]);
|
||||||
const uint64_t blind = dpf::uniform_sample<uint64_t>();
|
const uint64_t blind = dpf::uniform_sample<uint64_t>();
|
||||||
wrap[m][0] = blind;
|
wrap[m][0] = blind;
|
||||||
|
|
@ -574,6 +589,17 @@ geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <std::size_t Degree, typename InputT, typename Rng>
|
||||||
|
geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||||
|
InputT center0, InputT center1, InputT center, InputT eta,
|
||||||
|
const std::vector<InputT> & knots,
|
||||||
|
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||||
|
Rng rng)
|
||||||
|
{
|
||||||
|
return geneval_at<Degree>(false, center0, center1, center, eta, knots, coeff,
|
||||||
|
std::move(rng));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace offset_horner_detail
|
} // namespace offset_horner_detail
|
||||||
|
|
||||||
/// Geneval-style offset Horner. The center is XOR-shared as in `geneval_point`.
|
/// Geneval-style offset Horner. The center is XOR-shared as in `geneval_point`.
|
||||||
|
|
@ -609,10 +635,25 @@ geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
||||||
center0, center1, eta, knots, coeff, std::move(rng));
|
center0, center1, eta, knots, coeff, std::move(rng));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Additive shares of the center: `center0 + center1` is the comparison point.
|
||||||
|
template <std::size_t Degree = offset_horner_max_degree,
|
||||||
|
typename InputT,
|
||||||
|
typename Rng>
|
||||||
|
geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
||||||
|
dpf::arith_input_t, InputT center0, InputT center1, InputT eta,
|
||||||
|
const std::vector<InputT> & knots,
|
||||||
|
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||||
|
Rng rng)
|
||||||
|
{
|
||||||
|
const InputT center = offset_horner_group_add(center0, center1);
|
||||||
|
return offset_horner_detail::geneval_at<Degree>(
|
||||||
|
true, center0, center1, center, eta, knots, coeff, std::move(rng));
|
||||||
|
}
|
||||||
|
|
||||||
/// Additive shares of the input `x` and the mask `r`. Reconstructs
|
/// Additive shares of the input `x` and the mask `r`. Reconstructs
|
||||||
/// `eta = x - r` and `center = 2r`, XOR-shares that center as `(center, 0)`,
|
/// `eta = x - r` and passes additive shares of `center = 2r` (`2·r0`, `2·r1`)
|
||||||
/// and returns both parties' Horner shares of the cubic at `x + r`
|
/// to arithmetic `geneval_cmp`. Returns both parties' Horner shares of the
|
||||||
/// (the group element `x + r`).
|
/// cubic at `x + r` (the group element `x + r`).
|
||||||
template <std::size_t Degree = offset_horner_max_degree,
|
template <std::size_t Degree = offset_horner_max_degree,
|
||||||
typename InputT,
|
typename InputT,
|
||||||
typename Rng>
|
typename Rng>
|
||||||
|
|
@ -625,10 +666,11 @@ geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
||||||
const InputT x = offset_horner_group_add(x0, x1);
|
const InputT x = offset_horner_group_add(x0, x1);
|
||||||
const InputT r = offset_horner_group_add(r0, r1);
|
const InputT r = offset_horner_group_add(r0, r1);
|
||||||
const InputT eta = offset_horner_group_sub(x, r);
|
const InputT eta = offset_horner_group_sub(x, r);
|
||||||
const InputT center = offset_horner_group_add(r, r);
|
const InputT center0 = offset_horner_group_add(r0, r0);
|
||||||
InputT zero{};
|
const InputT center1 = offset_horner_group_add(r1, r1);
|
||||||
|
const InputT center = offset_horner_group_add(center0, center1);
|
||||||
return offset_horner_detail::geneval_at<Degree>(
|
return offset_horner_detail::geneval_at<Degree>(
|
||||||
center, zero, center, eta, knots, coeff, std::move(rng));
|
true, center0, center1, center, eta, knots, coeff, std::move(rng));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t Degree = offset_horner_max_degree, typename InputT>
|
template <std::size_t Degree = offset_horner_max_degree, typename InputT>
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ struct offset_iterator_base
|
||||||
std::add_const_t<reference>>;
|
std::add_const_t<reference>>;
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
constexpr offset_iterator_base(wrapped_iterator iter, size_type offset) noexcept
|
constexpr offset_iterator_base(wrapped_iterator iter, size_type offset) noexcept
|
||||||
: it{iter}, offset_{offset} { }
|
: it{iter}, offset_{offset} { }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
/// @file grotto/piecewise.hpp
|
/// @file grotto/piecewise.hpp
|
||||||
|
/// @brief Horner evaluation of a cubic and a bound-selected piece.
|
||||||
|
/// @details `eval_horner` evaluates one polynomial. `piecewise_eval` selects
|
||||||
|
/// the piece whose upper bound is the first entry of `bounds`
|
||||||
|
/// strictly greater than `x`.
|
||||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||||
/// @brief
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
/// @details
|
|
||||||
/// @copyright Copyright (c) 2019-2023 Ryan Henry and others
|
|
||||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||||
/// see [LICENSE.md](@ref GPLv2) for details.
|
/// see [LICENSE.md](@ref license) for details.
|
||||||
|
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
||||||
|
|
@ -13,6 +15,8 @@
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
namespace grotto
|
namespace grotto
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -25,16 +29,26 @@ template <typename T> using poly_quadratic = std::array<T, 3>;
|
||||||
template <typename T> using poly_cubic = std::array<T, 4>;
|
template <typename T> using poly_cubic = std::array<T, 4>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto eval_horner(const poly_constant<T> & f, T x) { return f[0]; }
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr auto eval_horner(const poly_constant<T> & f, T x) noexcept { return f[0]; }
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto eval_horner(const poly_linear<T> & f, T x) { return f[1] * x + f[0]; }
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr auto eval_horner(const poly_linear<T> & f, T x) noexcept { return f[1] * x + f[0]; }
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto eval_horner(const poly_quadratic<T> & f, T x) { return (f[2] * x + f[1]) * x + f[0]; }
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr auto eval_horner(const poly_quadratic<T> & f, T x) noexcept { return (f[2] * x + f[1]) * x + f[0]; }
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr auto eval_horner(const poly_cubic<T> & f, T x) { return ((f[3] * x + f[2]) * x + f[1]) * x + f[0]; }
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr auto eval_horner(const poly_cubic<T> & f, T x) noexcept { return ((f[3] * x + f[2]) * x + f[1]) * x + f[0]; }
|
||||||
|
|
||||||
template <typename T, std::size_t D, std::size_t N1, std::size_t N2>
|
template <typename T, std::size_t D, std::size_t N1, std::size_t N2>
|
||||||
auto piecewise_eval(const std::array<std::array<T, D>, N1> & polys, const std::array<T, N2> & bounds, T x)
|
HEDLEY_PURE
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
auto piecewise_eval(const std::array<std::array<T, D>, N1> & polys, const std::array<T, N2> & bounds, T x) noexcept
|
||||||
{
|
{
|
||||||
auto it = std::upper_bound(std::cbegin(bounds), std::cend(bounds), x,
|
auto it = std::upper_bound(std::cbegin(bounds), std::cend(bounds), x,
|
||||||
[](const T & lhs, const T & rhs){ return lhs < rhs; });
|
[](const T & lhs, const T & rhs){ return lhs < rhs; });
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "dpf/twiddle.hpp"
|
#include "dpf/twiddle.hpp"
|
||||||
#include "dpf/leaf_node.hpp"
|
#include "dpf/leaf_node.hpp"
|
||||||
#include "dpf/dcf.hpp"
|
#include "dpf/dcf.hpp"
|
||||||
|
#include "dpf/blocked_dcf.hpp"
|
||||||
#include "grotto/offset_iterable.hpp"
|
#include "grotto/offset_iterable.hpp"
|
||||||
#include "dpf/path_memoizer.hpp"
|
#include "dpf/path_memoizer.hpp"
|
||||||
#include "dpf/utils.hpp"
|
#include "dpf/utils.hpp"
|
||||||
|
|
@ -29,6 +30,7 @@ namespace grotto
|
||||||
|
|
||||||
template <typename NodeT,
|
template <typename NodeT,
|
||||||
typename InputT>
|
typename InputT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
auto parity_of_substring_prefix(const NodeT & node, InputT x) noexcept
|
auto parity_of_substring_prefix(const NodeT & node, InputT x) noexcept
|
||||||
{
|
{
|
||||||
static constexpr auto bits_per_limb = dpf::utils::bitlength_of_v<decltype(node[0])>;
|
static constexpr auto bits_per_limb = dpf::utils::bitlength_of_v<decltype(node[0])>;
|
||||||
|
|
@ -211,6 +213,7 @@ struct key_has_cmp<T, std::void_t<decltype(std::declval<const T &>().has_cmp())>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename KeyT>
|
template <typename KeyT>
|
||||||
|
HEDLEY_NO_THROW
|
||||||
uint64_t cmp_addend_raw(const KeyT & key) noexcept
|
uint64_t cmp_addend_raw(const KeyT & key) noexcept
|
||||||
{
|
{
|
||||||
if constexpr (dpf::is_party_key_v<KeyT>)
|
if constexpr (dpf::is_party_key_v<KeyT>)
|
||||||
|
|
@ -263,7 +266,12 @@ static auto signed_prefix_parities(const DpfKey & dpf,
|
||||||
constexpr std::size_t depth = key_type::depth;
|
constexpr std::size_t depth = key_type::depth;
|
||||||
const auto & ch = dpf.cmp();
|
const auto & ch = dpf.cmp();
|
||||||
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||||
if (nbits > depth)
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
if (key_type::cmp_h > depth)
|
||||||
|
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||||
|
}
|
||||||
|
else if (nbits > depth)
|
||||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||||
|
|
||||||
const uint64_t mask = ch.mask;
|
const uint64_t mask = ch.mask;
|
||||||
|
|
@ -284,6 +292,11 @@ static auto signed_prefix_parities(const DpfKey & dpf,
|
||||||
prefixes[which] = addend & mask;
|
prefixes[which] = addend & mask;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
prefixes[which] = dpf::detail::blocked::eval_share(dpf, tx, path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const std::size_t resume = dpf::detail::path_resume_for_level(
|
const std::size_t resume = dpf::detail::path_resume_for_level(
|
||||||
path, dpf, tx, nbits);
|
path, dpf, tx, nbits);
|
||||||
|
|
@ -363,7 +376,12 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
|
||||||
constexpr std::size_t depth = key_type::depth;
|
constexpr std::size_t depth = key_type::depth;
|
||||||
const auto & ch = dpf.cmp();
|
const auto & ch = dpf.cmp();
|
||||||
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||||
if (nbits > depth)
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
if (key_type::cmp_h > depth)
|
||||||
|
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||||
|
}
|
||||||
|
else if (nbits > depth)
|
||||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||||
|
|
||||||
const uint64_t mask = ch.mask;
|
const uint64_t mask = ch.mask;
|
||||||
|
|
@ -382,6 +400,11 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
|
||||||
out[which] = addend & mask;
|
out[which] = addend & mask;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if constexpr (key_type::cmp_block > 0)
|
||||||
|
{
|
||||||
|
out[which] = dpf::detail::blocked::eval_share(dpf, tx, path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const std::size_t resume = dpf::detail::path_resume_for_level(
|
const std::size_t resume = dpf::detail::path_resume_for_level(
|
||||||
path, dpf, tx, nbits);
|
path, dpf, tx, nbits);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
#ifndef LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
#ifndef LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
||||||
#define LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
#define LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
@ -44,6 +46,7 @@ enum class principal : unsigned
|
||||||
|
|
||||||
inline constexpr unsigned principal_precisions[] = {8u, 12u, 16u, 20u, 24u, 28u, 32u};
|
inline constexpr unsigned principal_precisions[] = {8u, 12u, 16u, 20u, 24u, 28u, 32u};
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline constexpr bool principal_precision(unsigned fractional_bits) noexcept
|
inline constexpr bool principal_precision(unsigned fractional_bits) noexcept
|
||||||
{
|
{
|
||||||
for (unsigned k : principal_precisions)
|
for (unsigned k : principal_precisions)
|
||||||
|
|
@ -150,6 +153,7 @@ inline w256 w_mul_u64(w256 value, std::uint64_t factor)
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEDLEY_NO_THROW
|
||||||
inline bool w_negative(w256 value) noexcept
|
inline bool w_negative(w256 value) noexcept
|
||||||
{
|
{
|
||||||
return value.hi < 0;
|
return value.hi < 0;
|
||||||
|
|
@ -311,7 +315,10 @@ inline w256 w_mul_i64(w256 value, std::int64_t factor)
|
||||||
{
|
{
|
||||||
if (factor >= 0)
|
if (factor >= 0)
|
||||||
return w_mul_u64(value, static_cast<std::uint64_t>(factor));
|
return w_mul_u64(value, static_cast<std::uint64_t>(factor));
|
||||||
return w_neg(w_mul_u64(value, static_cast<std::uint64_t>(-factor)));
|
// `-factor` is undefined at INT64_MIN. The magnitude is the unsigned wrap.
|
||||||
|
const auto mag = static_cast<std::uint64_t>(0)
|
||||||
|
- static_cast<std::uint64_t>(factor);
|
||||||
|
return w_neg(w_mul_u64(value, mag));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::int64_t horner(const cubic_bits & piece, unsigned q, std::int64_t raw, unsigned fractional_bits)
|
inline std::int64_t horner(const cubic_bits & piece, unsigned q, std::int64_t raw, unsigned fractional_bits)
|
||||||
|
|
|
||||||
676
include/grotto/range_lut.hpp
Normal file
676
include/grotto/range_lut.hpp
Normal file
|
|
@ -0,0 +1,676 @@
|
||||||
|
/// @file grotto/range_lut.hpp
|
||||||
|
/// @brief Full-domain maps built from the principal-domain cubics.
|
||||||
|
/// @details Each reduced map is one of the elementary range reductions, and
|
||||||
|
/// the polynomial it evaluates is the matching principal table:
|
||||||
|
/// `ln` / `lg` / `log10` share the mantissa logarithm;
|
||||||
|
/// `exp` / `exp2` / `exp10` share the `2^{-13}` exponential;
|
||||||
|
/// `sin` / `cos` share the quarter-turn sine;
|
||||||
|
/// `tan` / `cot` share `tanf` and `tang`;
|
||||||
|
/// `sec` / `csc` share `sec` and `gsec`;
|
||||||
|
/// `sinh` / `cosh` / `tanh` / `sech` share the hyperbolic addition;
|
||||||
|
/// `coth` and `csch` use their principal small-argument tables;
|
||||||
|
/// `sqrt` / `inv` / `rsqrt` / `invsq` are dyadic lifts of `[1/2, 1]`.
|
||||||
|
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||||
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||||
|
|
||||||
|
#ifndef LIBDPF_INCLUDE_GROTTO_RANGE_LUT_HPP__
|
||||||
|
#define LIBDPF_INCLUDE_GROTTO_RANGE_LUT_HPP__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "hedley/hedley.h"
|
||||||
|
|
||||||
|
#include "grotto/principal_lut.hpp"
|
||||||
|
|
||||||
|
namespace grotto
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class reduced : unsigned
|
||||||
|
{
|
||||||
|
ln = 0,
|
||||||
|
lg,
|
||||||
|
log10,
|
||||||
|
exp,
|
||||||
|
exp2,
|
||||||
|
exp10,
|
||||||
|
sin,
|
||||||
|
cos,
|
||||||
|
tan,
|
||||||
|
cot,
|
||||||
|
sec,
|
||||||
|
csc,
|
||||||
|
sinh,
|
||||||
|
cosh,
|
||||||
|
tanh,
|
||||||
|
coth,
|
||||||
|
sech,
|
||||||
|
csch,
|
||||||
|
sqrt,
|
||||||
|
inv,
|
||||||
|
rsqrt,
|
||||||
|
invsq,
|
||||||
|
};
|
||||||
|
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
inline std::int64_t eval_reduced(reduced which, unsigned fractional_bits, std::int64_t raw);
|
||||||
|
|
||||||
|
namespace range_detail
|
||||||
|
{
|
||||||
|
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
|
||||||
|
constexpr u128 words(std::uint64_t hi, std::uint64_t lo)
|
||||||
|
{
|
||||||
|
return (u128{hi} << 64) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `value * 2^64`, rounded half away from zero. Values above `2^64` keep the
|
||||||
|
/// high limb so the constant is not truncated.
|
||||||
|
inline constexpr u128 ln2_64 = words(0, 12786308645202655660ULL);
|
||||||
|
inline constexpr u128 inv_ln2_64 = words(1, 8166282121979093367ULL);
|
||||||
|
inline constexpr u128 log10_2_64 = words(0, 5553023288523357132ULL);
|
||||||
|
inline constexpr u128 ln10_64 = words(2, 5581709770980765788ULL);
|
||||||
|
inline constexpr u128 inv_ln10_64 = words(0, 8011319160293570763ULL);
|
||||||
|
inline constexpr u128 sqrt2_64 = words(1, 7640891576956012809ULL);
|
||||||
|
inline constexpr u128 rsqrt2_64 = words(0, 13043817825332782212ULL);
|
||||||
|
inline constexpr u128 two_over_pi_64 = words(0, 11743562013128004906ULL);
|
||||||
|
inline constexpr u128 four_over_pi_64 = words(1, 5040379952546458196ULL);
|
||||||
|
inline constexpr u128 pi_over_4_64 = words(0, 14488038916154245685ULL);
|
||||||
|
|
||||||
|
/// `exp(2^{i-13}) * 2^64`.
|
||||||
|
inline constexpr u128 exp_chunk_64[13] = {
|
||||||
|
words(1, 2251937258231296ULL),
|
||||||
|
words(1, 4504149427926357ULL),
|
||||||
|
words(1, 9009398635954180ULL),
|
||||||
|
words(1, 18023197466514910ULL),
|
||||||
|
words(1, 36064004308734226ULL),
|
||||||
|
words(1, 72198514957318099ULL),
|
||||||
|
words(1, 144679606912572172ULL),
|
||||||
|
words(1, 290493950045950331ULL),
|
||||||
|
words(1, 585562514163419534ULL),
|
||||||
|
words(1, 1189712777830127574ULL),
|
||||||
|
words(1, 2456155437534072733ULL),
|
||||||
|
words(1, 5239344172067481206ULL),
|
||||||
|
words(1, 11966795255776918679ULL),
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::int64_t round_mag(u128 mag, unsigned shift, bool neg)
|
||||||
|
{
|
||||||
|
if (shift >= 128)
|
||||||
|
return 0;
|
||||||
|
if (shift > 0)
|
||||||
|
{
|
||||||
|
mag += u128{1} << (shift - 1);
|
||||||
|
mag >>= shift;
|
||||||
|
}
|
||||||
|
if (mag > static_cast<u128>(INT64_MAX))
|
||||||
|
throw std::overflow_error("range lut: value does not fit int64");
|
||||||
|
const auto out = static_cast<std::int64_t>(mag);
|
||||||
|
return neg ? -out : out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t round_i128(__int128 value, unsigned shift)
|
||||||
|
{
|
||||||
|
const bool neg = value < 0;
|
||||||
|
const auto mag = static_cast<u128>(neg ? -value : value);
|
||||||
|
return round_mag(mag, shift, neg);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t scale_unit(u128 mag64, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return round_mag(mag64, 64u - fractional_bits, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t mul_raw(std::int64_t lhs, std::int64_t rhs, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return round_i128(static_cast<__int128>(lhs) * rhs, fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t div_raw(std::int64_t num, std::int64_t den, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
if (den == 0)
|
||||||
|
throw std::domain_error("range lut: division by zero");
|
||||||
|
const bool neg = (num < 0) != (den < 0);
|
||||||
|
auto n = static_cast<u128>(num < 0 ? -static_cast<__int128>(num) : num);
|
||||||
|
auto d = static_cast<u128>(den < 0 ? -static_cast<__int128>(den) : den);
|
||||||
|
n <<= fractional_bits;
|
||||||
|
const u128 quot = (n + d / 2) / d;
|
||||||
|
return round_mag(quot, 0, neg);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t shift_pow2(std::int64_t value, int places)
|
||||||
|
{
|
||||||
|
if (places == 0 || value == 0)
|
||||||
|
return value;
|
||||||
|
if (places > 0)
|
||||||
|
{
|
||||||
|
if (places >= 62)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
const __int128 wide = static_cast<__int128>(value) << places;
|
||||||
|
if (wide > INT64_MAX || wide < INT64_MIN)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
return static_cast<std::int64_t>(wide);
|
||||||
|
}
|
||||||
|
return round_i128(value, static_cast<unsigned>(-places));
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr std::int64_t one_raw(unsigned fractional_bits) noexcept
|
||||||
|
{
|
||||||
|
return std::int64_t{1} << fractional_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
HEDLEY_CONST
|
||||||
|
HEDLEY_NO_THROW
|
||||||
|
constexpr u128 magnitude_of(std::int64_t raw) noexcept
|
||||||
|
{
|
||||||
|
if (raw >= 0)
|
||||||
|
return static_cast<u128>(raw);
|
||||||
|
return static_cast<u128>(-static_cast<__int128>(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t abs_raw(std::int64_t raw)
|
||||||
|
{
|
||||||
|
const u128 mag = magnitude_of(raw);
|
||||||
|
if (mag > static_cast<u128>(INT64_MAX))
|
||||||
|
throw std::overflow_error("range lut: magnitude does not fit int64");
|
||||||
|
return static_cast<std::int64_t>(mag);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dyadic
|
||||||
|
{
|
||||||
|
std::int64_t mantissa_raw;
|
||||||
|
int power;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline dyadic split_positive(std::int64_t raw, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
if (raw <= 0)
|
||||||
|
throw std::domain_error("range lut: reduction requires a positive input");
|
||||||
|
const auto mag = static_cast<unsigned long long>(raw);
|
||||||
|
const int floor_log = 63 - __builtin_clzll(mag);
|
||||||
|
const int shift = static_cast<int>(fractional_bits) - floor_log - 1;
|
||||||
|
std::int64_t mantissa = shift >= 0
|
||||||
|
? raw << shift
|
||||||
|
: round_i128(raw, static_cast<unsigned>(-shift));
|
||||||
|
int power = floor_log + 1 - static_cast<int>(fractional_bits);
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
const std::int64_t half = one >> 1;
|
||||||
|
if (mantissa >= one)
|
||||||
|
{
|
||||||
|
mantissa >>= 1;
|
||||||
|
++power;
|
||||||
|
}
|
||||||
|
if (mantissa < half)
|
||||||
|
mantissa = half;
|
||||||
|
return dyadic{mantissa, power};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t ln2_raw(unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return scale_unit(ln2_64, fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t eval_ln_positive(unsigned fractional_bits, std::int64_t raw)
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||||
|
return ln_m + static_cast<std::int64_t>(part.power) * ln2_raw(fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t eval_exp_at_scale(unsigned fractional_bits, std::int64_t raw)
|
||||||
|
{
|
||||||
|
if (fractional_bits < 13)
|
||||||
|
{
|
||||||
|
const int lift = static_cast<int>(16u - fractional_bits);
|
||||||
|
const __int128 lifted_arg = static_cast<__int128>(raw) << lift;
|
||||||
|
if (lifted_arg > INT64_MAX || lifted_arg < INT64_MIN)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
const std::int64_t lifted = eval_exp_at_scale(16, static_cast<std::int64_t>(lifted_arg));
|
||||||
|
return round_i128(lifted, static_cast<unsigned>(lift));
|
||||||
|
}
|
||||||
|
const std::int64_t ln2 = ln2_raw(fractional_bits);
|
||||||
|
if (ln2 <= 0)
|
||||||
|
throw std::logic_error("range lut: ln 2 constant");
|
||||||
|
std::int64_t n_bin = raw / ln2;
|
||||||
|
std::int64_t remainder = raw - n_bin * ln2;
|
||||||
|
if (remainder < 0)
|
||||||
|
{
|
||||||
|
remainder += ln2;
|
||||||
|
--n_bin;
|
||||||
|
}
|
||||||
|
while (remainder >= ln2)
|
||||||
|
{
|
||||||
|
remainder -= ln2;
|
||||||
|
++n_bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::int64_t step = std::int64_t{1} << (fractional_bits - 13);
|
||||||
|
const std::int64_t chunks = remainder / step;
|
||||||
|
const std::int64_t tiny = remainder - chunks * step;
|
||||||
|
std::int64_t table_raw = tiny << 13;
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
if (table_raw > one)
|
||||||
|
table_raw = one;
|
||||||
|
std::int64_t exp_s = eval_principal(principal::exp, fractional_bits, table_raw);
|
||||||
|
for (unsigned bit = 0; bit < 13; ++bit)
|
||||||
|
{
|
||||||
|
if ((static_cast<unsigned long long>(chunks) & (1ull << bit)) == 0)
|
||||||
|
continue;
|
||||||
|
// `chunk` is `exp(2^{i-13}) * 2^64`, so the product's high limb is the raw product.
|
||||||
|
const u128 prod = static_cast<u128>(exp_s) * exp_chunk_64[bit];
|
||||||
|
exp_s = round_mag(prod, 64, false);
|
||||||
|
}
|
||||||
|
return shift_pow2(exp_s, static_cast<int>(n_bin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t fractional_raw(std::int64_t raw, unsigned fractional_bits, std::int64_t & whole)
|
||||||
|
{
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
std::int64_t q = raw / one;
|
||||||
|
std::int64_t f = raw - q * one;
|
||||||
|
if (f < 0)
|
||||||
|
{
|
||||||
|
f += one;
|
||||||
|
--q;
|
||||||
|
}
|
||||||
|
whole = q;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t pow10_raw(int exponent, unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
if (exponent == 0)
|
||||||
|
return one;
|
||||||
|
if (exponent < 0)
|
||||||
|
return div_raw(one, pow10_raw(-exponent, fractional_bits), fractional_bits);
|
||||||
|
u128 acc = static_cast<u128>(one);
|
||||||
|
for (int i = 0; i < exponent; ++i)
|
||||||
|
{
|
||||||
|
if (acc > static_cast<u128>(INT64_MAX) / 10)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
acc *= 10;
|
||||||
|
}
|
||||||
|
return static_cast<std::int64_t>(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct angle
|
||||||
|
{
|
||||||
|
unsigned index;
|
||||||
|
std::int64_t frac_raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `{ |x| * multiplier }` at this precision, with the integer part reduced
|
||||||
|
/// only as far as the low bits the quadrant logic reads.
|
||||||
|
inline angle reduce_positive(unsigned fractional_bits, std::int64_t raw, u128 multiplier_64)
|
||||||
|
{
|
||||||
|
const u128 scaled = magnitude_of(raw) * multiplier_64;
|
||||||
|
const u128 rounded = (scaled + (u128{1} << 63)) >> 64;
|
||||||
|
const u128 one = u128{1} << fractional_bits;
|
||||||
|
return angle{
|
||||||
|
static_cast<unsigned>(rounded >> fractional_bits),
|
||||||
|
static_cast<std::int64_t>(rounded & (one - 1)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t principal_sin_fraction(unsigned fractional_bits, std::int64_t fraction_raw, bool complement)
|
||||||
|
{
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
std::int64_t argument = complement ? one - fraction_raw : fraction_raw;
|
||||||
|
if (argument < 0)
|
||||||
|
argument = 0;
|
||||||
|
if (argument > one)
|
||||||
|
argument = one;
|
||||||
|
return eval_principal(principal::sin, fractional_bits, argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t sin_from_angle(unsigned fractional_bits, const angle & turned, int sign)
|
||||||
|
{
|
||||||
|
const unsigned which = turned.index & 3u;
|
||||||
|
const bool complement = which == 1 || which == 3;
|
||||||
|
const int quadrant_sign = (which == 2 || which == 3) ? -1 : 1;
|
||||||
|
const std::int64_t magnitude = principal_sin_fraction(
|
||||||
|
fractional_bits, turned.frac_raw, complement);
|
||||||
|
return magnitude * quadrant_sign * sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t cos_from_angle(unsigned fractional_bits, const angle & turned)
|
||||||
|
{
|
||||||
|
angle shifted = turned;
|
||||||
|
shifted.index += 1;
|
||||||
|
return sin_from_angle(fractional_bits, shifted, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t pi_over_4_raw(unsigned fractional_bits)
|
||||||
|
{
|
||||||
|
return scale_unit(pi_over_4_64, fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t tan_positive(unsigned fractional_bits, std::int64_t magnitude, int quarter_shift)
|
||||||
|
{
|
||||||
|
const angle turned = reduce_positive(fractional_bits, magnitude, four_over_pi_64);
|
||||||
|
const unsigned q = (turned.index + static_cast<unsigned>(quarter_shift)) & 3u;
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
std::int64_t t = (q == 0 || q == 2) ? turned.frac_raw : one - turned.frac_raw;
|
||||||
|
if (t < 0)
|
||||||
|
t = 0;
|
||||||
|
if (t > one)
|
||||||
|
t = one;
|
||||||
|
const std::int64_t z = mul_raw(t, pi_over_4_raw(fractional_bits), fractional_bits);
|
||||||
|
if (q == 0 || q == 3)
|
||||||
|
{
|
||||||
|
const std::int64_t tanf = eval_principal(principal::tanf, fractional_bits, t);
|
||||||
|
const std::int64_t y = mul_raw(z, tanf, fractional_bits);
|
||||||
|
return q == 3 ? -y : y;
|
||||||
|
}
|
||||||
|
if (z == 0)
|
||||||
|
throw std::domain_error("range lut: tan pole");
|
||||||
|
const std::int64_t tang = eval_principal(principal::tang, fractional_bits, t);
|
||||||
|
const std::int64_t y = div_raw(one, z, fractional_bits) + tang;
|
||||||
|
return q == 2 ? -y : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t sec_positive(unsigned fractional_bits, std::int64_t magnitude, int octant_shift)
|
||||||
|
{
|
||||||
|
const angle turned = reduce_positive(fractional_bits, magnitude, four_over_pi_64);
|
||||||
|
const unsigned q8 = (turned.index + static_cast<unsigned>(octant_shift)) & 7u;
|
||||||
|
const unsigned q = q8 & 3u;
|
||||||
|
const int sigma = (q8 & 4u) == 0 ? 1 : -1;
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
std::int64_t t = (q == 0 || q == 2) ? turned.frac_raw : one - turned.frac_raw;
|
||||||
|
if (t < 0)
|
||||||
|
t = 0;
|
||||||
|
if (t > one)
|
||||||
|
t = one;
|
||||||
|
if (q == 0 || q == 3)
|
||||||
|
{
|
||||||
|
const std::int64_t sec = eval_principal(principal::sec, fractional_bits, t);
|
||||||
|
const int sign = (q == 3 ? -1 : 1) * sigma;
|
||||||
|
return sec * sign;
|
||||||
|
}
|
||||||
|
const std::int64_t z = mul_raw(t, pi_over_4_raw(fractional_bits), fractional_bits);
|
||||||
|
if (z == 0)
|
||||||
|
throw std::domain_error("range lut: sec pole");
|
||||||
|
const std::int64_t gsec = eval_principal(principal::gsec, fractional_bits, t);
|
||||||
|
std::int64_t y = div_raw(one, z, fractional_bits) + gsec;
|
||||||
|
if (q == 2)
|
||||||
|
y = -y;
|
||||||
|
return y * sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void quotient_2_13(unsigned fractional_bits, std::int64_t magnitude,
|
||||||
|
std::int64_t & quotient, std::int64_t & remainder)
|
||||||
|
{
|
||||||
|
if (fractional_bits >= 13)
|
||||||
|
{
|
||||||
|
const unsigned shift = fractional_bits - 13;
|
||||||
|
quotient = magnitude >> shift;
|
||||||
|
const std::int64_t mask = shift >= 63 ? INT64_MAX : (std::int64_t{1} << shift) - 1;
|
||||||
|
remainder = shift == 0 ? 0 : magnitude & mask;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int lift = static_cast<int>(13u - fractional_bits);
|
||||||
|
const __int128 wide = static_cast<__int128>(magnitude) << lift;
|
||||||
|
if (wide > INT64_MAX)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
quotient = static_cast<std::int64_t>(wide);
|
||||||
|
remainder = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::int64_t exp_of_quotient(unsigned fractional_bits, std::int64_t quotient, std::int64_t magnitude)
|
||||||
|
{
|
||||||
|
if (quotient == 0)
|
||||||
|
return one_raw(fractional_bits);
|
||||||
|
__int128 argument;
|
||||||
|
if (fractional_bits >= 13)
|
||||||
|
argument = static_cast<__int128>(quotient) << (fractional_bits - 13);
|
||||||
|
else
|
||||||
|
argument = magnitude;
|
||||||
|
if (argument > INT64_MAX)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
return eval_exp_at_scale(fractional_bits, static_cast<std::int64_t>(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hyp
|
||||||
|
{
|
||||||
|
std::int64_t sinh_raw;
|
||||||
|
std::int64_t cosh_raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline hyp sinh_cosh(unsigned fractional_bits, std::int64_t raw)
|
||||||
|
{
|
||||||
|
const bool neg = raw < 0;
|
||||||
|
const auto mag_wide = magnitude_of(raw);
|
||||||
|
if (mag_wide > static_cast<u128>(INT64_MAX))
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
const std::int64_t mag = static_cast<std::int64_t>(mag_wide);
|
||||||
|
std::int64_t quotient = 0;
|
||||||
|
std::int64_t remainder = 0;
|
||||||
|
quotient_2_13(fractional_bits, mag, quotient, remainder);
|
||||||
|
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
std::int64_t table = 0;
|
||||||
|
if (fractional_bits >= 13 && remainder != 0)
|
||||||
|
{
|
||||||
|
const __int128 lifted = static_cast<__int128>(remainder) << 13;
|
||||||
|
table = lifted > one ? one : static_cast<std::int64_t>(lifted);
|
||||||
|
}
|
||||||
|
const std::int64_t sr = eval_principal(principal::sinh, fractional_bits, table);
|
||||||
|
const std::int64_t cr = eval_principal(principal::cosh, fractional_bits, table);
|
||||||
|
std::int64_t sh = sr;
|
||||||
|
std::int64_t ch = cr;
|
||||||
|
if (quotient != 0)
|
||||||
|
{
|
||||||
|
const std::int64_t grown = exp_of_quotient(fractional_bits, quotient, mag);
|
||||||
|
std::int64_t inv = 0;
|
||||||
|
if (grown != 0)
|
||||||
|
inv = div_raw(one, grown, fractional_bits);
|
||||||
|
const std::int64_t sq = round_i128(static_cast<__int128>(grown) - inv, 1);
|
||||||
|
const std::int64_t cq = round_i128(static_cast<__int128>(grown) + inv, 1);
|
||||||
|
const __int128 sinh_sum = static_cast<__int128>(sq) * cr + static_cast<__int128>(cq) * sr;
|
||||||
|
const __int128 cosh_sum = static_cast<__int128>(cq) * cr + static_cast<__int128>(sq) * sr;
|
||||||
|
sh = round_i128(sinh_sum, fractional_bits);
|
||||||
|
ch = round_i128(cosh_sum, fractional_bits);
|
||||||
|
}
|
||||||
|
if (neg)
|
||||||
|
sh = -sh;
|
||||||
|
return hyp{sh, ch};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `ln(2^{k+1} ± 1) / 2`, the saturation threshold used by `tanh` and `coth`.
|
||||||
|
inline std::int64_t beta_raw(unsigned fractional_bits, bool plus)
|
||||||
|
{
|
||||||
|
u128 ln = u128{fractional_bits + 1} * ln2_64;
|
||||||
|
const u128 eps = u128{1} << (63u - fractional_bits);
|
||||||
|
if (plus)
|
||||||
|
ln += eps;
|
||||||
|
else
|
||||||
|
ln -= eps;
|
||||||
|
return round_mag(ln, 65u - fractional_bits, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int half_pow_of(int power)
|
||||||
|
{
|
||||||
|
return (power & 1) != 0 ? (power - 1) / 2 : power / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace range_detail
|
||||||
|
|
||||||
|
HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
inline std::int64_t eval_reduced(reduced which, unsigned fractional_bits, std::int64_t raw)
|
||||||
|
{
|
||||||
|
using namespace range_detail;
|
||||||
|
if (!principal_precision(fractional_bits))
|
||||||
|
throw std::invalid_argument("range lut: precision must be 8, 12, ..., 32");
|
||||||
|
const std::int64_t one = one_raw(fractional_bits);
|
||||||
|
switch (which)
|
||||||
|
{
|
||||||
|
case reduced::ln:
|
||||||
|
return eval_ln_positive(fractional_bits, raw);
|
||||||
|
case reduced::lg:
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||||
|
const std::int64_t lg_m = mul_raw(
|
||||||
|
ln_m, scale_unit(inv_ln2_64, fractional_bits), fractional_bits);
|
||||||
|
return lg_m + (static_cast<std::int64_t>(part.power) << fractional_bits);
|
||||||
|
}
|
||||||
|
case reduced::log10:
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||||
|
const std::int64_t mantissa = mul_raw(
|
||||||
|
ln_m, scale_unit(inv_ln10_64, fractional_bits), fractional_bits);
|
||||||
|
const std::int64_t lift = static_cast<std::int64_t>(part.power)
|
||||||
|
* scale_unit(log10_2_64, fractional_bits);
|
||||||
|
return mantissa + lift;
|
||||||
|
}
|
||||||
|
case reduced::exp:
|
||||||
|
return eval_exp_at_scale(fractional_bits, raw);
|
||||||
|
case reduced::exp2:
|
||||||
|
{
|
||||||
|
std::int64_t whole = 0;
|
||||||
|
const std::int64_t frac = fractional_raw(raw, fractional_bits, whole);
|
||||||
|
const std::int64_t natural = mul_raw(frac, ln2_raw(fractional_bits), fractional_bits);
|
||||||
|
return shift_pow2(eval_exp_at_scale(fractional_bits, natural), static_cast<int>(whole));
|
||||||
|
}
|
||||||
|
case reduced::exp10:
|
||||||
|
{
|
||||||
|
std::int64_t whole = 0;
|
||||||
|
const std::int64_t frac = fractional_raw(raw, fractional_bits, whole);
|
||||||
|
const std::int64_t natural = mul_raw(
|
||||||
|
frac, scale_unit(ln10_64, fractional_bits), fractional_bits);
|
||||||
|
if (whole > 18 || whole < -18)
|
||||||
|
throw std::overflow_error("range lut: exponent overflow");
|
||||||
|
return mul_raw(
|
||||||
|
eval_exp_at_scale(fractional_bits, natural),
|
||||||
|
pow10_raw(static_cast<int>(whole), fractional_bits),
|
||||||
|
fractional_bits);
|
||||||
|
}
|
||||||
|
case reduced::sin:
|
||||||
|
return sin_from_angle(
|
||||||
|
fractional_bits,
|
||||||
|
reduce_positive(fractional_bits, raw, two_over_pi_64),
|
||||||
|
raw < 0 ? -1 : 1);
|
||||||
|
case reduced::cos:
|
||||||
|
return cos_from_angle(
|
||||||
|
fractional_bits, reduce_positive(fractional_bits, raw, two_over_pi_64));
|
||||||
|
case reduced::tan:
|
||||||
|
{
|
||||||
|
const std::int64_t y = tan_positive(fractional_bits, abs_raw(raw), 0);
|
||||||
|
return raw < 0 ? -y : y;
|
||||||
|
}
|
||||||
|
case reduced::cot:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
throw std::domain_error("range lut: cot pole");
|
||||||
|
const std::int64_t y = -tan_positive(fractional_bits, abs_raw(raw), 2);
|
||||||
|
return raw < 0 ? -y : y;
|
||||||
|
}
|
||||||
|
case reduced::sec:
|
||||||
|
return sec_positive(fractional_bits, abs_raw(raw), 0);
|
||||||
|
case reduced::csc:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
throw std::domain_error("range lut: csc pole");
|
||||||
|
const std::int64_t y = sec_positive(fractional_bits, abs_raw(raw), -2);
|
||||||
|
return raw < 0 ? -y : y;
|
||||||
|
}
|
||||||
|
case reduced::sinh:
|
||||||
|
return sinh_cosh(fractional_bits, raw).sinh_raw;
|
||||||
|
case reduced::cosh:
|
||||||
|
return sinh_cosh(fractional_bits, raw).cosh_raw;
|
||||||
|
case reduced::tanh:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
const std::int64_t limit = beta_raw(fractional_bits, false);
|
||||||
|
const std::int64_t mag = abs_raw(raw);
|
||||||
|
if (mag >= limit)
|
||||||
|
return raw < 0 ? -one : one;
|
||||||
|
const hyp pair = sinh_cosh(fractional_bits, raw);
|
||||||
|
return div_raw(pair.sinh_raw, pair.cosh_raw, fractional_bits);
|
||||||
|
}
|
||||||
|
case reduced::coth:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
throw std::domain_error("range lut: coth pole");
|
||||||
|
const std::int64_t limit = beta_raw(fractional_bits, true);
|
||||||
|
const std::int64_t mag = abs_raw(raw);
|
||||||
|
std::int64_t y;
|
||||||
|
if (mag >= limit)
|
||||||
|
y = one;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::int64_t t = div_raw(mag, limit, fractional_bits);
|
||||||
|
const std::int64_t argument = t > one ? one : t;
|
||||||
|
const std::int64_t removed = eval_principal(principal::coth, fractional_bits, argument);
|
||||||
|
y = removed + div_raw(one, mag, fractional_bits);
|
||||||
|
}
|
||||||
|
return raw < 0 ? -y : y;
|
||||||
|
}
|
||||||
|
case reduced::sech:
|
||||||
|
{
|
||||||
|
const std::int64_t ch = sinh_cosh(fractional_bits, raw).cosh_raw;
|
||||||
|
return div_raw(one, ch, fractional_bits);
|
||||||
|
}
|
||||||
|
case reduced::csch:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
throw std::domain_error("range lut: csch pole");
|
||||||
|
const bool neg = raw < 0;
|
||||||
|
const std::int64_t mag = abs_raw(raw);
|
||||||
|
std::int64_t y;
|
||||||
|
if (mag <= one)
|
||||||
|
{
|
||||||
|
const std::int64_t removed = eval_principal(principal::csch, fractional_bits, mag);
|
||||||
|
y = removed + div_raw(one, mag, fractional_bits);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
y = div_raw(one, sinh_cosh(fractional_bits, mag).sinh_raw, fractional_bits);
|
||||||
|
}
|
||||||
|
return neg ? -y : y;
|
||||||
|
}
|
||||||
|
case reduced::sqrt:
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
std::int64_t root = eval_principal(principal::sqrt, fractional_bits, part.mantissa_raw);
|
||||||
|
if ((part.power & 1) != 0)
|
||||||
|
root = mul_raw(root, scale_unit(sqrt2_64, fractional_bits), fractional_bits);
|
||||||
|
return shift_pow2(root, half_pow_of(part.power));
|
||||||
|
}
|
||||||
|
case reduced::inv:
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
const std::int64_t reciprocal = eval_principal(
|
||||||
|
principal::inv, fractional_bits, part.mantissa_raw);
|
||||||
|
return shift_pow2(reciprocal, -part.power);
|
||||||
|
}
|
||||||
|
case reduced::rsqrt:
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
std::int64_t root = eval_principal(principal::rsqrt, fractional_bits, part.mantissa_raw);
|
||||||
|
if ((part.power & 1) != 0)
|
||||||
|
root = mul_raw(root, scale_unit(rsqrt2_64, fractional_bits), fractional_bits);
|
||||||
|
return shift_pow2(root, -half_pow_of(part.power));
|
||||||
|
}
|
||||||
|
case reduced::invsq:
|
||||||
|
{
|
||||||
|
const dyadic part = split_positive(raw, fractional_bits);
|
||||||
|
const std::int64_t square = eval_principal(
|
||||||
|
principal::invsq, fractional_bits, part.mantissa_raw);
|
||||||
|
return shift_pow2(square, -2 * part.power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::invalid_argument("range lut: unknown map");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace grotto
|
||||||
|
|
||||||
|
#endif // LIBDPF_INCLUDE_GROTTO_RANGE_LUT_HPP__
|
||||||
|
|
@ -40,6 +40,10 @@ add_executable(bit_array_test tests/bit_array_test.cpp)
|
||||||
add_executable(parallel_bit_iterable_test tests/parallel_bit_iterable_test.cpp)
|
add_executable(parallel_bit_iterable_test tests/parallel_bit_iterable_test.cpp)
|
||||||
add_executable(setbit_index_iterable_test tests/setbit_index_iterable_test.cpp)
|
add_executable(setbit_index_iterable_test tests/setbit_index_iterable_test.cpp)
|
||||||
add_executable(incremental_test tests/incremental_test.cpp)
|
add_executable(incremental_test tests/incremental_test.cpp)
|
||||||
|
add_executable(path_recipe_test tests/path_recipe_test.cpp)
|
||||||
|
add_executable(blocked_dcf_test tests/blocked_dcf_test.cpp)
|
||||||
|
target_compile_definitions(blocked_dcf_test PRIVATE LIBDPF_HAS_NLOHMANN_JSON)
|
||||||
|
target_include_directories(blocked_dcf_test PRIVATE ../thirdparty/json/include)
|
||||||
add_executable(geneval_test tests/geneval_test.cpp)
|
add_executable(geneval_test tests/geneval_test.cpp)
|
||||||
add_executable(stress_scenarios_test tests/stress_scenarios_test.cpp)
|
add_executable(stress_scenarios_test tests/stress_scenarios_test.cpp)
|
||||||
add_executable(incremental_json_test tests/incremental_json_test.cpp)
|
add_executable(incremental_json_test tests/incremental_json_test.cpp)
|
||||||
|
|
@ -58,14 +62,19 @@ target_compile_options(random_test PRIVATE -ftrapv)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
target_link_libraries(random_test Threads::Threads)
|
target_link_libraries(random_test Threads::Threads)
|
||||||
add_executable(prg_lowmc_test tests/prg_lowmc_test.cpp)
|
add_executable(prg_lowmc_test tests/prg_lowmc_test.cpp)
|
||||||
|
add_executable(prg_chacha_test tests/prg_chacha_test.cpp)
|
||||||
add_executable(secret_share_test tests/secret_share_test.cpp)
|
add_executable(secret_share_test tests/secret_share_test.cpp)
|
||||||
add_executable(beaver_test tests/beaver_test.cpp)
|
add_executable(beaver_test tests/beaver_test.cpp)
|
||||||
add_executable(constant_lut_test tests/constant_lut_test.cpp)
|
add_executable(constant_lut_test tests/constant_lut_test.cpp)
|
||||||
add_executable(signed_prefix_test tests/signed_prefix_test.cpp)
|
add_executable(signed_prefix_test tests/signed_prefix_test.cpp)
|
||||||
add_executable(easy_lut_test tests/easy_lut_test.cpp)
|
add_executable(easy_lut_test tests/easy_lut_test.cpp)
|
||||||
|
add_executable(dyadic_lut_test tests/dyadic_lut_test.cpp)
|
||||||
|
add_executable(nmod_test tests/nmod_test.cpp)
|
||||||
add_executable(principal_lut_test tests/principal_lut_test.cpp)
|
add_executable(principal_lut_test tests/principal_lut_test.cpp)
|
||||||
|
add_executable(range_lut_test tests/range_lut_test.cpp)
|
||||||
add_executable(window_lut_test tests/window_lut_test.cpp)
|
add_executable(window_lut_test tests/window_lut_test.cpp)
|
||||||
add_executable(offset_horner_test tests/offset_horner_test.cpp)
|
add_executable(offset_horner_test tests/offset_horner_test.cpp)
|
||||||
|
add_executable(corner_gaps_test tests/corner_gaps_test.cpp)
|
||||||
|
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(dpf_key_test)
|
gtest_discover_tests(dpf_key_test)
|
||||||
|
|
@ -86,6 +95,8 @@ gtest_discover_tests(bit_array_test)
|
||||||
gtest_discover_tests(parallel_bit_iterable_test)
|
gtest_discover_tests(parallel_bit_iterable_test)
|
||||||
gtest_discover_tests(setbit_index_iterable_test)
|
gtest_discover_tests(setbit_index_iterable_test)
|
||||||
gtest_discover_tests(incremental_test)
|
gtest_discover_tests(incremental_test)
|
||||||
|
gtest_discover_tests(path_recipe_test)
|
||||||
|
gtest_discover_tests(blocked_dcf_test)
|
||||||
gtest_discover_tests(geneval_test)
|
gtest_discover_tests(geneval_test)
|
||||||
gtest_discover_tests(stress_scenarios_test)
|
gtest_discover_tests(stress_scenarios_test)
|
||||||
gtest_discover_tests(incremental_json_test)
|
gtest_discover_tests(incremental_json_test)
|
||||||
|
|
@ -96,11 +107,18 @@ gtest_discover_tests(lane_blast_test)
|
||||||
gtest_discover_tests(context_blast_test)
|
gtest_discover_tests(context_blast_test)
|
||||||
gtest_discover_tests(random_test)
|
gtest_discover_tests(random_test)
|
||||||
gtest_discover_tests(prg_lowmc_test)
|
gtest_discover_tests(prg_lowmc_test)
|
||||||
|
gtest_discover_tests(prg_chacha_test)
|
||||||
gtest_discover_tests(secret_share_test)
|
gtest_discover_tests(secret_share_test)
|
||||||
gtest_discover_tests(beaver_test)
|
gtest_discover_tests(beaver_test)
|
||||||
gtest_discover_tests(constant_lut_test)
|
gtest_discover_tests(constant_lut_test)
|
||||||
gtest_discover_tests(signed_prefix_test)
|
gtest_discover_tests(signed_prefix_test)
|
||||||
gtest_discover_tests(easy_lut_test)
|
gtest_discover_tests(easy_lut_test)
|
||||||
|
gtest_discover_tests(dyadic_lut_test)
|
||||||
|
gtest_discover_tests(nmod_test)
|
||||||
gtest_discover_tests(principal_lut_test)
|
gtest_discover_tests(principal_lut_test)
|
||||||
|
gtest_discover_tests(range_lut_test)
|
||||||
gtest_discover_tests(window_lut_test)
|
gtest_discover_tests(window_lut_test)
|
||||||
gtest_discover_tests(offset_horner_test)
|
gtest_discover_tests(offset_horner_test)
|
||||||
|
add_executable(ic_test tests/ic_test.cpp)
|
||||||
|
gtest_discover_tests(ic_test)
|
||||||
|
gtest_discover_tests(corner_gaps_test)
|
||||||
|
|
|
||||||
|
|
@ -1552,3 +1552,26 @@ TEST(Beaver, RejectsBadUse)
|
||||||
EXPECT_THROW((void)[&] { return a.bit_mul(x, x); }(), std::invalid_argument);
|
EXPECT_THROW((void)[&] { return a.bit_mul(x, x); }(), std::invalid_argument);
|
||||||
(void)z;
|
(void)z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Beaver, ProductExtremes)
|
||||||
|
{
|
||||||
|
const std::tuple<u64, u64, u64> cases[] = {
|
||||||
|
{0ull, 5ull, 0ull},
|
||||||
|
{7ull, 0ull, 0ull},
|
||||||
|
{~u64{0}, ~u64{0}, 1ull},
|
||||||
|
{1ull, ~u64{0}, ~u64{0}},
|
||||||
|
};
|
||||||
|
for (const auto & [a, b, want] : cases)
|
||||||
|
{
|
||||||
|
session64 s;
|
||||||
|
auto x = s.input();
|
||||||
|
auto y = s.input();
|
||||||
|
auto z = s(x * y);
|
||||||
|
Counter rng;
|
||||||
|
s.sample(rng);
|
||||||
|
s.bind(x, a, rng);
|
||||||
|
s.bind(y, b, rng);
|
||||||
|
s.evaluate();
|
||||||
|
EXPECT_EQ(s.open(z), want) << a << " * " << b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
404
test/tests/blocked_dcf_test.cpp
Normal file
404
test/tests/blocked_dcf_test.cpp
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
#include "dpf/json.hpp"
|
||||||
|
#include "grotto/offset_horner.hpp"
|
||||||
|
#include "grotto/prefix_parity.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
simde__m128i g_roots[16];
|
||||||
|
int g_ri = 0;
|
||||||
|
simde__m128i take_root() { return g_roots[g_ri++]; }
|
||||||
|
|
||||||
|
struct PadA
|
||||||
|
{
|
||||||
|
uint64_t n = 1;
|
||||||
|
simde__m128i block()
|
||||||
|
{
|
||||||
|
auto v = simde_mm_set_epi64x(static_cast<long long>(n),
|
||||||
|
static_cast<long long>(n * 9 + 3));
|
||||||
|
n += 2;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
void fill(void * p, std::size_t nbytes)
|
||||||
|
{
|
||||||
|
auto * b = static_cast<unsigned char *>(p);
|
||||||
|
for (std::size_t i = 0; i < nbytes; ++i)
|
||||||
|
b[i] = static_cast<unsigned char>(n + i * 17);
|
||||||
|
n += nbytes;
|
||||||
|
}
|
||||||
|
uint8_t bit() { return static_cast<uint8_t>(n++ & 1u); }
|
||||||
|
};
|
||||||
|
|
||||||
|
void reset_tape()
|
||||||
|
{
|
||||||
|
g_ri = 0;
|
||||||
|
for (int i = 0; i < 16; ++i)
|
||||||
|
g_roots[i] = simde_mm_set_epi64x(0x11110000LL + i, 0x22220000LL + i * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
auto recon(const A & a, const B & b)
|
||||||
|
{
|
||||||
|
return dpf::reconstruct(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K0, typename K1, typename X>
|
||||||
|
uint64_t recon_cmp(const K0 & k0, const K1 & k1, X x)
|
||||||
|
{
|
||||||
|
return recon(dpf::eval_point(dpf::cmp, k0, x),
|
||||||
|
dpf::eval_point(dpf::cmp, k1, x)) & k0.cmp().mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t B, typename Spec>
|
||||||
|
void expect_u8_kind(uint8_t alpha, Spec spec, uint64_t below, uint64_t at,
|
||||||
|
uint64_t above)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::block_width<B>(std::move(spec)));
|
||||||
|
using KT = std::decay_t<decltype(k0)>;
|
||||||
|
EXPECT_GT(KT::cmp_block, 0u);
|
||||||
|
EXPECT_EQ(KT::cmp_q, 2u);
|
||||||
|
EXPECT_EQ(KT::cmp_h, 6u);
|
||||||
|
EXPECT_EQ(k0.value_cw().size(), KT::cmp_checkpoints);
|
||||||
|
EXPECT_EQ(k0.tail_cw().size(), KT::cmp_tail);
|
||||||
|
EXPECT_EQ(KT::depth, KT::cmp_h);
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
const uint64_t got = recon_cmp(k0, k1, q);
|
||||||
|
const uint64_t want = q < alpha ? below : (q == alpha ? at : above);
|
||||||
|
ASSERT_EQ(got, want) << "B=" << B << " x=" << x << " alpha=" << int(alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t B>
|
||||||
|
void exhaustive_lt(uint8_t alpha, uint64_t yt, uint64_t yf)
|
||||||
|
{
|
||||||
|
expect_u8_kind<B>(alpha, dpf::lt(yt, yf), yt, yf, yf);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(BlockedDcf, ExhaustiveUint8AllWidths)
|
||||||
|
{
|
||||||
|
const uint64_t yt = 9, yf = 0;
|
||||||
|
for (uint8_t alpha : {uint8_t{0}, uint8_t{1}, uint8_t{7}, uint8_t{64},
|
||||||
|
uint8_t{127}, uint8_t{200}, uint8_t{255}})
|
||||||
|
{
|
||||||
|
exhaustive_lt<1>(alpha, yt, yf);
|
||||||
|
exhaustive_lt<2>(alpha, yt, yf);
|
||||||
|
exhaustive_lt<3>(alpha, yt, yf);
|
||||||
|
exhaustive_lt<4>(alpha, yt, yf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, KindsIfFalseAndPayloadWidths)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 40;
|
||||||
|
expect_u8_kind<4>(alpha, dpf::lt(uint64_t{9}, uint64_t{2}), 9, 2, 2);
|
||||||
|
expect_u8_kind<4>(alpha, dpf::leq(uint64_t{9}, uint64_t{2}), 9, 9, 2);
|
||||||
|
expect_u8_kind<4>(alpha, dpf::gt(uint64_t{9}, uint64_t{2}), 2, 2, 9);
|
||||||
|
expect_u8_kind<4>(alpha, dpf::geq(uint64_t{9}, uint64_t{2}), 2, 9, 9);
|
||||||
|
expect_u8_kind<4>(alpha, dpf::lt(dpf::bit::one), 1, 0, 0);
|
||||||
|
expect_u8_kind<2>(alpha, dpf::lt(uint16_t{7}, uint16_t{1}), 7, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, Block1MatchesFunctionNotBytes)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 30;
|
||||||
|
auto bare = dpf::make_dpf(alpha, dpf::lt(uint64_t{4}));
|
||||||
|
auto blocked = dpf::make_dpf(alpha, dpf::block_width<1>(dpf::lt(uint64_t{4})));
|
||||||
|
using Bare = std::decay_t<decltype(bare.first)>;
|
||||||
|
using Blk = std::decay_t<decltype(blocked.first)>;
|
||||||
|
EXPECT_NE(Bare::depth, Blk::depth);
|
||||||
|
EXPECT_NE(bare.first.value_cw().size(), blocked.first.value_cw().size());
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
EXPECT_EQ(recon_cmp(bare.first, bare.second, q),
|
||||||
|
recon_cmp(blocked.first, blocked.second, q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, DeeperOutputForcesFullTree)
|
||||||
|
{
|
||||||
|
const uint32_t alpha = 0x01020304u;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, uint32_t{11},
|
||||||
|
dpf::block_width<4>(dpf::lt_at<8>(uint64_t{5}, uint64_t{1})));
|
||||||
|
using KT = std::decay_t<decltype(k0)>;
|
||||||
|
EXPECT_EQ(KT::cmp_q, 0u);
|
||||||
|
EXPECT_EQ(KT::cmp_h, 8u);
|
||||||
|
EXPECT_GT(KT::depth, KT::cmp_h);
|
||||||
|
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)), 11u);
|
||||||
|
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha ^ 1u),
|
||||||
|
*dpf::eval_point(k1, alpha ^ 1u)), 0u);
|
||||||
|
auto top = [](uint32_t v) { return static_cast<uint8_t>(v >> 24); };
|
||||||
|
auto cmp = [&](uint32_t q) { return recon_cmp(k0, k1, q); };
|
||||||
|
EXPECT_EQ(cmp((uint32_t{top(alpha)} - 1u) << 24), 5u);
|
||||||
|
EXPECT_EQ(cmp(alpha), 1u);
|
||||||
|
EXPECT_EQ(cmp((uint32_t{top(alpha)} + 1u) << 24), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, ShallowerLeafKeepsTail)
|
||||||
|
{
|
||||||
|
const uint16_t alpha = 0x1234;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::at<4>(uint8_t{3}),
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{8})));
|
||||||
|
using KT = std::decay_t<decltype(k0)>;
|
||||||
|
EXPECT_EQ(KT::cmp_q, 2u);
|
||||||
|
EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 4>, k0, alpha),
|
||||||
|
*dpf::eval_point(dpf::out<0, 4>, k1, alpha)), 3u);
|
||||||
|
EXPECT_EQ(recon_cmp(k0, k1, uint16_t{alpha - 1}), 8u);
|
||||||
|
EXPECT_EQ(recon_cmp(k0, k1, alpha), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, PointIntervalFullSequenceInnerProduct)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 100;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha,
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{6}, uint64_t{1})));
|
||||||
|
using KT = std::decay_t<decltype(k0)>;
|
||||||
|
auto path0 = dpf::make_basic_path_memoizer(k0);
|
||||||
|
auto path1 = dpf::make_basic_path_memoizer(k1);
|
||||||
|
EXPECT_EQ(recon_cmp(k0, k1, uint8_t{50}), 6u);
|
||||||
|
EXPECT_EQ(dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::cmp, k0, uint8_t{50}, path0),
|
||||||
|
dpf::eval_point(dpf::cmp, k1, uint8_t{50}, path1)) & k0.cmp().mask,
|
||||||
|
6u);
|
||||||
|
EXPECT_EQ(dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::cmp, k0, uint8_t{150}, path0),
|
||||||
|
dpf::eval_point(dpf::cmp, k1, uint8_t{150}, path1)) & k0.cmp().mask,
|
||||||
|
1u);
|
||||||
|
|
||||||
|
auto narrow0 = dpf::make_output_buffer(dpf::cmp, k0, uint8_t{90}, uint8_t{110});
|
||||||
|
auto narrow1 = dpf::make_output_buffer(dpf::cmp, k1, uint8_t{90}, uint8_t{110});
|
||||||
|
dpf::eval_interval(dpf::cmp, k0, uint8_t{90}, uint8_t{110}, narrow0);
|
||||||
|
dpf::eval_interval(dpf::cmp, k1, uint8_t{90}, uint8_t{110}, narrow1);
|
||||||
|
for (uint8_t x = 90; x <= 110; ++x)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(recon(narrow0[x - 90], narrow1[x - 90]) & k0.cmp().mask,
|
||||||
|
recon_cmp(k0, k1, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t stop = KT::cmp_depth;
|
||||||
|
dpf::detail::incr::cmp_full_interval_memo<KT, stop> memo0{21};
|
||||||
|
dpf::detail::incr::cmp_full_interval_memo<KT, stop> memo1{21};
|
||||||
|
auto again0 = dpf::make_output_buffer(dpf::cmp, k0, uint8_t{90}, uint8_t{110});
|
||||||
|
auto again1 = dpf::make_output_buffer(dpf::cmp, k1, uint8_t{90}, uint8_t{110});
|
||||||
|
dpf::eval_interval(dpf::cmp, k0, uint8_t{90}, uint8_t{110}, again0, memo0);
|
||||||
|
dpf::eval_interval(dpf::cmp, k1, uint8_t{90}, uint8_t{110}, again1, memo1);
|
||||||
|
EXPECT_EQ(recon(again0[0], again1[0]) & k0.cmp().mask, 6u);
|
||||||
|
EXPECT_EQ(recon(again0[10], again1[10]) & k0.cmp().mask, 1u);
|
||||||
|
|
||||||
|
auto full0 = dpf::eval_full(dpf::cmp, k0);
|
||||||
|
auto full1 = dpf::eval_full(dpf::cmp, k1);
|
||||||
|
ASSERT_EQ(full0.size(), 256u);
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(recon(full0[x], full1[x]) & k0.cmp().mask,
|
||||||
|
recon_cmp(k0, k1, static_cast<uint8_t>(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, 4> pts{{0, 99, 100, 255}};
|
||||||
|
auto seq0 = dpf::make_output_buffer(dpf::cmp, k0, pts.size());
|
||||||
|
auto seq1 = dpf::make_output_buffer(dpf::cmp, k1, pts.size());
|
||||||
|
dpf::eval_sequence(dpf::cmp, k0, pts.begin(), pts.end(), seq0, path0);
|
||||||
|
dpf::eval_sequence(dpf::cmp, k1, pts.begin(), pts.end(), seq1, path1);
|
||||||
|
for (std::size_t i = 0; i < pts.size(); ++i)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(recon(seq0[i], seq1[i]) & k0.cmp().mask,
|
||||||
|
recon_cmp(k0, k1, pts[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint64_t> w(21, 1);
|
||||||
|
const uint64_t dot =
|
||||||
|
dpf::eval_inner_product(dpf::cmp, k0, uint8_t{90}, uint8_t{110}, w)
|
||||||
|
+ dpf::eval_inner_product(dpf::cmp, k1, uint8_t{90}, uint8_t{110}, w);
|
||||||
|
uint64_t want = 0;
|
||||||
|
for (uint8_t x = 90; x <= 110; ++x)
|
||||||
|
want = (want + recon_cmp(k0, k1, x)) & k0.cmp().mask;
|
||||||
|
EXPECT_EQ(dot & k0.cmp().mask, want);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, DealerMatchesDoernerShelat)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 77;
|
||||||
|
const uint8_t x0 = 3;
|
||||||
|
const uint8_t x1 = static_cast<uint8_t>(alpha ^ x0);
|
||||||
|
reset_tape();
|
||||||
|
auto dealer = dpf::make_dpf(alpha,
|
||||||
|
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{15}, uint64_t{2})));
|
||||||
|
reset_tape();
|
||||||
|
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
|
||||||
|
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{15}, uint64_t{2})));
|
||||||
|
|
||||||
|
EXPECT_EQ(std::memcmp(dealer.first.correction_words().data(),
|
||||||
|
ds.first.correction_words().data(),
|
||||||
|
dealer.first.correction_words().size()
|
||||||
|
* sizeof(dealer.first.correction_words()[0])),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(dealer.first.correction_advice(), ds.first.correction_advice());
|
||||||
|
EXPECT_EQ(dealer.first.value_cw(), ds.first.value_cw());
|
||||||
|
EXPECT_EQ(dealer.first.tail_cw(), ds.first.tail_cw());
|
||||||
|
EXPECT_EQ(dealer.first.cw_last(), ds.first.cw_last());
|
||||||
|
EXPECT_EQ(dealer.first.cmp_addend().raw(), ds.first.cmp_addend().raw());
|
||||||
|
EXPECT_EQ(dealer.second.cmp_addend().raw(), ds.second.cmp_addend().raw());
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
EXPECT_EQ(recon_cmp(dealer.first, dealer.second, q),
|
||||||
|
recon_cmp(ds.first, ds.second, q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, WildcardAssignMatchesConcrete)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 19;
|
||||||
|
const uint64_t yt = 42;
|
||||||
|
reset_tape();
|
||||||
|
auto wild = dpf::make_dpf(alpha,
|
||||||
|
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
|
||||||
|
dpf::block_width<4>(dpf::lt(dpf::wildcard_value<uint64_t>{})));
|
||||||
|
EXPECT_FALSE(wild.first.cmp_assigned());
|
||||||
|
EXPECT_THROW(dpf::eval_point(dpf::cmp, wild.first, alpha), std::exception);
|
||||||
|
reset_tape();
|
||||||
|
auto concrete = dpf::make_dpf(alpha,
|
||||||
|
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
|
||||||
|
dpf::block_width<4>(dpf::lt(yt)));
|
||||||
|
dpf::assign_cmp(wild.first, wild.second, yt);
|
||||||
|
EXPECT_TRUE(wild.first.cmp_assigned());
|
||||||
|
EXPECT_EQ(wild.first.value_cw(), concrete.first.value_cw());
|
||||||
|
EXPECT_EQ(wild.first.tail_cw(), concrete.first.tail_cw());
|
||||||
|
EXPECT_EQ(wild.first.cw_last(), concrete.first.cw_last());
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
EXPECT_EQ(recon_cmp(wild.first, wild.second, q),
|
||||||
|
recon_cmp(concrete.first, concrete.second, q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, TrivialDomainEdges)
|
||||||
|
{
|
||||||
|
auto hi_gt = dpf::make_dpf(uint8_t{255},
|
||||||
|
dpf::block_width<4>(dpf::gt(uint64_t{3})));
|
||||||
|
EXPECT_EQ(hi_gt.first.cmp().trivial, dpf::cmp_trivial::always_false);
|
||||||
|
EXPECT_EQ(recon_cmp(hi_gt.first, hi_gt.second, uint8_t{0}), 0u);
|
||||||
|
EXPECT_EQ(recon_cmp(hi_gt.first, hi_gt.second, uint8_t{255}), 0u);
|
||||||
|
|
||||||
|
auto hi_leq = dpf::make_dpf(uint8_t{255},
|
||||||
|
dpf::block_width<4>(dpf::leq(uint64_t{3})));
|
||||||
|
EXPECT_EQ(hi_leq.first.cmp().trivial, dpf::cmp_trivial::always_true);
|
||||||
|
EXPECT_EQ(recon_cmp(hi_leq.first, hi_leq.second, uint8_t{0}), 3u);
|
||||||
|
EXPECT_EQ(recon_cmp(hi_leq.first, hi_leq.second, uint8_t{255}), 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, JsonRoundTrip)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 12;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha,
|
||||||
|
dpf::block_width<4>(dpf::gt(uint64_t{7}, uint64_t{1})));
|
||||||
|
using KT = typename std::decay_t<decltype(k0)>::key_type;
|
||||||
|
const std::string s0 = dpf::json::to_json(k0.key());
|
||||||
|
const std::string s1 = dpf::json::to_json(k1.key());
|
||||||
|
EXPECT_NE(s0.find("block_width"), std::string::npos);
|
||||||
|
EXPECT_NE(s0.find("tail_cw"), std::string::npos);
|
||||||
|
auto r0 = dpf::json::from_json<KT>(s0);
|
||||||
|
auto r1 = dpf::json::from_json<KT>(s1);
|
||||||
|
EXPECT_EQ(r0.value_cw(), k0.value_cw());
|
||||||
|
EXPECT_EQ(r0.tail_cw(), k0.tail_cw());
|
||||||
|
EXPECT_EQ(r0.cmp().block_width, 4);
|
||||||
|
EXPECT_EQ(r0.cmp().tail_bits, static_cast<int>(KT::cmp_q));
|
||||||
|
const uint64_t mask = k0.cmp().mask;
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
const uint64_t got =
|
||||||
|
(dpf::eval_point(dpf::cmp, r0, q) + dpf::eval_point(dpf::cmp, r1, q))
|
||||||
|
& mask;
|
||||||
|
EXPECT_EQ(got, recon_cmp(k0, k1, q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, GenevalOpensCheckpointWords)
|
||||||
|
{
|
||||||
|
const uint8_t alpha = 20;
|
||||||
|
const uint8_t x0 = 1;
|
||||||
|
const uint8_t x1 = static_cast<uint8_t>(alpha ^ x0);
|
||||||
|
reset_tape();
|
||||||
|
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
|
||||||
|
std::array<uint8_t, 3> ends{{0, 20, 21}};
|
||||||
|
auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng,
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{5})));
|
||||||
|
using KT = std::decay_t<decltype(dpf::make_dpf(alpha,
|
||||||
|
dpf::block_width<4>(dpf::lt(uint64_t{5}))).first)>;
|
||||||
|
EXPECT_EQ(g.value_cw.size(), KT::cmp_checkpoints);
|
||||||
|
EXPECT_EQ(g.tail_cw.size(), KT::cmp_tail);
|
||||||
|
EXPECT_EQ(g.correction_words.size(), KT::depth);
|
||||||
|
EXPECT_EQ((g.party0[0] + g.party1[0]) & g.mask, 5u);
|
||||||
|
EXPECT_EQ((g.party0[1] + g.party1[1]) & g.mask, 0u);
|
||||||
|
EXPECT_EQ((g.party0[2] + g.party1[2]) & g.mask, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BlockedDcf, GrottoPrefixSegmentAndHorner)
|
||||||
|
{
|
||||||
|
const uint16_t alpha = 1000;
|
||||||
|
std::array<uint16_t, 4> ends{{0, 500, 1000, 4000}};
|
||||||
|
auto bare = dpf::make_dpf(alpha, dpf::gt(uint64_t{1}));
|
||||||
|
auto blk = dpf::make_dpf(alpha, dpf::block_width<4>(dpf::gt(uint64_t{1})));
|
||||||
|
const auto b0 = grotto::signed_prefix_parities(bare.first, ends);
|
||||||
|
const auto b1 = grotto::signed_prefix_parities(bare.second, ends);
|
||||||
|
const auto k0 = grotto::signed_prefix_parities(blk.first, ends);
|
||||||
|
const auto k1 = grotto::signed_prefix_parities(blk.second, ends);
|
||||||
|
const uint64_t maskp = bare.first.cmp().mask;
|
||||||
|
for (std::size_t i = 0; i < ends.size(); ++i)
|
||||||
|
EXPECT_EQ((b0[i] + b1[i]) & maskp, (k0[i] + k1[i]) & maskp);
|
||||||
|
|
||||||
|
const auto s0 = grotto::signed_segment_parities(bare.first, ends);
|
||||||
|
const auto s1 = grotto::signed_segment_parities(bare.second, ends);
|
||||||
|
const auto t0 = grotto::signed_segment_parities(blk.first, ends);
|
||||||
|
const auto t1 = grotto::signed_segment_parities(blk.second, ends);
|
||||||
|
for (std::size_t i = 0; i < ends.size(); ++i)
|
||||||
|
EXPECT_EQ((s0[i] + s1[i]) & maskp, (t0[i] + t1[i]) & maskp);
|
||||||
|
|
||||||
|
const uint16_t center = 30;
|
||||||
|
auto mat = grotto::make_offset_horner_keys<uint16_t, 1>(center);
|
||||||
|
uint64_t payload[2];
|
||||||
|
payload[0] = 1;
|
||||||
|
payload[1] = center;
|
||||||
|
using bare_pair = decltype(dpf::make_dpf(center, dpf::gt(uint64_t{0})));
|
||||||
|
using pair = decltype(dpf::make_dpf(center,
|
||||||
|
dpf::block_width<4>(dpf::gt(uint64_t{0}))));
|
||||||
|
std::vector<bare_pair> bare_keys{
|
||||||
|
dpf::make_dpf(center, dpf::gt(payload[0])),
|
||||||
|
dpf::make_dpf(center, dpf::gt(payload[1]))};
|
||||||
|
std::vector<pair> keys{
|
||||||
|
dpf::make_dpf(center, dpf::block_width<4>(dpf::gt(payload[0]))),
|
||||||
|
dpf::make_dpf(center, dpf::block_width<4>(dpf::gt(payload[1])))};
|
||||||
|
std::vector<uint16_t> knots{0, 10, 40};
|
||||||
|
std::vector<std::array<uint64_t, 2>> coeff{
|
||||||
|
{1, 0},
|
||||||
|
{2, 3},
|
||||||
|
{4, 1}};
|
||||||
|
const uint16_t eta = 0;
|
||||||
|
const auto b0s = grotto::offset_horner_coefficient_share<0, 1>(
|
||||||
|
bare_keys, mat.wrap_share, knots, coeff, eta);
|
||||||
|
const auto b1s = grotto::offset_horner_coefficient_share<1, 1>(
|
||||||
|
bare_keys, mat.wrap_share, knots, coeff, eta);
|
||||||
|
const auto c0 = grotto::offset_horner_coefficient_share<0, 1>(
|
||||||
|
keys, mat.wrap_share, knots, coeff, eta);
|
||||||
|
const auto c1 = grotto::offset_horner_coefficient_share<1, 1>(
|
||||||
|
keys, mat.wrap_share, knots, coeff, eta);
|
||||||
|
for (std::size_t m = 0; m < 2; ++m)
|
||||||
|
EXPECT_EQ(b0s[m] + b1s[m], c0[m] + c1[m]);
|
||||||
|
}
|
||||||
390
test/tests/corner_gaps_test.cpp
Normal file
390
test/tests/corner_gaps_test.cpp
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
#include "grotto/fixedpoint.hpp"
|
||||||
|
#include "grotto/fixedpoint_mul.hpp"
|
||||||
|
#include "grotto/principal_lut.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void expect_same(const T & got, const T & point)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(std::memcmp(&got, &point, sizeof(T)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename In, typename Out>
|
||||||
|
void expect_wrapping_interval(In from, In to, In alpha, Out y, std::size_t leaves)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, y);
|
||||||
|
using key_t = std::decay_t<decltype(k0)>;
|
||||||
|
EXPECT_EQ((dpf::utils::get_nodes_in_interval<key_t>(from, to)), leaves);
|
||||||
|
|
||||||
|
auto [buf0, it0] = dpf::eval_interval(k0, from, to);
|
||||||
|
auto [buf1, it1] = dpf::eval_interval(k1, from, to);
|
||||||
|
auto a = std::begin(it0);
|
||||||
|
auto b = std::begin(it1);
|
||||||
|
const auto a_end = std::end(it0);
|
||||||
|
const auto b_end = std::end(it1);
|
||||||
|
|
||||||
|
std::size_t n = 0;
|
||||||
|
In cur = from;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
ASSERT_NE(a, a_end);
|
||||||
|
ASSERT_NE(b, b_end);
|
||||||
|
auto p0 = *dpf::eval_point(k0, cur);
|
||||||
|
auto p1 = *dpf::eval_point(k1, cur);
|
||||||
|
expect_same(*a, p0);
|
||||||
|
expect_same(*b, p1);
|
||||||
|
++a;
|
||||||
|
++b;
|
||||||
|
++n;
|
||||||
|
if (cur == to)
|
||||||
|
break;
|
||||||
|
cur = static_cast<In>(static_cast<std::uint64_t>(cur) + 1u);
|
||||||
|
ASSERT_LT(n, std::size_t{1} << 20);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(a, a_end);
|
||||||
|
EXPECT_EQ(b, b_end);
|
||||||
|
const std::uint64_t width = std::uint64_t{1} << dpf::utils::bitlength_of_v<In>;
|
||||||
|
const std::uint64_t masked = (static_cast<std::uint64_t>(to)
|
||||||
|
- static_cast<std::uint64_t>(from)) & (width - 1);
|
||||||
|
EXPECT_EQ(n, masked + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(CornerGaps, SameLeafWrapUint8Uint32)
|
||||||
|
{
|
||||||
|
expect_wrapping_interval<uint8_t, uint32_t>(10, 9, 40, 0x11111111u, 65);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, AdjacentLeafWrapUint8Uint32)
|
||||||
|
{
|
||||||
|
expect_wrapping_interval<uint8_t, uint32_t>(8, 7, 40, 0x22222222u, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, LongWrapStillMatchesPoint)
|
||||||
|
{
|
||||||
|
expect_wrapping_interval<uint8_t, uint32_t>(200, 10, 3, 0x33333333u, 17);
|
||||||
|
|
||||||
|
auto [k0, k1] = dpf::make_dpf(uint8_t{3}, uint32_t{0x33333333u});
|
||||||
|
using key_t = std::decay_t<decltype(k0)>;
|
||||||
|
auto memo0 = dpf::make_full_tree_interval_memoizer<key_t>(uint8_t{200}, uint8_t{10});
|
||||||
|
auto memo1 = dpf::make_full_tree_interval_memoizer<key_t>(uint8_t{200}, uint8_t{10});
|
||||||
|
auto [buf0, it0] = dpf::eval_interval(k0, uint8_t{200}, uint8_t{10}, std::move(memo0));
|
||||||
|
auto [buf1, it1] = dpf::eval_interval(k1, uint8_t{200}, uint8_t{10}, std::move(memo1));
|
||||||
|
auto p0 = *dpf::eval_point(k0, uint8_t{200});
|
||||||
|
auto p1 = *dpf::eval_point(k1, uint8_t{200});
|
||||||
|
expect_same(*std::begin(it0), p0);
|
||||||
|
expect_same(*std::begin(it1), p1);
|
||||||
|
EXPECT_EQ(dpf::reconstruct(*std::begin(it0), *std::begin(it1)), 0u);
|
||||||
|
auto back0 = std::begin(it0);
|
||||||
|
auto back1 = std::begin(it1);
|
||||||
|
for (int i = 0; i < 66; ++i, ++back0, ++back1) {}
|
||||||
|
auto q0 = *dpf::eval_point(k0, uint8_t{10});
|
||||||
|
auto q1 = *dpf::eval_point(k1, uint8_t{10});
|
||||||
|
expect_same(*back0, q0);
|
||||||
|
expect_same(*back1, q1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, OneOutputPerLeafWrap)
|
||||||
|
{
|
||||||
|
expect_wrapping_interval<uint16_t, simde_uint128>(5, 4, 9, simde_uint128{7}, 65536);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, SaturatedUint64LeafCount)
|
||||||
|
{
|
||||||
|
using in_t = uint64_t;
|
||||||
|
using out_t = simde_uint128;
|
||||||
|
using key_t = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, in_t, out_t>;
|
||||||
|
EXPECT_EQ((dpf::utils::get_nodes_in_interval<key_t>(in_t{1}, ~in_t{0})),
|
||||||
|
std::numeric_limits<std::size_t>::max());
|
||||||
|
EXPECT_THROW((dpf::utils::get_nodes_in_interval<key_t>(in_t{0}, ~in_t{0})),
|
||||||
|
std::length_error);
|
||||||
|
EXPECT_THROW((dpf::utils::get_nodes_in_interval<key_t>(in_t{5}, in_t{4})),
|
||||||
|
std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, MemoizerRejectsALargerInterval)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(uint8_t{4}, uint32_t{1});
|
||||||
|
using key_t = std::decay_t<decltype(k0)>;
|
||||||
|
auto memo = dpf::make_basic_interval_memoizer<key_t>(uint8_t{0}, uint8_t{10});
|
||||||
|
auto buf = dpf::make_output_buffer_for_interval<key_t>(uint8_t{0}, uint8_t{100});
|
||||||
|
EXPECT_THROW(dpf::eval_interval(k0, uint8_t{0}, uint8_t{100}, buf, memo),
|
||||||
|
std::length_error);
|
||||||
|
(void)k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, OddStartInteriorTail)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(uint16_t{3}, simde_uint128{11});
|
||||||
|
for (uint16_t to : {uint16_t{9}, uint16_t{10}})
|
||||||
|
{
|
||||||
|
auto [b0, it0] = dpf::eval_interval(k0, uint16_t{1}, to);
|
||||||
|
auto [b1, it1] = dpf::eval_interval(k1, uint16_t{1}, to);
|
||||||
|
auto a = std::begin(it0);
|
||||||
|
auto b = std::begin(it1);
|
||||||
|
for (uint16_t q = 1; q <= to; ++q, ++a, ++b)
|
||||||
|
{
|
||||||
|
auto p0 = *dpf::eval_point(k0, q);
|
||||||
|
auto p1 = *dpf::eval_point(k1, q);
|
||||||
|
expect_same(*a, p0);
|
||||||
|
expect_same(*b, p1);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(a, std::end(it0));
|
||||||
|
EXPECT_EQ(b, std::end(it1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, PathMemoizerExtremes)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(uint16_t{0x0102}, uint16_t{9});
|
||||||
|
using key_t = std::decay_t<decltype(k0)>;
|
||||||
|
dpf::basic_path_memoizer<key_t> m0;
|
||||||
|
dpf::basic_path_memoizer<key_t> m1;
|
||||||
|
const uint16_t queries[] = {0, 0x8000, 1, 0};
|
||||||
|
for (uint16_t q : queries)
|
||||||
|
{
|
||||||
|
auto a = *dpf::eval_point(k0, q, m0);
|
||||||
|
auto b = *dpf::eval_point(k1, q, m1);
|
||||||
|
auto fa = *dpf::eval_point(k0, q);
|
||||||
|
auto fb = *dpf::eval_point(k1, q);
|
||||||
|
EXPECT_EQ(a, fa) << q;
|
||||||
|
EXPECT_EQ(b, fb) << q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, DepthOneBitInterval)
|
||||||
|
{
|
||||||
|
for (uint8_t alpha : {uint8_t{0}, uint8_t{127}, uint8_t{128}, uint8_t{255}})
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::bit::one);
|
||||||
|
using key_t = std::decay_t<decltype(k0)>;
|
||||||
|
EXPECT_EQ(key_t::depth, 1u);
|
||||||
|
|
||||||
|
auto check = [&](uint8_t from, uint8_t to) {
|
||||||
|
auto [b0, it0] = dpf::eval_interval(k0, from, to);
|
||||||
|
auto [b1, it1] = dpf::eval_interval(k1, from, to);
|
||||||
|
auto a = std::begin(it0);
|
||||||
|
auto b = std::begin(it1);
|
||||||
|
for (uint8_t q = from; ; )
|
||||||
|
{
|
||||||
|
const bool on = q == alpha;
|
||||||
|
const bool bit = static_cast<bool>(*a) != static_cast<bool>(*b);
|
||||||
|
EXPECT_EQ(bit, on) << int(q);
|
||||||
|
++a;
|
||||||
|
++b;
|
||||||
|
if (q == to)
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(a, std::end(it0));
|
||||||
|
EXPECT_EQ(std::end(it1), b);
|
||||||
|
};
|
||||||
|
check(alpha, alpha);
|
||||||
|
check(127, 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, EmptyAndDuplicateSequence)
|
||||||
|
{
|
||||||
|
auto [k0, k1] = dpf::make_dpf(uint8_t{40}, uint8_t{7});
|
||||||
|
std::vector<uint8_t> empty;
|
||||||
|
auto [eb0, eit0] = dpf::eval_sequence(k0, empty.begin(), empty.end());
|
||||||
|
auto [eb1, eit1] = dpf::eval_sequence(k1, empty.begin(), empty.end());
|
||||||
|
EXPECT_EQ(std::begin(eit0), std::end(eit0));
|
||||||
|
EXPECT_EQ(std::begin(eit1), std::end(eit1));
|
||||||
|
|
||||||
|
const std::vector<uint8_t> seq{40, 40, 41};
|
||||||
|
auto [b0, it0] = dpf::eval_sequence(k0, seq.begin(), seq.end());
|
||||||
|
auto [b1, it1] = dpf::eval_sequence(k1, seq.begin(), seq.end());
|
||||||
|
auto a = std::begin(it0);
|
||||||
|
auto b = std::begin(it1);
|
||||||
|
const uint8_t want[] = {7, 7, 0};
|
||||||
|
for (int i = 0; i < 3; ++i, ++a, ++b)
|
||||||
|
EXPECT_EQ(dpf::reconstruct(*a, *b), want[i]) << i;
|
||||||
|
EXPECT_EQ(a, std::end(it0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, IncrementalAdjacentLaneWrap)
|
||||||
|
{
|
||||||
|
const uint16_t alpha = 0x00ab;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::at<8>(uint32_t{0xabcdu}));
|
||||||
|
auto [b0, it0] = dpf::eval_interval(dpf::out<0, 8>, k0, uint8_t{8}, uint8_t{7});
|
||||||
|
auto [b1, it1] = dpf::eval_interval(dpf::out<0, 8>, k1, uint8_t{8}, uint8_t{7});
|
||||||
|
auto a = std::begin(it0);
|
||||||
|
auto b = std::begin(it1);
|
||||||
|
std::size_t n = 0;
|
||||||
|
for (int lane = 8; ; )
|
||||||
|
{
|
||||||
|
const uint16_t query = static_cast<uint16_t>(static_cast<uint16_t>(lane) << 8);
|
||||||
|
auto p0 = *dpf::eval_point(dpf::out<0, 8>, k0, query);
|
||||||
|
auto p1 = *dpf::eval_point(dpf::out<0, 8>, k1, query);
|
||||||
|
EXPECT_EQ(*a, p0) << lane;
|
||||||
|
EXPECT_EQ(*b, p1) << lane;
|
||||||
|
++a;
|
||||||
|
++b;
|
||||||
|
++n;
|
||||||
|
if (lane == 7)
|
||||||
|
break;
|
||||||
|
lane = (lane + 1) & 255;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(n, 256u);
|
||||||
|
EXPECT_EQ(a, std::end(it0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, ModintWideLiteralAndLimbShift)
|
||||||
|
{
|
||||||
|
using namespace dpf::literals;
|
||||||
|
EXPECT_EQ(1_u129, dpf::modint<129>{1});
|
||||||
|
const uint256_t bit128{1, 0};
|
||||||
|
const auto wide = 340282366920938463463374607431768211456_u129;
|
||||||
|
EXPECT_EQ(wide, (dpf::modint<129>{bit128}));
|
||||||
|
|
||||||
|
const bool shift10 = (dpf::modint<10>{1} << 10) == dpf::modint<10>{0};
|
||||||
|
const bool shift16 = (dpf::modint<10>{1} << 16) == dpf::modint<10>{0};
|
||||||
|
const bool shift64 = (dpf::modint<64>{1} << 64) == dpf::modint<64>{0};
|
||||||
|
const bool shift128 = (dpf::modint<65>{1} << 128) == dpf::modint<65>{0};
|
||||||
|
const bool rshift10 = (dpf::modint<10>{5} >> 10) == dpf::modint<10>{0};
|
||||||
|
const bool rshift64 = (dpf::modint<64>{1} >> 64) == dpf::modint<64>{0};
|
||||||
|
EXPECT_TRUE(shift10 && shift16 && shift64 && shift128 && rshift10 && rshift64);
|
||||||
|
|
||||||
|
dpf::modint<10> assigned{1};
|
||||||
|
assigned <<= 16;
|
||||||
|
EXPECT_TRUE(assigned == dpf::modint<10>{0});
|
||||||
|
assigned = dpf::modint<10>{7};
|
||||||
|
assigned >>= 10;
|
||||||
|
EXPECT_TRUE(assigned == dpf::modint<10>{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, SetbitEmptyAndSingleAndNarrowLeaf)
|
||||||
|
{
|
||||||
|
dpf::dynamic_bit_array<> zeros(128);
|
||||||
|
using iter_t = decltype(zeros.begin());
|
||||||
|
dpf::subinterval_iterable<iter_t> all(zeros.begin(), zeros.size(),
|
||||||
|
0, zeros.size() - 1, 0, 0);
|
||||||
|
auto none = dpf::indices_set_in(all);
|
||||||
|
EXPECT_EQ(none.begin(), none.end());
|
||||||
|
|
||||||
|
zeros[0] = true;
|
||||||
|
dpf::subinterval_iterable<iter_t> one(zeros.begin(), zeros.size(),
|
||||||
|
0, zeros.size() - 1, 0, 0);
|
||||||
|
auto set = dpf::indices_set_in(one);
|
||||||
|
auto it = set.begin();
|
||||||
|
ASSERT_NE(it, set.end());
|
||||||
|
EXPECT_EQ(*it, 0u);
|
||||||
|
++it;
|
||||||
|
EXPECT_EQ(it, set.end());
|
||||||
|
|
||||||
|
dpf::dynamic_bit_array<> narrow(128);
|
||||||
|
narrow[0] = true;
|
||||||
|
dpf::subinterval_iterable<iter_t> clipped(narrow.begin(), narrow.size(),
|
||||||
|
0, 1, 0, 2);
|
||||||
|
auto clipped_set = dpf::indices_set_in(clipped);
|
||||||
|
auto cit = clipped_set.begin();
|
||||||
|
ASSERT_NE(cit, clipped_set.end());
|
||||||
|
EXPECT_EQ(*cit, 0u);
|
||||||
|
++cit;
|
||||||
|
EXPECT_EQ(cit, clipped_set.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, EmptyRotationIsEmptyAndZeroIsIdentity)
|
||||||
|
{
|
||||||
|
std::vector<int> empty;
|
||||||
|
dpf::rotation_iterable<std::vector<int>::iterator> none(
|
||||||
|
empty.begin(), empty.end(), 1);
|
||||||
|
EXPECT_EQ(none.begin(), none.end());
|
||||||
|
|
||||||
|
std::vector<int> values{1, 2, 3};
|
||||||
|
dpf::rotation_iterable<std::vector<int>::iterator> id(
|
||||||
|
values.begin(), values.end(), 0);
|
||||||
|
std::vector<int> got;
|
||||||
|
for (auto it = id.begin(); it != id.end(); ++it)
|
||||||
|
got.push_back(*it);
|
||||||
|
EXPECT_EQ(got, values);
|
||||||
|
|
||||||
|
dpf::rotation_iterable<std::vector<int>::iterator> rot(
|
||||||
|
values.begin(), values.end(), 1);
|
||||||
|
got.clear();
|
||||||
|
for (auto it = rot.begin(); it != rot.end(); ++it)
|
||||||
|
got.push_back(*it);
|
||||||
|
EXPECT_EQ(got, (std::vector<int>{2, 3, 1}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, Party1NegatesSignedMinimum)
|
||||||
|
{
|
||||||
|
using sub = dpf::subtractive_share<int32_t, 1>;
|
||||||
|
using add0 = dpf::additive_share<int32_t, 0>;
|
||||||
|
const auto raw = std::numeric_limits<int32_t>::min();
|
||||||
|
const auto party1 = sub::from_raw(raw).as_additive();
|
||||||
|
EXPECT_EQ(party1.raw(), raw);
|
||||||
|
EXPECT_EQ(dpf::reconstruct(add0::from_raw(0), party1), raw);
|
||||||
|
EXPECT_EQ((-sub::from_raw(raw)).raw(), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, FixedMulFloorsAndPrecisionCastDiffersFromLogicalShift)
|
||||||
|
{
|
||||||
|
using q4 = grotto::fixedpoint<4, std::int32_t>;
|
||||||
|
const auto prod = grotto::fixed_mul<8, 4>(q4::from_raw(-3), q4::from_raw(1));
|
||||||
|
EXPECT_EQ(prod.integral_representation(), -1);
|
||||||
|
|
||||||
|
const auto neg_pair = grotto::fixed_mul<8, 4>(q4::from_raw(-3), q4::from_raw(-2));
|
||||||
|
EXPECT_EQ(neg_pair.integral_representation(), 0);
|
||||||
|
|
||||||
|
auto quarter = q4::from_raw(-12);
|
||||||
|
const auto casted = grotto::precision_cast<0>(quarter);
|
||||||
|
EXPECT_EQ(casted.integral_representation(), -1);
|
||||||
|
quarter >>= 4;
|
||||||
|
EXPECT_EQ(quarter.integral_representation(), 268435455);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, Int64MinFactorIsDefined)
|
||||||
|
{
|
||||||
|
using grotto::principal_detail::w_from_i128;
|
||||||
|
using grotto::principal_detail::w_mul_i64;
|
||||||
|
using grotto::principal_detail::w_mul_u64;
|
||||||
|
using grotto::principal_detail::w_neg;
|
||||||
|
const auto value = w_from_i128(3);
|
||||||
|
const auto got = w_mul_i64(value, std::numeric_limits<std::int64_t>::min());
|
||||||
|
const auto want = w_neg(w_mul_u64(value, std::uint64_t{1} << 63));
|
||||||
|
EXPECT_EQ(got.lo, want.lo);
|
||||||
|
EXPECT_EQ(got.hi, want.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CornerGaps, PrgRejectsUint32Seam)
|
||||||
|
{
|
||||||
|
alignas(64) simde__m128i seed = simde_mm_set_epi64x(1, 2);
|
||||||
|
alignas(64) simde__m128i out[4];
|
||||||
|
const auto pos = static_cast<psnip_uint32_t>(UINT32_MAX - 1u);
|
||||||
|
EXPECT_THROW(dpf::prg::aes128::eval(seed, out, 4, pos), std::invalid_argument);
|
||||||
|
EXPECT_THROW(dpf::prg::lowmc128::eval(seed, out, 4, pos), std::invalid_argument);
|
||||||
|
|
||||||
|
const auto ok = static_cast<psnip_uint32_t>(UINT32_MAX - 3u);
|
||||||
|
dpf::prg::aes128::eval(seed, out, 4, ok);
|
||||||
|
for (psnip_uint32_t i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
const auto one = dpf::prg::aes128::eval(seed, ok + i);
|
||||||
|
EXPECT_EQ(std::memcmp(&out[i], &one, sizeof(one)), 0) << i;
|
||||||
|
}
|
||||||
|
dpf::prg::lowmc128::eval(seed, out, 4, ok);
|
||||||
|
for (psnip_uint32_t i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
const auto one = dpf::prg::lowmc128::eval(seed, ok + i);
|
||||||
|
EXPECT_EQ(std::memcmp(&out[i], &one, sizeof(one)), 0) << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_THROW((dpf::randomness::detail::lane_codec<dpf::prg::aes128, simde__m128i>::fill(
|
||||||
|
seed, static_cast<std::uint64_t>(UINT32_MAX) - 1u, out, 4)),
|
||||||
|
std::invalid_argument);
|
||||||
|
}
|
||||||
215
test/tests/dyadic_lut_test.cpp
Normal file
215
test/tests/dyadic_lut_test.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "grotto/dyadic_lut.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
int ref_clz(std::int16_t raw)
|
||||||
|
{
|
||||||
|
const auto bits = static_cast<std::uint16_t>(raw);
|
||||||
|
if (bits == 0)
|
||||||
|
return 16;
|
||||||
|
return __builtin_clz(static_cast<unsigned>(bits)) - (32 - 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ref_clrsb(std::int16_t raw)
|
||||||
|
{
|
||||||
|
const auto bits = static_cast<std::uint16_t>(raw);
|
||||||
|
const bool neg = (bits & 0x8000u) != 0;
|
||||||
|
int count = 0;
|
||||||
|
for (int b = 14; b >= 0; --b)
|
||||||
|
{
|
||||||
|
const bool bit = ((bits >> b) & 1u) != 0;
|
||||||
|
if (bit != neg)
|
||||||
|
break;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ref_ilogb(std::int16_t raw, unsigned k)
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
unsigned mag;
|
||||||
|
if (raw == std::numeric_limits<std::int16_t>::min())
|
||||||
|
mag = 1u << 15;
|
||||||
|
else
|
||||||
|
mag = static_cast<unsigned>(raw < 0 ? -raw : raw);
|
||||||
|
int log = 0;
|
||||||
|
while (mag > 1)
|
||||||
|
{
|
||||||
|
mag >>= 1;
|
||||||
|
++log;
|
||||||
|
}
|
||||||
|
return log - static_cast<int>(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ref_ilog10(std::int64_t raw, unsigned k)
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
return 0;
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
u128 mag;
|
||||||
|
if (raw == std::numeric_limits<std::int64_t>::min())
|
||||||
|
mag = u128{1} << 63;
|
||||||
|
else
|
||||||
|
mag = static_cast<u128>(raw < 0 ? -raw : raw);
|
||||||
|
int e = -static_cast<int>(k) - 2;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
bool ge = false;
|
||||||
|
if (e >= 0)
|
||||||
|
{
|
||||||
|
u128 decade = 1;
|
||||||
|
for (int i = 0; i < e; ++i)
|
||||||
|
decade *= 10;
|
||||||
|
ge = mag >= (decade << k);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u128 decade = 1;
|
||||||
|
for (int i = 0; i < -e; ++i)
|
||||||
|
decade *= 10;
|
||||||
|
const u128 scale = u128{1} << k;
|
||||||
|
u128 threshold = scale / decade;
|
||||||
|
if (scale % decade != 0)
|
||||||
|
++threshold;
|
||||||
|
ge = mag >= threshold;
|
||||||
|
}
|
||||||
|
if (!ge)
|
||||||
|
return e - 1;
|
||||||
|
++e;
|
||||||
|
if (e > 40)
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int64_t enc(std::int64_t units, unsigned k)
|
||||||
|
{
|
||||||
|
return units << k;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(DyadicLut, SignBundleInt16)
|
||||||
|
{
|
||||||
|
const auto positive = grotto::make_positive_lut<std::int16_t>();
|
||||||
|
const auto negative = grotto::make_negative_lut<std::int16_t>();
|
||||||
|
const auto nonnegative = grotto::make_nonnegative_lut<std::int16_t>();
|
||||||
|
const auto nonpositive = grotto::make_nonpositive_lut<std::int16_t>();
|
||||||
|
const auto zero = grotto::make_zero_lut<std::int16_t>();
|
||||||
|
const auto nonzero = grotto::make_nonzero_lut<std::int16_t>();
|
||||||
|
const auto signum = grotto::make_signum_lut<std::int16_t>();
|
||||||
|
EXPECT_EQ(positive.parts(), 2u);
|
||||||
|
EXPECT_EQ(signum.parts(), 3u);
|
||||||
|
EXPECT_EQ(zero.parts(), 3u);
|
||||||
|
|
||||||
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||||||
|
{
|
||||||
|
const auto x = static_cast<std::int16_t>(raw);
|
||||||
|
EXPECT_EQ(positive(x), x > 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(negative(x), x < 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(nonnegative(x), x >= 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(nonpositive(x), x <= 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(zero(x), x == 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(nonzero(x), x != 0 ? 1 : 0);
|
||||||
|
const int sgn = x < 0 ? -1 : (x > 0 ? 1 : 0);
|
||||||
|
EXPECT_EQ(signum(x), sgn);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scaled = grotto::make_positive_lut<std::int16_t>(4);
|
||||||
|
EXPECT_EQ(scaled(std::int16_t{1}), 16);
|
||||||
|
EXPECT_EQ(scaled(std::int16_t{0}), 0);
|
||||||
|
EXPECT_EQ(grotto::make_signum_lut<std::int16_t>(4)(std::int16_t{-3}), -16);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DyadicLut, ClzAndClrsbInt16)
|
||||||
|
{
|
||||||
|
const auto clz = grotto::make_clz_lut<std::int16_t>();
|
||||||
|
const auto clrsb = grotto::make_clrsb_lut<std::int16_t>();
|
||||||
|
EXPECT_EQ(clz.parts(), 17u);
|
||||||
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||||||
|
{
|
||||||
|
const auto x = static_cast<std::int16_t>(raw);
|
||||||
|
EXPECT_EQ(clz(x), ref_clz(x)) << raw;
|
||||||
|
EXPECT_EQ(clrsb(x), ref_clrsb(x)) << raw;
|
||||||
|
}
|
||||||
|
const auto scaled = grotto::make_clz_lut<std::int16_t>(3);
|
||||||
|
EXPECT_EQ(scaled(std::int16_t{1}), ref_clz(1) << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DyadicLut, IntegerLogsInt16)
|
||||||
|
{
|
||||||
|
for (unsigned k : {0u, 4u})
|
||||||
|
{
|
||||||
|
const auto lg = grotto::make_ilogb_lut<std::int16_t>(k);
|
||||||
|
const auto log10 = grotto::make_ilog10_lut<std::int16_t>(k);
|
||||||
|
EXPECT_EQ(lg(std::int16_t{0}), grotto::ilog_of_zero);
|
||||||
|
EXPECT_EQ(log10(std::int16_t{0}), grotto::ilog_of_zero);
|
||||||
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||||||
|
{
|
||||||
|
if (raw == 0)
|
||||||
|
continue;
|
||||||
|
const auto x = static_cast<std::int16_t>(raw);
|
||||||
|
EXPECT_EQ(lg(x), enc(ref_ilogb(x, k), k)) << raw << " k=" << k;
|
||||||
|
EXPECT_EQ(log10(x), enc(ref_ilog10(raw, k), k)) << raw << " k=" << k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(grotto::make_ilogb_lut<std::int16_t>()(std::int16_t{1}), 0);
|
||||||
|
EXPECT_EQ(grotto::make_ilogb_lut<std::int16_t>()(std::int16_t{2}), 1);
|
||||||
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{9}), 0);
|
||||||
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{10}), 1);
|
||||||
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>(4)(std::int16_t{1}), enc(-2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DyadicLut, MostSignificantBits)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < grotto::msb_bit_limit && i < 16; ++i)
|
||||||
|
{
|
||||||
|
const auto lut = grotto::make_msb_lut<std::int16_t>(i);
|
||||||
|
EXPECT_EQ(lut.parts(), std::size_t{1} << (i + 1)) << i;
|
||||||
|
const int shift = 15 - static_cast<int>(i);
|
||||||
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||||||
|
{
|
||||||
|
const auto x = static_cast<std::int16_t>(raw);
|
||||||
|
const int bit = (static_cast<std::uint16_t>(x) >> shift) & 1;
|
||||||
|
EXPECT_EQ(lut(x), bit) << raw << " i=" << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_THROW(grotto::make_msb_lut<std::int16_t>(grotto::msb_bit_limit),
|
||||||
|
std::invalid_argument);
|
||||||
|
const auto scaled = grotto::make_msb_lut<std::int16_t>(0, 4);
|
||||||
|
EXPECT_EQ(scaled(std::int16_t{-1}), 16);
|
||||||
|
EXPECT_EQ(scaled(std::int16_t{1}), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DyadicLut, Int64Edges)
|
||||||
|
{
|
||||||
|
const auto clz = grotto::make_clz_lut<std::int64_t>();
|
||||||
|
EXPECT_EQ(clz(std::int64_t{0}), 64);
|
||||||
|
EXPECT_EQ(clz(std::int64_t{-1}), 0);
|
||||||
|
EXPECT_EQ(clz(std::int64_t{1}), 63);
|
||||||
|
EXPECT_EQ(clz(std::numeric_limits<std::int64_t>::min()), 0);
|
||||||
|
EXPECT_EQ(clz(std::int64_t{1} << 62), 1);
|
||||||
|
|
||||||
|
const auto clrsb = grotto::make_clrsb_lut<std::int64_t>();
|
||||||
|
EXPECT_EQ(clrsb(std::int64_t{0}), 63);
|
||||||
|
EXPECT_EQ(clrsb(std::int64_t{-1}), 63);
|
||||||
|
EXPECT_EQ(clrsb(std::int64_t{1}), 62);
|
||||||
|
EXPECT_EQ(clrsb(std::numeric_limits<std::int64_t>::min()), 0);
|
||||||
|
|
||||||
|
const auto lg = grotto::make_ilogb_lut<std::int64_t>(16);
|
||||||
|
EXPECT_EQ(lg(std::int64_t{1} << 16), 0);
|
||||||
|
EXPECT_EQ(lg(std::int64_t{1} << 17), enc(1, 16));
|
||||||
|
EXPECT_EQ(lg(std::numeric_limits<std::int64_t>::min()), enc(63 - 16, 16));
|
||||||
|
|
||||||
|
const auto bit = grotto::make_msb_lut<std::int64_t>(0);
|
||||||
|
EXPECT_EQ(bit.parts(), 2u);
|
||||||
|
EXPECT_EQ(bit(std::int64_t{-5}), 1);
|
||||||
|
EXPECT_EQ(bit(std::int64_t{5}), 0);
|
||||||
|
}
|
||||||
|
|
@ -362,110 +362,87 @@ TEST(Geneval, EmptySequence)
|
||||||
EXPECT_TRUE(g.correction_words.empty());
|
EXPECT_TRUE(g.correction_words.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, WildcardPointMatchesShiftedEval)
|
TEST(Geneval, ArithPointMatchesDealerAtQuery)
|
||||||
{
|
{
|
||||||
using in_t = uint16_t;
|
using in_t = uint16_t;
|
||||||
using out_t = uint16_t;
|
using out_t = uint16_t;
|
||||||
in_t x = 0x1357;
|
in_t x = 0x1357;
|
||||||
in_t x0 = 0x0100;
|
in_t x0 = 0x0100;
|
||||||
in_t x1 = static_cast<in_t>(x - x0);
|
in_t x1 = static_cast<in_t>(x - x0);
|
||||||
in_t alpha = 0xabcd;
|
|
||||||
in_t query = 0x2000;
|
in_t query = 0x2000;
|
||||||
out_t y = 99;
|
out_t y = 99;
|
||||||
const in_t delta = static_cast<in_t>(alpha - x);
|
|
||||||
const in_t shifted = static_cast<in_t>(query + delta);
|
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto keys = dpf::make_dpf(x, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto g = dpf::geneval_point(dpf::wildcard_input, x0, x1, query, rng<in_t>(),
|
auto g = dpf::geneval_point(dpf::arith_input, x0, x1, query, rng<in_t>(), y);
|
||||||
[&] { return alpha; }, y);
|
|
||||||
|
|
||||||
using key_t = std::decay_t<decltype(keys.first)>;
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
const auto live = live_through_lcp(
|
const auto live = live_through_lcp(
|
||||||
lcp_bits(leaf_of<key_t>(alpha), leaf_of<key_t>(shifted), key_t::depth),
|
lcp_bits(leaf_of<key_t>(x), leaf_of<key_t>(query), key_t::depth),
|
||||||
key_t::depth);
|
key_t::depth);
|
||||||
EXPECT_EQ(g.live_levels, live);
|
EXPECT_EQ(g.live_levels, live);
|
||||||
EXPECT_LT(live, key_t::depth);
|
EXPECT_LT(live, key_t::depth);
|
||||||
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
||||||
g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf));
|
g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf));
|
||||||
|
|
||||||
auto e0 = ev(keys.first, shifted);
|
auto e0 = ev(keys.first, query);
|
||||||
auto e1 = ev(keys.second, shifted);
|
auto e1 = ev(keys.second, query);
|
||||||
EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(e0, e1));
|
EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(e0, e1));
|
||||||
|
|
||||||
dpf::wildcard_value<in_t> slot{alpha};
|
|
||||||
reset_roots();
|
|
||||||
auto wild = dpf::make_dpf(slot, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
|
||||||
auto s0 = wild.first.offset_x.compute_and_get_share(x0);
|
|
||||||
auto s1 = wild.second.offset_x.compute_and_get_share(x1);
|
|
||||||
wild.first.offset_x.reconstruct(s1);
|
|
||||||
wild.second.offset_x.reconstruct(s0);
|
|
||||||
auto w0 = ev(wild.first, query);
|
|
||||||
auto w1 = ev(wild.second, query);
|
|
||||||
EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(w0, w1));
|
|
||||||
expect_prefix_words(wild.first, g.correction_words, g.correction_advice,
|
|
||||||
g.live_levels, false, nullptr, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, WildcardPointOnSecretIsFullKey)
|
TEST(Geneval, ArithPointOnSecretIsFullKey)
|
||||||
{
|
{
|
||||||
using in_t = uint16_t;
|
using in_t = uint16_t;
|
||||||
using out_t = uint16_t;
|
using out_t = uint16_t;
|
||||||
in_t x = 0x42;
|
in_t x = 0x42;
|
||||||
in_t x0 = 0x10;
|
in_t x0 = 0x10;
|
||||||
in_t x1 = static_cast<in_t>(x - x0);
|
in_t x1 = static_cast<in_t>(x - x0);
|
||||||
in_t alpha = 0x1111;
|
|
||||||
out_t y = 8;
|
out_t y = 8;
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto keys = dpf::make_dpf(x, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto g = dpf::geneval_point(dpf::wildcard_input, x0, x1, x, rng<in_t>(),
|
auto g = dpf::geneval_point(dpf::arith_input, x0, x1, x, rng<in_t>(), y);
|
||||||
[&] { return alpha; }, y);
|
|
||||||
|
|
||||||
using key_t = std::decay_t<decltype(keys.first)>;
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
EXPECT_EQ(g.live_levels, key_t::depth);
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
EXPECT_TRUE(g.leaf_live);
|
EXPECT_TRUE(g.leaf_live);
|
||||||
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
||||||
g.live_levels, true, &g.leaf, sizeof(g.leaf));
|
g.live_levels, true, &g.leaf, sizeof(g.leaf));
|
||||||
auto e0 = ev(keys.first, alpha);
|
auto e0 = ev(keys.first, x);
|
||||||
auto e1 = ev(keys.second, alpha);
|
auto e1 = ev(keys.second, x);
|
||||||
EXPECT_EQ(g.party0[0], e0);
|
EXPECT_EQ(g.party0[0], e0);
|
||||||
EXPECT_EQ(g.party1[0], e1);
|
EXPECT_EQ(g.party1[0], e1);
|
||||||
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
|
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, WildcardIntervalAndSequence)
|
TEST(Geneval, ArithIntervalAndSequence)
|
||||||
{
|
{
|
||||||
using in_t = uint8_t;
|
using in_t = uint8_t;
|
||||||
using out_t = uint8_t;
|
using out_t = uint8_t;
|
||||||
in_t x = 40;
|
in_t x = 40;
|
||||||
in_t x0 = 7;
|
in_t x0 = 7;
|
||||||
in_t x1 = static_cast<in_t>(x - x0);
|
in_t x1 = static_cast<in_t>(x - x0);
|
||||||
in_t alpha = 200;
|
|
||||||
out_t y = 3;
|
out_t y = 3;
|
||||||
const in_t delta = static_cast<in_t>(alpha - x);
|
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto keys = dpf::make_dpf(x, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto iv = dpf::geneval_interval(dpf::wildcard_input, x0, x1, in_t{10}, in_t{20},
|
auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, in_t{10}, in_t{20},
|
||||||
rng<in_t>(), [&] { return alpha; }, y);
|
rng<in_t>(), y);
|
||||||
|
|
||||||
using key_t = std::decay_t<decltype(keys.first)>;
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
ASSERT_EQ(iv.party0.size(), 11u);
|
ASSERT_EQ(iv.party0.size(), 11u);
|
||||||
for (in_t q = 10; q <= 20; ++q)
|
for (in_t q = 10; q <= 20; ++q)
|
||||||
{
|
{
|
||||||
const in_t shifted = static_cast<in_t>(q + delta);
|
|
||||||
const std::size_t i = static_cast<std::size_t>(q - 10);
|
const std::size_t i = static_cast<std::size_t>(q - 10);
|
||||||
auto e0 = ev(keys.first, shifted);
|
auto e0 = ev(keys.first, q);
|
||||||
auto e1 = ev(keys.second, shifted);
|
auto e1 = ev(keys.second, q);
|
||||||
EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(e0, e1)) << int(q);
|
EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(e0, e1)) << int(q);
|
||||||
}
|
}
|
||||||
const in_t shifted_from = static_cast<in_t>(10 + delta);
|
|
||||||
const auto live = live_through_lcp(
|
const auto live = live_through_lcp(
|
||||||
lcp_bits(leaf_of<key_t>(alpha), leaf_of<key_t>(shifted_from), key_t::depth),
|
lcp_bits(leaf_of<key_t>(x), leaf_of<key_t>(in_t{10}), key_t::depth),
|
||||||
key_t::depth);
|
key_t::depth);
|
||||||
EXPECT_EQ(iv.live_levels, live);
|
EXPECT_EQ(iv.live_levels, live);
|
||||||
expect_prefix_words(keys.first, iv.correction_words, iv.correction_advice,
|
expect_prefix_words(keys.first, iv.correction_words, iv.correction_advice,
|
||||||
|
|
@ -473,8 +450,8 @@ TEST(Geneval, WildcardIntervalAndSequence)
|
||||||
|
|
||||||
const in_t seq[] = {1, x, 255, 2};
|
const in_t seq[] = {1, x, 255, 2};
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto sq = dpf::geneval_sequence(dpf::wildcard_input, x0, x1,
|
auto sq = dpf::geneval_sequence(dpf::arith_input, x0, x1,
|
||||||
std::begin(seq), std::end(seq), rng<in_t>(), [&] { return alpha; }, y);
|
std::begin(seq), std::end(seq), rng<in_t>(), y);
|
||||||
ASSERT_EQ(sq.party0.size(), 4u);
|
ASSERT_EQ(sq.party0.size(), 4u);
|
||||||
EXPECT_TRUE(sq.leaf_live);
|
EXPECT_TRUE(sq.leaf_live);
|
||||||
EXPECT_EQ(sq.live_levels, key_t::depth);
|
EXPECT_EQ(sq.live_levels, key_t::depth);
|
||||||
|
|
@ -482,31 +459,27 @@ TEST(Geneval, WildcardIntervalAndSequence)
|
||||||
sq.live_levels, true, &sq.leaf, sizeof(sq.leaf));
|
sq.live_levels, true, &sq.leaf, sizeof(sq.leaf));
|
||||||
for (std::size_t i = 0; i < 4; ++i)
|
for (std::size_t i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
const in_t shifted = static_cast<in_t>(seq[i] + delta);
|
auto e0 = ev(keys.first, seq[i]);
|
||||||
auto e0 = ev(keys.first, shifted);
|
auto e1 = ev(keys.second, seq[i]);
|
||||||
auto e1 = ev(keys.second, shifted);
|
|
||||||
EXPECT_EQ(sq.party0[i], e0);
|
EXPECT_EQ(sq.party0[i], e0);
|
||||||
EXPECT_EQ(sq.party1[i], e1);
|
EXPECT_EQ(sq.party1[i], e1);
|
||||||
EXPECT_EQ(recon(sq.party0[i], sq.party1[i]), seq[i] == x ? y : out_t{0});
|
EXPECT_EQ(recon(sq.party0[i], sq.party1[i]), seq[i] == x ? y : out_t{0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, WildcardFullRotates)
|
TEST(Geneval, ArithFullMatchesDealer)
|
||||||
{
|
{
|
||||||
using in_t = uint8_t;
|
using in_t = uint8_t;
|
||||||
using out_t = uint8_t;
|
using out_t = uint8_t;
|
||||||
in_t x = 40;
|
in_t x = 40;
|
||||||
in_t x0 = 7;
|
in_t x0 = 7;
|
||||||
in_t x1 = static_cast<in_t>(x - x0);
|
in_t x1 = static_cast<in_t>(x - x0);
|
||||||
in_t alpha = 200;
|
|
||||||
out_t y = 3;
|
out_t y = 3;
|
||||||
const in_t delta = static_cast<in_t>(alpha - x);
|
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto keys = dpf::make_dpf(x, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto g = dpf::geneval_full(dpf::wildcard_input, x0, x1, rng<in_t>(),
|
auto g = dpf::geneval_full(dpf::arith_input, x0, x1, rng<in_t>(), y);
|
||||||
[&] { return alpha; }, y);
|
|
||||||
|
|
||||||
using key_t = std::decay_t<decltype(keys.first)>;
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
EXPECT_EQ(g.party0.size(), 256u);
|
EXPECT_EQ(g.party0.size(), 256u);
|
||||||
|
|
@ -517,9 +490,8 @@ TEST(Geneval, WildcardFullRotates)
|
||||||
EXPECT_EQ(recon(g.party0[x], g.party1[x]), y);
|
EXPECT_EQ(recon(g.party0[x], g.party1[x]), y);
|
||||||
for (int q = 0; q < 256; ++q)
|
for (int q = 0; q < 256; ++q)
|
||||||
{
|
{
|
||||||
const in_t shifted = static_cast<in_t>(static_cast<in_t>(q) + delta);
|
auto e0 = ev(keys.first, static_cast<in_t>(q));
|
||||||
auto e0 = ev(keys.first, shifted);
|
auto e1 = ev(keys.second, static_cast<in_t>(q));
|
||||||
auto e1 = ev(keys.second, shifted);
|
|
||||||
EXPECT_EQ(g.party0[q], e0);
|
EXPECT_EQ(g.party0[q], e0);
|
||||||
EXPECT_EQ(g.party1[q], e1);
|
EXPECT_EQ(g.party1[q], e1);
|
||||||
}
|
}
|
||||||
|
|
@ -919,7 +891,7 @@ TEST(Geneval, SignedPointIntervalAndCrossZero)
|
||||||
(void)near;
|
(void)near;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, SignedFullAndWildcardFull)
|
TEST(Geneval, SignedFullAndArithFull)
|
||||||
{
|
{
|
||||||
using in_t = int8_t;
|
using in_t = int8_t;
|
||||||
using out_t = int8_t;
|
using out_t = int8_t;
|
||||||
|
|
@ -945,13 +917,11 @@ TEST(Geneval, SignedFullAndWildcardFull)
|
||||||
in_t secret = -20;
|
in_t secret = -20;
|
||||||
in_t a0 = 100;
|
in_t a0 = 100;
|
||||||
in_t a1 = static_cast<in_t>(secret - a0);
|
in_t a1 = static_cast<in_t>(secret - a0);
|
||||||
in_t target = 40;
|
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
||||||
const in_t delta = static_cast<in_t>(target - secret);
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto wkeys = dpf::make_dpf(target, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto wkeys = dpf::make_dpf(secret, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto w = dpf::geneval_full(dpf::wildcard_input, a0, a1, rng<in_t>(),
|
auto w = dpf::geneval_full(dpf::arith_input, a0, a1, rng<in_t>(), y);
|
||||||
[&] { return target; }, y);
|
|
||||||
ASSERT_EQ(w.party0.size(), 256u);
|
ASSERT_EQ(w.party0.size(), 256u);
|
||||||
EXPECT_EQ(w.live_levels, std::decay_t<decltype(wkeys.first)>::depth);
|
EXPECT_EQ(w.live_levels, std::decay_t<decltype(wkeys.first)>::depth);
|
||||||
expect_prefix_words(wkeys.first, w.correction_words, w.correction_advice,
|
expect_prefix_words(wkeys.first, w.correction_words, w.correction_advice,
|
||||||
|
|
@ -959,16 +929,15 @@ TEST(Geneval, SignedFullAndWildcardFull)
|
||||||
for (int q = -128; q <= 127; ++q)
|
for (int q = -128; q <= 127; ++q)
|
||||||
{
|
{
|
||||||
in_t v = static_cast<in_t>(q);
|
in_t v = static_cast<in_t>(q);
|
||||||
in_t shifted = static_cast<in_t>(v + delta);
|
|
||||||
const std::size_t i = static_cast<std::size_t>(to_int(v));
|
const std::size_t i = static_cast<std::size_t>(to_int(v));
|
||||||
EXPECT_EQ(w.party0[i], ev(wkeys.first, shifted)) << q;
|
EXPECT_EQ(w.party0[i], ev(wkeys.first, v)) << q;
|
||||||
EXPECT_EQ(w.party1[i], ev(wkeys.second, shifted)) << q;
|
EXPECT_EQ(w.party1[i], ev(wkeys.second, v)) << q;
|
||||||
}
|
}
|
||||||
EXPECT_EQ(recon(w.party0[static_cast<std::size_t>(to_int(secret))],
|
EXPECT_EQ(recon(w.party0[static_cast<std::size_t>(to_int(secret))],
|
||||||
w.party1[static_cast<std::size_t>(to_int(secret))]), y);
|
w.party1[static_cast<std::size_t>(to_int(secret))]), y);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Geneval, WildcardShareOverflowAndWrappingInterval)
|
TEST(Geneval, ArithShareOverflowAndWrappingInterval)
|
||||||
{
|
{
|
||||||
using in_t = uint8_t;
|
using in_t = uint8_t;
|
||||||
using out_t = uint8_t;
|
using out_t = uint8_t;
|
||||||
|
|
@ -976,15 +945,12 @@ TEST(Geneval, WildcardShareOverflowAndWrappingInterval)
|
||||||
in_t x0 = 200;
|
in_t x0 = 200;
|
||||||
in_t x1 = 66;
|
in_t x1 = 66;
|
||||||
ASSERT_EQ(static_cast<in_t>(x0 + x1), secret);
|
ASSERT_EQ(static_cast<in_t>(x0 + x1), secret);
|
||||||
in_t alpha = 5;
|
|
||||||
out_t y = 17;
|
out_t y = 17;
|
||||||
const in_t delta = static_cast<in_t>(alpha - secret);
|
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
auto keys = dpf::make_dpf(secret, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto on = dpf::geneval_point(dpf::wildcard_input, x0, x1, secret, rng<in_t>(),
|
auto on = dpf::geneval_point(dpf::arith_input, x0, x1, secret, rng<in_t>(), y);
|
||||||
[&] { return alpha; }, y);
|
|
||||||
using key_t = std::decay_t<decltype(keys.first)>;
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
EXPECT_EQ(on.live_levels, key_t::depth);
|
EXPECT_EQ(on.live_levels, key_t::depth);
|
||||||
EXPECT_TRUE(on.leaf_live);
|
EXPECT_TRUE(on.leaf_live);
|
||||||
|
|
@ -993,36 +959,33 @@ TEST(Geneval, WildcardShareOverflowAndWrappingInterval)
|
||||||
EXPECT_EQ(recon(on.party0[0], on.party1[0]), y);
|
EXPECT_EQ(recon(on.party0[0], on.party1[0]), y);
|
||||||
|
|
||||||
in_t query = 250;
|
in_t query = 250;
|
||||||
const in_t shifted = static_cast<in_t>(query + delta);
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto off = dpf::geneval_point(dpf::wildcard_input, x0, x1, query, rng<in_t>(),
|
auto off = dpf::geneval_point(dpf::arith_input, x0, x1, query, rng<in_t>(), y);
|
||||||
[&] { return alpha; }, y);
|
|
||||||
const auto live = live_through_lcp(
|
const auto live = live_through_lcp(
|
||||||
lcp_bits(leaf_of<key_t>(alpha), leaf_of<key_t>(shifted), key_t::depth),
|
lcp_bits(leaf_of<key_t>(secret), leaf_of<key_t>(query), key_t::depth),
|
||||||
key_t::depth);
|
key_t::depth);
|
||||||
EXPECT_EQ(off.live_levels, live);
|
EXPECT_EQ(off.live_levels, live);
|
||||||
expect_prefix_words(keys.first, off.correction_words, off.correction_advice,
|
expect_prefix_words(keys.first, off.correction_words, off.correction_advice,
|
||||||
off.live_levels, off.leaf_live, &off.leaf, sizeof(off.leaf));
|
off.live_levels, off.leaf_live, &off.leaf, sizeof(off.leaf));
|
||||||
EXPECT_EQ(recon(off.party0[0], off.party1[0]),
|
EXPECT_EQ(recon(off.party0[0], off.party1[0]),
|
||||||
recon(ev(keys.first, shifted), ev(keys.second, shifted)));
|
recon(ev(keys.first, query), ev(keys.second, query)));
|
||||||
|
|
||||||
in_t from = 250;
|
in_t from = 250;
|
||||||
in_t to = 10;
|
in_t to = 10;
|
||||||
EXPECT_THROW((dpf::geneval_interval(dpf::wildcard_input, x0, x1, from, to,
|
EXPECT_THROW((dpf::geneval_interval(dpf::arith_input, x0, x1, from, to,
|
||||||
rng<in_t>(), [&] { return alpha; }, y)), std::invalid_argument);
|
rng<in_t>(), y)), std::invalid_argument);
|
||||||
|
|
||||||
from = 250;
|
from = 250;
|
||||||
to = 255;
|
to = 255;
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto iv = dpf::geneval_interval(dpf::wildcard_input, x0, x1, from, to,
|
auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, from, to,
|
||||||
rng<in_t>(), [&] { return alpha; }, y);
|
rng<in_t>(), y);
|
||||||
ASSERT_EQ(iv.party0.size(), 6u);
|
ASSERT_EQ(iv.party0.size(), 6u);
|
||||||
for (in_t q = from; ; ++q)
|
for (in_t q = from; ; ++q)
|
||||||
{
|
{
|
||||||
const std::size_t i = static_cast<std::size_t>(static_cast<in_t>(q - from));
|
const std::size_t i = static_cast<std::size_t>(static_cast<in_t>(q - from));
|
||||||
const in_t s = static_cast<in_t>(q + delta);
|
|
||||||
EXPECT_EQ(recon(iv.party0[i], iv.party1[i]),
|
EXPECT_EQ(recon(iv.party0[i], iv.party1[i]),
|
||||||
recon(ev(keys.first, s), ev(keys.second, s))) << int(q);
|
recon(ev(keys.first, q), ev(keys.second, q))) << int(q);
|
||||||
if (q == to)
|
if (q == to)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1099,26 +1062,19 @@ TEST(Geneval, SignedRegressionsFromTheCornerPass)
|
||||||
EXPECT_EQ(full.party0[bit], ev(keys.first, v)) << q;
|
EXPECT_EQ(full.party0[bit], ev(keys.first, v)) << q;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative target, additive shares that wrap, bound through a real
|
// Negative secret, additive shares that wrap through the signed MSB.
|
||||||
// wildcard key so the raw offset bits are what geneval subtracts.
|
|
||||||
in_t secret = -20;
|
in_t secret = -20;
|
||||||
in_t a0 = 100;
|
in_t a0 = 100;
|
||||||
in_t a1 = static_cast<in_t>(secret - a0);
|
in_t a1 = static_cast<in_t>(secret - a0);
|
||||||
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
||||||
in_t target = -90;
|
|
||||||
in_t query = 60;
|
in_t query = 60;
|
||||||
reset_roots();
|
reset_roots();
|
||||||
dpf::wildcard_value<in_t> slot{target};
|
auto akeys = dpf::make_dpf(secret, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
auto wild = dpf::make_dpf(slot, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
|
||||||
auto s0 = wild.first.offset_x.compute_and_get_share(a0);
|
|
||||||
auto s1 = wild.second.offset_x.compute_and_get_share(a1);
|
|
||||||
wild.first.offset_x.reconstruct(s1);
|
|
||||||
wild.second.offset_x.reconstruct(s0);
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto g = dpf::geneval_point(dpf::wildcard_input, a0, a1, query, rng<in_t>(),
|
auto g = dpf::geneval_point(dpf::arith_input, a0, a1, query, rng<in_t>(), y);
|
||||||
[&] { return target; }, y);
|
EXPECT_EQ(recon(g.party0[0], g.party1[0]),
|
||||||
EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(ev(wild.first, query), ev(wild.second, query)));
|
recon(ev(akeys.first, query), ev(akeys.second, query)));
|
||||||
expect_prefix_words(wild.first, g.correction_words, g.correction_advice,
|
expect_prefix_words(akeys.first, g.correction_words, g.correction_advice,
|
||||||
g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf));
|
g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1223,3 +1179,211 @@ TEST(Geneval, CmpLtIsTheComplementOfTheStrictUpperSet)
|
||||||
EXPECT_EQ(opened, ends[i] < alpha ? 4u : 0u) << int(ends[i]);
|
EXPECT_EQ(opened, ends[i] < alpha ? 4u : 0u) << int(ends[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Geneval, DoernerShelatOnTargetSharesMatch)
|
||||||
|
{
|
||||||
|
using in_t = uint16_t;
|
||||||
|
using out_t = uint16_t;
|
||||||
|
const in_t alpha = 0x55aa;
|
||||||
|
const in_t x0 = 0x1234;
|
||||||
|
const in_t x1 = static_cast<in_t>(alpha ^ x0);
|
||||||
|
const out_t y = 0x9f3c;
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
|
||||||
|
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, y);
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_point(x0, x1, alpha, rng<in_t>(), y);
|
||||||
|
|
||||||
|
using key_t = std::decay_t<decltype(ds.first)>;
|
||||||
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
|
EXPECT_TRUE(g.leaf_live);
|
||||||
|
expect_prefix_words(ds.first, g.correction_words, g.correction_advice,
|
||||||
|
g.live_levels, true, &g.leaf, sizeof(g.leaf));
|
||||||
|
EXPECT_EQ(g.party0[0], ev(ds.first, alpha));
|
||||||
|
EXPECT_EQ(g.party1[0], ev(ds.second, alpha));
|
||||||
|
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Geneval, WideLiveFrontierMatchesDealer)
|
||||||
|
{
|
||||||
|
using in_t = uint16_t;
|
||||||
|
using out_t = uint16_t;
|
||||||
|
const in_t alpha = 0x00ff;
|
||||||
|
const in_t x0 = 0x0f0f;
|
||||||
|
const in_t x1 = static_cast<in_t>(alpha ^ x0);
|
||||||
|
const out_t y = 0xabcd;
|
||||||
|
const in_t from = 0;
|
||||||
|
const in_t to = 0x00ff;
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_interval(x0, x1, from, to, rng<in_t>(), y);
|
||||||
|
|
||||||
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
|
EXPECT_TRUE(g.leaf_live);
|
||||||
|
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
||||||
|
g.live_levels, true, &g.leaf, sizeof(g.leaf));
|
||||||
|
ASSERT_EQ(g.party0.size(), static_cast<std::size_t>(to - from) + 1);
|
||||||
|
for (in_t q = from; ; ++q)
|
||||||
|
{
|
||||||
|
const std::size_t i = static_cast<std::size_t>(q - from);
|
||||||
|
EXPECT_EQ(g.party0[i], ev(keys.first, q)) << q;
|
||||||
|
EXPECT_EQ(g.party1[i], ev(keys.second, q)) << q;
|
||||||
|
const out_t opened = recon(g.party0[i], g.party1[i]);
|
||||||
|
EXPECT_EQ(opened, q == alpha ? y : out_t{0}) << q;
|
||||||
|
if (q == to)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Geneval, CmpLeqGeqNonzeroElseAndDomainMin)
|
||||||
|
{
|
||||||
|
using in_t = uint8_t;
|
||||||
|
const in_t alpha = 10;
|
||||||
|
const in_t x0 = 3;
|
||||||
|
const in_t x1 = static_cast<in_t>(alpha ^ x0);
|
||||||
|
const std::vector<in_t> ends{0, 9, 10, 11, 255};
|
||||||
|
|
||||||
|
auto check = [&](auto spec, auto pred) {
|
||||||
|
auto spec_ds = spec;
|
||||||
|
auto spec_g = spec;
|
||||||
|
reset_roots();
|
||||||
|
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
|
||||||
|
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, spec_ds);
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng<in_t>(), spec_g);
|
||||||
|
using key_t = std::decay_t<decltype(ds.first)>;
|
||||||
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
|
EXPECT_EQ(g.correction_words.size(), key_t::depth);
|
||||||
|
for (std::size_t level = 0; level < key_t::depth; ++level)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(std::memcmp(&g.correction_words[level],
|
||||||
|
&ds.first.correction_word(level), sizeof(simde__m128i)), 0) << level;
|
||||||
|
EXPECT_EQ(g.correction_advice[level], ds.first.correction_advice(level));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < ends.size(); ++i)
|
||||||
|
{
|
||||||
|
const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask;
|
||||||
|
const uint64_t from_key =
|
||||||
|
(dpf::eval_point(dpf::cmp, ds.first, ends[i]).raw()
|
||||||
|
+ dpf::eval_point(dpf::cmp, ds.second, ends[i]).raw())
|
||||||
|
& ds.first.cmp().mask;
|
||||||
|
EXPECT_EQ(opened, from_key) << int(ends[i]);
|
||||||
|
EXPECT_EQ(opened, pred(ends[i])) << int(ends[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
check(dpf::leq(uint64_t{5}, uint64_t{2}), [&](in_t e) {
|
||||||
|
return e <= alpha ? uint64_t{5} : uint64_t{2};
|
||||||
|
});
|
||||||
|
check(dpf::geq(uint64_t{5}, uint64_t{2}), [&](in_t e) {
|
||||||
|
return e >= alpha ? uint64_t{5} : uint64_t{2};
|
||||||
|
});
|
||||||
|
|
||||||
|
using wide = int16_t;
|
||||||
|
const wide amin = std::numeric_limits<wide>::min();
|
||||||
|
const wide w0 = 1;
|
||||||
|
const wide w1 = static_cast<wide>(amin ^ w0);
|
||||||
|
const std::vector<wide> wends{amin, static_cast<wide>(amin + 1), wide{-1}, wide{0},
|
||||||
|
std::numeric_limits<wide>::max()};
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_cmp(w0, w1, wends.begin(), wends.end(), rng<wide>(),
|
||||||
|
dpf::gt(uint64_t{3}));
|
||||||
|
for (std::size_t i = 0; i < wends.size(); ++i)
|
||||||
|
{
|
||||||
|
const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask;
|
||||||
|
EXPECT_EQ(opened, wends[i] > amin ? 3u : 0u) << wends[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Geneval, ArithSignedMsbAndCarryAcrossPowerOfTwo)
|
||||||
|
{
|
||||||
|
using in_t = int8_t;
|
||||||
|
using out_t = int8_t;
|
||||||
|
const in_t secret = -20;
|
||||||
|
const in_t a0 = 100;
|
||||||
|
const in_t a1 = static_cast<in_t>(secret - a0);
|
||||||
|
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
||||||
|
const out_t y = -7;
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
auto keys = dpf::make_dpf(secret, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_point(dpf::arith_input, a0, a1, secret, rng<in_t>(), y);
|
||||||
|
using key_t = std::decay_t<decltype(keys.first)>;
|
||||||
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
|
EXPECT_TRUE(g.leaf_live);
|
||||||
|
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
|
||||||
|
g.live_levels, true, &g.leaf, sizeof(g.leaf));
|
||||||
|
EXPECT_EQ(g.party0[0], ev(keys.first, secret));
|
||||||
|
EXPECT_EQ(g.party1[0], ev(keys.second, secret));
|
||||||
|
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
|
||||||
|
|
||||||
|
// Carry across 2^k: shares that wrap the unsigned modulus.
|
||||||
|
using u8 = uint8_t;
|
||||||
|
const u8 usecret = 7;
|
||||||
|
const u8 x0 = 200;
|
||||||
|
const u8 x1 = static_cast<u8>(usecret - x0);
|
||||||
|
ASSERT_EQ(static_cast<u8>(x0 + x1), usecret);
|
||||||
|
const u8 uy = 9;
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
auto ukeys = dpf::make_dpf(usecret, dpf::root_sampler_t<dpf::prg::aes128>{take_root}, uy);
|
||||||
|
reset_roots();
|
||||||
|
auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, u8{0}, u8{3},
|
||||||
|
rng<u8>(), uy);
|
||||||
|
ASSERT_EQ(iv.party0.size(), 4u);
|
||||||
|
for (u8 q = 0; q <= 3; ++q)
|
||||||
|
{
|
||||||
|
const std::size_t i = static_cast<std::size_t>(q);
|
||||||
|
EXPECT_EQ(iv.party0[i], ev(ukeys.first, q)) << int(q);
|
||||||
|
EXPECT_EQ(iv.party1[i], ev(ukeys.second, q)) << int(q);
|
||||||
|
EXPECT_EQ(recon(iv.party0[i], iv.party1[i]),
|
||||||
|
recon(ev(ukeys.first, q), ev(ukeys.second, q))) << int(q);
|
||||||
|
}
|
||||||
|
using ukey_t = std::decay_t<decltype(ukeys.first)>;
|
||||||
|
EXPECT_EQ(iv.live_levels,
|
||||||
|
live_through_lcp(
|
||||||
|
lcp_bits(leaf_of<ukey_t>(usecret), leaf_of<ukey_t>(u8{0}), ukey_t::depth),
|
||||||
|
ukey_t::depth));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Geneval, ArithDoernerShelatAndCmpMatchDealer)
|
||||||
|
{
|
||||||
|
using in_t = uint8_t;
|
||||||
|
const in_t secret = 40;
|
||||||
|
const in_t a0 = 250;
|
||||||
|
const in_t a1 = static_cast<in_t>(secret - a0);
|
||||||
|
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
||||||
|
const uint64_t beta = 7;
|
||||||
|
const std::vector<in_t> ends{0, 1, 10, 40, 200, 255};
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
auto dealer = dpf::make_dpf(secret, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
|
||||||
|
dpf::gt(beta));
|
||||||
|
reset_roots();
|
||||||
|
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_input, a0, a1, rng<in_t>(),
|
||||||
|
dpf::gt(beta));
|
||||||
|
using key_t = std::decay_t<decltype(dealer.first)>;
|
||||||
|
for (std::size_t level = 0; level < key_t::depth; ++level)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(std::memcmp(&ds.first.correction_word(level),
|
||||||
|
&dealer.first.correction_word(level), sizeof(simde__m128i)), 0) << level;
|
||||||
|
EXPECT_EQ(ds.first.correction_advice(level),
|
||||||
|
dealer.first.correction_advice(level)) << level;
|
||||||
|
EXPECT_EQ(ds.first.value_cw(level), dealer.first.value_cw(level)) << level;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_roots();
|
||||||
|
auto g = dpf::geneval_cmp(dpf::arith_input, a0, a1, ends.begin(), ends.end(),
|
||||||
|
rng<in_t>(), beta);
|
||||||
|
EXPECT_EQ(g.live_levels, key_t::depth);
|
||||||
|
for (std::size_t i = 0; i < ends.size(); ++i)
|
||||||
|
{
|
||||||
|
const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask;
|
||||||
|
EXPECT_EQ(opened, ends[i] > secret ? beta : 0u) << int(ends[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
224
test/tests/ic_test.cpp
Normal file
224
test/tests/ic_test.cpp
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
uint64_t oracle(uint64_t x, uint64_t r, uint64_t p, uint64_t q,
|
||||||
|
uint64_t nmask, uint64_t if_true, uint64_t if_false, uint64_t gmask)
|
||||||
|
{
|
||||||
|
const uint64_t w = (x - r) & nmask;
|
||||||
|
const bool inside = w >= p && w <= q;
|
||||||
|
return (inside ? if_true : if_false) & gmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input, typename Beta>
|
||||||
|
void expect_domain(Input r, Input p, Input q, Beta if_true, Beta if_false,
|
||||||
|
uint64_t gmask)
|
||||||
|
{
|
||||||
|
auto keys = dpf::make_dpf(r, dpf::ic(p, q, if_true, if_false));
|
||||||
|
const uint64_t nmask = keys.first.input_mask;
|
||||||
|
const uint64_t rb = static_cast<uint64_t>(r);
|
||||||
|
const uint64_t pb = static_cast<uint64_t>(p);
|
||||||
|
const uint64_t qb = static_cast<uint64_t>(q);
|
||||||
|
for (uint64_t x = 0; x <= nmask; ++x)
|
||||||
|
{
|
||||||
|
const auto y0 = dpf::eval_point(dpf::ic, keys.first,
|
||||||
|
static_cast<Input>(x));
|
||||||
|
const auto y1 = dpf::eval_point(dpf::ic, keys.second,
|
||||||
|
static_cast<Input>(x));
|
||||||
|
const uint64_t got = static_cast<uint64_t>(dpf::reconstruct(y0, y1)) & gmask;
|
||||||
|
const uint64_t want = oracle(x, rb, pb, qb, nmask,
|
||||||
|
static_cast<uint64_t>(if_true), static_cast<uint64_t>(if_false), gmask);
|
||||||
|
ASSERT_EQ(got, want) << "r=" << rb << " x=" << x
|
||||||
|
<< " p=" << pb << " q=" << qb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(Ic, Uint8FullDomainCorners)
|
||||||
|
{
|
||||||
|
const uint32_t betas[] = {1u, 7u, 255u};
|
||||||
|
const uint32_t falses[] = {0u, 9u};
|
||||||
|
const uint8_t intervals[][2] = {
|
||||||
|
{0, 0}, {0, 255}, {5, 5}, {1, 20}, {200, 250}, {0, 1}, {254, 255}, {10, 40}
|
||||||
|
};
|
||||||
|
for (uint32_t beta : betas)
|
||||||
|
{
|
||||||
|
for (uint32_t f : falses)
|
||||||
|
{
|
||||||
|
for (const auto & iv : intervals)
|
||||||
|
{
|
||||||
|
for (int r = 0; r < 256; r += 17)
|
||||||
|
{
|
||||||
|
expect_domain<uint8_t>(static_cast<uint8_t>(r), iv[0], iv[1],
|
||||||
|
beta, f, 0xffffffffu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, Uint8AllMasksOneInterval)
|
||||||
|
{
|
||||||
|
expect_domain<uint8_t>(uint8_t{0}, uint8_t{10}, uint8_t{20},
|
||||||
|
uint32_t{3}, uint32_t{0}, 0xffffffffu);
|
||||||
|
expect_domain<uint8_t>(uint8_t{200}, uint8_t{10}, uint8_t{100},
|
||||||
|
uint32_t{3}, uint32_t{1}, 0xffffffffu);
|
||||||
|
expect_domain<uint8_t>(uint8_t{255}, uint8_t{0}, uint8_t{255},
|
||||||
|
uint32_t{1}, uint32_t{0}, 0xffffffffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, MemoizerAgrees)
|
||||||
|
{
|
||||||
|
const uint8_t r = 40, p = 7, q = 90;
|
||||||
|
auto keys = dpf::make_dpf(r, dpf::ic(p, q, uint32_t{11}, uint32_t{2}));
|
||||||
|
dpf::basic_path_memoizer<decltype(keys.first.key)> memo0;
|
||||||
|
dpf::basic_path_memoizer<decltype(keys.second.key)> memo1;
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto a = dpf::eval_point(dpf::ic, keys.first, static_cast<uint8_t>(x), memo0);
|
||||||
|
const auto b = dpf::eval_point(dpf::ic, keys.second, static_cast<uint8_t>(x), memo1);
|
||||||
|
const auto c = dpf::eval_point(dpf::ic, keys.first, static_cast<uint8_t>(x));
|
||||||
|
const auto d = dpf::eval_point(dpf::ic, keys.second, static_cast<uint8_t>(x));
|
||||||
|
EXPECT_EQ(dpf::reconstruct(a, b), dpf::reconstruct(c, d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, IntervalAndSequenceBuffers)
|
||||||
|
{
|
||||||
|
const uint8_t r = 15, p = 4, q = 12;
|
||||||
|
auto keys = dpf::make_dpf(r, dpf::ic(p, q, uint16_t{9}));
|
||||||
|
auto buf0 = dpf::make_output_buffer(dpf::ic, keys.first, uint8_t{3}, uint8_t{18});
|
||||||
|
auto buf1 = dpf::make_output_buffer(dpf::ic, keys.second, uint8_t{3}, uint8_t{18});
|
||||||
|
dpf::basic_path_memoizer<decltype(keys.first.key)> memo;
|
||||||
|
dpf::eval_interval(dpf::ic, keys.first, uint8_t{3}, uint8_t{18}, buf0, memo);
|
||||||
|
dpf::eval_interval(dpf::ic, keys.second, uint8_t{3}, uint8_t{18}, buf1);
|
||||||
|
for (std::size_t i = 0; i < buf0.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto point = dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, keys.first, static_cast<uint8_t>(3 + i)),
|
||||||
|
dpf::eval_point(dpf::ic, keys.second, static_cast<uint8_t>(3 + i)));
|
||||||
|
EXPECT_EQ(dpf::reconstruct(buf0[i], buf1[i]), point);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t pts[] = {0, 9, 15, 255, 4};
|
||||||
|
auto s0 = dpf::make_output_buffer(dpf::ic, keys.first, 5);
|
||||||
|
auto s1 = dpf::make_output_buffer(dpf::ic, keys.second, 5);
|
||||||
|
dpf::eval_sequence(dpf::ic, keys.first, std::begin(pts), std::end(pts), s0);
|
||||||
|
dpf::eval_sequence(dpf::ic, keys.second, std::begin(pts), std::end(pts), s1);
|
||||||
|
for (std::size_t i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
const auto point = dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, keys.first, pts[i]),
|
||||||
|
dpf::eval_point(dpf::ic, keys.second, pts[i]));
|
||||||
|
EXPECT_EQ(dpf::reconstruct(s0[i], s1[i]), point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, WildcardAssign)
|
||||||
|
{
|
||||||
|
auto keys = dpf::make_dpf(uint8_t{33},
|
||||||
|
dpf::ic(uint8_t{2}, uint8_t{8}, dpf::wildcard<uint32_t>));
|
||||||
|
EXPECT_THROW(dpf::eval_point(dpf::ic, keys.first, uint8_t{0}), std::invalid_argument);
|
||||||
|
dpf::assign_cmp(keys.first, keys.second, uint32_t{6}, uint32_t{1});
|
||||||
|
expect_domain<uint8_t>(uint8_t{33}, uint8_t{2}, uint8_t{8},
|
||||||
|
uint32_t{6}, uint32_t{1}, 0xffffffffu);
|
||||||
|
// The keys just assigned are a different generation; check those directly.
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const uint64_t got = static_cast<uint64_t>(dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, keys.first, static_cast<uint8_t>(x)),
|
||||||
|
dpf::eval_point(dpf::ic, keys.second, static_cast<uint8_t>(x))));
|
||||||
|
const uint64_t w = static_cast<uint64_t>(static_cast<uint8_t>(x - 33));
|
||||||
|
const uint64_t want = (w >= 2 && w <= 8) ? 6u : 1u;
|
||||||
|
EXPECT_EQ(got, want) << x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, DoernerShelatMatchesDealer)
|
||||||
|
{
|
||||||
|
std::mt19937 rng{7};
|
||||||
|
std::uniform_int_distribution<int> d(0, 255);
|
||||||
|
for (int n = 0; n < 30; ++n)
|
||||||
|
{
|
||||||
|
const uint8_t r0 = static_cast<uint8_t>(d(rng));
|
||||||
|
const uint8_t r1 = static_cast<uint8_t>(d(rng));
|
||||||
|
const uint8_t p = static_cast<uint8_t>(d(rng));
|
||||||
|
const uint8_t q = static_cast<uint8_t>(p + static_cast<uint8_t>(d(rng) % (256 - p)));
|
||||||
|
const uint32_t beta = 1u + static_cast<uint32_t>(d(rng));
|
||||||
|
const uint8_t r = static_cast<uint8_t>(r0 ^ r1);
|
||||||
|
auto dealer = dpf::make_dpf(r, dpf::ic(p, q, beta));
|
||||||
|
struct Pad
|
||||||
|
{
|
||||||
|
simde__m128i block() { return dpf::uniform_sample<simde__m128i>(); }
|
||||||
|
uint8_t bit() { return static_cast<uint8_t>(dpf::uniform_sample<uint8_t>() & 1u); }
|
||||||
|
};
|
||||||
|
dpf::ds_randomness<decltype(&dpf::uniform_sample<simde__m128i>), Pad> rngs{
|
||||||
|
&dpf::uniform_sample<simde__m128i>, {}};
|
||||||
|
auto ds = dpf::make_dpf_doerner_shelat(r0, r1, rngs, dpf::ic(p, q, beta));
|
||||||
|
for (int x = 0; x < 256; x += 5)
|
||||||
|
{
|
||||||
|
const auto dealer_y = dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, dealer.first, static_cast<uint8_t>(x)),
|
||||||
|
dpf::eval_point(dpf::ic, dealer.second, static_cast<uint8_t>(x)));
|
||||||
|
const auto ds_y = dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, ds.first, static_cast<uint8_t>(x)),
|
||||||
|
dpf::eval_point(dpf::ic, ds.second, static_cast<uint8_t>(x)));
|
||||||
|
EXPECT_EQ(dealer_y, ds_y) << "x=" << x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, Geneval)
|
||||||
|
{
|
||||||
|
struct Pad
|
||||||
|
{
|
||||||
|
simde__m128i block() { return dpf::uniform_sample<simde__m128i>(); }
|
||||||
|
uint8_t bit() { return static_cast<uint8_t>(dpf::uniform_sample<uint8_t>() & 1u); }
|
||||||
|
};
|
||||||
|
const uint8_t r0 = 9, r1 = 100, p = 3, q = 50;
|
||||||
|
const uint32_t beta = 4;
|
||||||
|
const uint8_t queries[] = {0, 3, 12, 49, 50, 51, 255};
|
||||||
|
dpf::ds_randomness<decltype(&dpf::uniform_sample<simde__m128i>), Pad> rngs{
|
||||||
|
&dpf::uniform_sample<simde__m128i>, {}};
|
||||||
|
auto opened = dpf::geneval_ic(r0, r1, std::begin(queries), std::end(queries),
|
||||||
|
rngs, dpf::ic(p, q, beta));
|
||||||
|
ASSERT_EQ(opened.party0.size(), 7u);
|
||||||
|
ASSERT_EQ(opened.live_levels, 8u);
|
||||||
|
const uint8_t r = static_cast<uint8_t>(r0 ^ r1);
|
||||||
|
for (std::size_t i = 0; i < 7; ++i)
|
||||||
|
{
|
||||||
|
const uint64_t got = (opened.party0[i] + opened.party1[i]) & 0xffffffffu;
|
||||||
|
const uint64_t w = static_cast<uint64_t>(
|
||||||
|
static_cast<uint8_t>(queries[i] - r));
|
||||||
|
const uint64_t want = (w >= p && w <= q) ? beta : 0u;
|
||||||
|
EXPECT_EQ(got, want) << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, RejectsWrappedBounds)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(dpf::make_dpf(uint8_t{1}, dpf::ic(uint8_t{9}, uint8_t{2}, uint32_t{1})),
|
||||||
|
std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Ic, BitPayload)
|
||||||
|
{
|
||||||
|
auto keys = dpf::make_dpf(uint8_t{4},
|
||||||
|
dpf::ic(uint8_t{1}, uint8_t{3}, dpf::bit::one, dpf::bit::zero));
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto y = dpf::reconstruct(
|
||||||
|
dpf::eval_point(dpf::ic, keys.first, static_cast<uint8_t>(x)),
|
||||||
|
dpf::eval_point(dpf::ic, keys.second, static_cast<uint8_t>(x)));
|
||||||
|
const uint64_t w = static_cast<uint64_t>(static_cast<uint8_t>(x - 4));
|
||||||
|
EXPECT_EQ(static_cast<bool>(y), w >= 1 && w <= 3) << x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -448,7 +448,7 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
|
||||||
EXPECT_EQ(opened(sq.party0[i], sq.party1[i]), seq[i] == alpha ? y : out_t{});
|
EXPECT_EQ(opened(sq.party0[i], sq.party1[i]), seq[i] == alpha ? y : out_t{});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LaneBlast, GenevalNybleWildcardAndSigned)
|
TEST(LaneBlast, GenevalNybleArithAndSigned)
|
||||||
{
|
{
|
||||||
using out_t = dpf::nyble;
|
using out_t = dpf::nyble;
|
||||||
const out_t y{0x0c};
|
const out_t y{0x0c};
|
||||||
|
|
@ -459,20 +459,19 @@ TEST(LaneBlast, GenevalNybleWildcardAndSigned)
|
||||||
const in_t a0 = 200;
|
const in_t a0 = 200;
|
||||||
const in_t a1 = 66;
|
const in_t a1 = 66;
|
||||||
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
ASSERT_EQ(static_cast<in_t>(a0 + a1), secret);
|
||||||
const in_t target = 5;
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto keys = dpf::make_dpf(target,
|
auto keys = dpf::make_dpf(secret,
|
||||||
dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto g = dpf::geneval_point(dpf::wildcard_input, a0, a1, secret,
|
auto g = dpf::geneval_point(dpf::arith_input, a0, a1, secret,
|
||||||
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, [&] { return target; }, y);
|
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
|
||||||
EXPECT_TRUE(g.leaf_live);
|
EXPECT_TRUE(g.leaf_live);
|
||||||
expect_live_words(keys.first, g);
|
expect_live_words(keys.first, g);
|
||||||
EXPECT_EQ(opened(g.party0[0], g.party1[0]), y);
|
EXPECT_EQ(opened(g.party0[0], g.party1[0]), y);
|
||||||
|
|
||||||
reset_roots();
|
reset_roots();
|
||||||
auto miss = dpf::geneval_point(dpf::wildcard_input, a0, a1, in_t{250},
|
auto miss = dpf::geneval_point(dpf::arith_input, a0, a1, in_t{250},
|
||||||
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, [&] { return target; }, y);
|
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
|
||||||
EXPECT_EQ(opened(miss.party0[0], miss.party1[0]), out_t{});
|
EXPECT_EQ(opened(miss.party0[0], miss.party1[0]), out_t{});
|
||||||
expect_live_words(keys.first, miss);
|
expect_live_words(keys.first, miss);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
176
test/tests/nmod_test.cpp
Normal file
176
test/tests/nmod_test.cpp
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "grotto/nmod.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
using u128 = unsigned __int128;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
grotto::nmod_result oracle(std::int64_t x_raw, unsigned x_bits, std::uint64_t recip,
|
||||||
|
unsigned recip_bits, unsigned residue_bits)
|
||||||
|
{
|
||||||
|
const unsigned scale = x_bits + recip_bits;
|
||||||
|
const __int128 prod = static_cast<__int128>(x_raw) * static_cast<__int128>(recip);
|
||||||
|
const bool neg = prod < 0;
|
||||||
|
const auto mag = static_cast<u128>(neg ? -prod : prod);
|
||||||
|
const u128 mask = scale >= 128 ? ~u128{0} : (u128{1} << scale) - 1;
|
||||||
|
const u128 quot_mag = scale >= 128 ? 0 : mag >> scale;
|
||||||
|
const u128 rem = scale >= 128 ? mag : mag & mask;
|
||||||
|
grotto::nmod_result out;
|
||||||
|
if (!neg)
|
||||||
|
out.quotient = static_cast<std::int64_t>(quot_mag);
|
||||||
|
else if (rem == 0)
|
||||||
|
out.quotient = -static_cast<std::int64_t>(quot_mag);
|
||||||
|
else
|
||||||
|
out.quotient = -static_cast<std::int64_t>(quot_mag) - 1;
|
||||||
|
if (residue_bits == 0 || rem == 0)
|
||||||
|
return out;
|
||||||
|
u128 field = rem;
|
||||||
|
if (neg)
|
||||||
|
field = (u128{1} << scale) - rem;
|
||||||
|
if (scale >= residue_bits)
|
||||||
|
field >>= scale - residue_bits;
|
||||||
|
else
|
||||||
|
field <<= residue_bits - scale;
|
||||||
|
const u128 unit = u128{1} << residue_bits;
|
||||||
|
out.residue = static_cast<std::int64_t>(field & (unit - 1));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(Nmod, SplitsAnIntegerModulusOnTheFractionalBoundary)
|
||||||
|
{
|
||||||
|
// 3.25 = 13/4, modulo 1.
|
||||||
|
const auto split = grotto::nmod(13, 2, 1, 0, 2);
|
||||||
|
EXPECT_EQ(split.quotient, 3);
|
||||||
|
EXPECT_EQ(split.residue, 1);
|
||||||
|
|
||||||
|
// -1.25 = -5/4. floor is -2 and the residue is 0.75.
|
||||||
|
const auto neg = grotto::nmod(-5, 2, 1, 0, 2);
|
||||||
|
EXPECT_EQ(neg.quotient, -2);
|
||||||
|
EXPECT_EQ(neg.residue, 3);
|
||||||
|
|
||||||
|
// Coarser residue truncates toward -infinity: floor(0.75 * 2) = 1.
|
||||||
|
EXPECT_EQ(grotto::nmod(-5, 2, 1, 0, 1).residue, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, ExactNegativeIntegersHaveAZeroResidue)
|
||||||
|
{
|
||||||
|
const auto split = grotto::nmod(-8, 2, 1, 0, 2);
|
||||||
|
EXPECT_EQ(split.quotient, -2);
|
||||||
|
EXPECT_EQ(split.residue, 0);
|
||||||
|
EXPECT_EQ(grotto::nmod(0, 8, 1, 0, 8).quotient, 0);
|
||||||
|
EXPECT_EQ(grotto::nmod(0, 8, 1, 0, 8).residue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, FloorDoesNotRoundUpToTheNextQuotient)
|
||||||
|
{
|
||||||
|
// 1 - 2^{-16}. A round-to-nearest reciprocal product would become 1.
|
||||||
|
const auto split = grotto::nmod(1, 0, (std::uint64_t{1} << 16) - 1, 16, 8);
|
||||||
|
EXPECT_EQ(split.quotient, 0);
|
||||||
|
EXPECT_EQ(split.residue, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, PowerOfTwoModulusIsAnExactShift)
|
||||||
|
{
|
||||||
|
// 1.5 / 2^{-1} = 3 exactly.
|
||||||
|
const auto half = grotto::nmod_pow2(6, 2, 1, 2);
|
||||||
|
EXPECT_EQ(half.quotient, 3);
|
||||||
|
EXPECT_EQ(half.residue, 0);
|
||||||
|
|
||||||
|
// 1.5 / 2 = 0.75.
|
||||||
|
const auto two = grotto::nmod_pow2(6, 2, -1, 2);
|
||||||
|
EXPECT_EQ(two.quotient, 0);
|
||||||
|
EXPECT_EQ(two.residue, 3);
|
||||||
|
|
||||||
|
// 2^x splits at the integer.
|
||||||
|
const auto unit = grotto::nmod_pow2(6, 2, 0, 2);
|
||||||
|
EXPECT_EQ(unit.quotient, 1);
|
||||||
|
EXPECT_EQ(unit.residue, 2);
|
||||||
|
|
||||||
|
const auto via_recip = grotto::nmod(6, 2, 2, 0, 2);
|
||||||
|
EXPECT_EQ(half.quotient, via_recip.quotient);
|
||||||
|
EXPECT_EQ(half.residue, via_recip.residue);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, IntegerReciprocalAgreesWithFloorDivision)
|
||||||
|
{
|
||||||
|
// round(2^100 / 3) == floor(2^100 / 3) for this width.
|
||||||
|
const u128 recip = (u128{1} << 100) / 3;
|
||||||
|
const auto split = grotto::nmod(10, 0, recip, 100, 16);
|
||||||
|
EXPECT_EQ(split.quotient, 3);
|
||||||
|
EXPECT_EQ(split.residue, 21845);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, WideQuarterTurnReciprocal)
|
||||||
|
{
|
||||||
|
// RN(4/π · 2^80). x = 2.5 at 8 fractional bits, residue at 16 bits.
|
||||||
|
const u128 recip = (u128{83443} << 64) | 494442167743545356ULL;
|
||||||
|
const auto split = grotto::nmod(640, 8, recip, 80, 16);
|
||||||
|
EXPECT_EQ(split.quotient, 3);
|
||||||
|
EXPECT_EQ(split.residue, 11999);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, MatchesFloorOnRandomReciprocals)
|
||||||
|
{
|
||||||
|
std::mt19937 rng(0x6d6f64u);
|
||||||
|
std::uniform_int_distribution<int> values(-4000, 4000);
|
||||||
|
std::uniform_int_distribution<int> bits(0, 20);
|
||||||
|
std::uniform_int_distribution<unsigned> recip_dist(1, 100000);
|
||||||
|
for (int i = 0; i < 4000; ++i)
|
||||||
|
{
|
||||||
|
const unsigned x_bits = static_cast<unsigned>(bits(rng));
|
||||||
|
const unsigned recip_bits = static_cast<unsigned>(bits(rng));
|
||||||
|
const unsigned residue_bits = static_cast<unsigned>(bits(rng) % 16);
|
||||||
|
const std::int64_t x_raw = values(rng);
|
||||||
|
const std::uint64_t recip = recip_dist(rng);
|
||||||
|
const auto got = grotto::nmod(x_raw, x_bits, recip, recip_bits, residue_bits);
|
||||||
|
const auto want = oracle(x_raw, x_bits, recip, recip_bits, residue_bits);
|
||||||
|
EXPECT_EQ(got.quotient, want.quotient) << i;
|
||||||
|
EXPECT_EQ(got.residue, want.residue) << i;
|
||||||
|
if (got.quotient != want.quotient || got.residue != want.residue)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, PowerOfTwoAgreesWithTheGeneralSplit)
|
||||||
|
{
|
||||||
|
std::mt19937 rng(13);
|
||||||
|
std::uniform_int_distribution<int> values(-2000, 2000);
|
||||||
|
std::uniform_int_distribution<int> exps(-12, 12);
|
||||||
|
for (int i = 0; i < 500; ++i)
|
||||||
|
{
|
||||||
|
const int exp = exps(rng);
|
||||||
|
const unsigned residue_bits = static_cast<unsigned>(i % 10);
|
||||||
|
const std::int64_t x_raw = values(rng);
|
||||||
|
const auto got = grotto::nmod_pow2(x_raw, 8, exp, residue_bits);
|
||||||
|
grotto::nmod_result want;
|
||||||
|
if (exp >= 0)
|
||||||
|
want = grotto::nmod(x_raw, 8, std::uint64_t{1} << exp, 0, residue_bits);
|
||||||
|
else
|
||||||
|
want = grotto::nmod(x_raw, 8, 1, static_cast<unsigned>(-exp), residue_bits);
|
||||||
|
EXPECT_EQ(got.quotient, want.quotient);
|
||||||
|
EXPECT_EQ(got.residue, want.residue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, RejectsAZeroReciprocalAndAHugeQuotient)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(grotto::nmod(1, 0, 0, 0, 4), std::invalid_argument);
|
||||||
|
EXPECT_THROW(grotto::nmod(1, 0, 1, 0, 64), std::invalid_argument);
|
||||||
|
EXPECT_THROW(grotto::nmod_pow2(1, 0, 128, 4), std::overflow_error);
|
||||||
|
EXPECT_THROW(grotto::nmod(INT64_MAX, 0, u128{1} << 80, 0, 4),
|
||||||
|
std::overflow_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Nmod, MostNegativeInputModuloOne)
|
||||||
|
{
|
||||||
|
const auto split = grotto::nmod(INT64_MIN, 0, 1, 0, 4);
|
||||||
|
EXPECT_EQ(split.quotient, INT64_MIN);
|
||||||
|
EXPECT_EQ(split.residue, 0);
|
||||||
|
}
|
||||||
|
|
@ -835,6 +835,33 @@ TEST(OffsetHorner, HornerOfOpenedCoefficientsMatchesValue)
|
||||||
EXPECT_EQ(y, gold<D>(center, eta, knots, coeff));
|
EXPECT_EQ(y, gold<D>(center, eta, knots, coeff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(OffsetHorner, OpenedSharesAreNotHornerInputs)
|
||||||
|
{
|
||||||
|
constexpr std::size_t D = 2;
|
||||||
|
const std::vector<uint8_t> knots{0, 50, 150};
|
||||||
|
const auto coeff = take_degree<D>(pad3({
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
{0, 4, 1, 0},
|
||||||
|
{8, 0, 0, 0},
|
||||||
|
}));
|
||||||
|
const uint8_t center = 10;
|
||||||
|
const uint8_t eta = 60;
|
||||||
|
auto mat = grotto::make_offset_horner_keys<uint8_t, D>(center);
|
||||||
|
const auto c = open_coeffs<D>(mat, knots, coeff, eta);
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (uint64_t ck : c)
|
||||||
|
sum += ck;
|
||||||
|
const uint64_t value = gold<D>(center, eta, knots, coeff);
|
||||||
|
EXPECT_EQ(sum, value);
|
||||||
|
EXPECT_EQ(value, 5180u);
|
||||||
|
|
||||||
|
uint64_t horner = c[D];
|
||||||
|
const uint64_t limb = lift(center);
|
||||||
|
for (std::size_t k = D; k-- > 0; )
|
||||||
|
horner = horner * limb + c[k];
|
||||||
|
EXPECT_NE(horner, value);
|
||||||
|
}
|
||||||
|
|
||||||
template <std::size_t Degree, typename T>
|
template <std::size_t Degree, typename T>
|
||||||
void expect_wrapped(const grotto::offset_horner_keys<T, Degree> & mat,
|
void expect_wrapped(const grotto::offset_horner_keys<T, Degree> & mat,
|
||||||
const std::vector<T> & knots,
|
const std::vector<T> & knots,
|
||||||
|
|
|
||||||
223
test/tests/path_recipe_test.cpp
Normal file
223
test/tests/path_recipe_test.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B, typename Target>
|
||||||
|
uint64_t recon_cmp(const A & a, const B & b, Target target, uint8_t x)
|
||||||
|
{
|
||||||
|
return dpf::reconstruct(dpf::eval_point(target, a, x),
|
||||||
|
dpf::eval_point(target, b, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t lcp_len(uint8_t x, uint8_t alpha, std::size_t n = 8, std::size_t width = 8)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
const uint8_t shift = static_cast<uint8_t>(width - 1 - i);
|
||||||
|
if (((x >> shift) & 1) != ((alpha >> shift) & 1))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t high_prefix(uint8_t alpha, uint64_t d, std::size_t n = 8)
|
||||||
|
{
|
||||||
|
if (d == 0)
|
||||||
|
return 0;
|
||||||
|
if (d >= n)
|
||||||
|
return alpha & ((1u << n) - 1u);
|
||||||
|
const unsigned drop = static_cast<unsigned>(n - d);
|
||||||
|
return (static_cast<unsigned>(alpha) >> drop) << drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t low_prefix(uint8_t alpha, uint64_t d, std::size_t n = 8)
|
||||||
|
{
|
||||||
|
if (d == 0)
|
||||||
|
return 0;
|
||||||
|
if (d >= n)
|
||||||
|
return alpha;
|
||||||
|
return static_cast<unsigned>(alpha) >> (n - d);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Make, typename Unit>
|
||||||
|
void expect_domain(Make make, Unit unit_of)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0xB4;
|
||||||
|
auto [k0, k1] = make(alpha);
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto got = recon_cmp(k0, k1, dpf::cmp, static_cast<uint8_t>(x));
|
||||||
|
EXPECT_EQ(got, unit_of(static_cast<uint8_t>(x), alpha))
|
||||||
|
<< "x=" << x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(PathRecipe, LengthMaskPrefixBreakAndPacked)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0xB4;
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) { return dpf::make_dpf(a, dpf::lcp(uint64_t{1})); },
|
||||||
|
[](uint8_t x, uint8_t a) { return lcp_len(x, a); });
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) { return dpf::make_dpf(a, dpf::common_prefix(uint64_t{1})); },
|
||||||
|
[](uint8_t x, uint8_t a) { return high_prefix(a, lcp_len(x, a)); });
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) { return dpf::make_dpf(a, dpf::prefix_mask(uint64_t{1})); },
|
||||||
|
[](uint8_t x, uint8_t a) { return high_prefix(0xFF, lcp_len(x, a)); });
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) { return dpf::make_dpf(a, dpf::diverge_one_hot(uint64_t{1})); },
|
||||||
|
[](uint8_t x, uint8_t a) { return 1ULL << lcp_len(x, a); });
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) { return dpf::make_dpf(a, dpf::break_bit(uint64_t{3})); },
|
||||||
|
[](uint8_t x, uint8_t a) {
|
||||||
|
const auto d = lcp_len(x, a);
|
||||||
|
if (d >= 8)
|
||||||
|
return 0ULL;
|
||||||
|
return 3ULL * ((a >> (7 - d)) & 1);
|
||||||
|
});
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) {
|
||||||
|
return dpf::make_dpf(a, dpf::prefix_with_length<4>(uint64_t{1}));
|
||||||
|
},
|
||||||
|
[](uint8_t x, uint8_t a) {
|
||||||
|
const auto d = lcp_len(x, a);
|
||||||
|
return (low_prefix(a, d) << 4) | d;
|
||||||
|
});
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) {
|
||||||
|
return dpf::make_dpf(a, dpf::lcp(uint64_t{5}, uint64_t{2}));
|
||||||
|
},
|
||||||
|
[](uint8_t x, uint8_t a) { return 2ULL + 3ULL * lcp_len(x, a); });
|
||||||
|
expect_domain(
|
||||||
|
[](uint8_t a) {
|
||||||
|
return dpf::make_dpf(a, dpf::path_paint(
|
||||||
|
[](std::size_t matched, uint64_t, bool) {
|
||||||
|
return static_cast<uint64_t>(matched);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[](uint8_t x, uint8_t a) { return lcp_len(x, a); });
|
||||||
|
|
||||||
|
auto [p0, p1] = dpf::make_dpf(alpha, dpf::lcp_at<4>(uint64_t{1}));
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto got = recon_cmp(p0, p1, dpf::cmp, static_cast<uint8_t>(x));
|
||||||
|
EXPECT_EQ(got, lcp_len(static_cast<uint8_t>(x), alpha, 4, 8)) << x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PathRecipe, WildcardScaleAssignsLength)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0x3C;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::lcp(dpf::wildcard<uint64_t>));
|
||||||
|
dpf::assign_cmp(k0, k1, uint64_t{4});
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(recon_cmp(k0, k1, dpf::cmp, static_cast<uint8_t>(x)),
|
||||||
|
4ULL * lcp_len(static_cast<uint8_t>(x), alpha))
|
||||||
|
<< x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PathRecipe, IdpfSlotsArePrefixPointFunctions)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0xA6;
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha,
|
||||||
|
dpf::idpf(uint64_t{11}, uint64_t{22}, uint64_t{33}));
|
||||||
|
auto slot = [&](auto target, uint8_t x) {
|
||||||
|
return dpf::reconstruct(*dpf::eval_point(target, k0, x),
|
||||||
|
*dpf::eval_point(target, k1, x));
|
||||||
|
};
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto d = lcp_len(static_cast<uint8_t>(x), alpha);
|
||||||
|
EXPECT_EQ(slot(dpf::out<0>, static_cast<uint8_t>(x)), d >= 1 ? 11u : 0u);
|
||||||
|
EXPECT_EQ(slot(dpf::out<1>, static_cast<uint8_t>(x)), d >= 2 ? 22u : 0u);
|
||||||
|
EXPECT_EQ(slot(dpf::out<2>, static_cast<uint8_t>(x)), d >= 3 ? 33u : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [s0, s1] = dpf::make_dpf(alpha, dpf::idpf_at<4, 7>(uint8_t{9}, uint8_t{8}));
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto d = lcp_len(static_cast<uint8_t>(x), alpha);
|
||||||
|
auto at = [&](auto target) {
|
||||||
|
return dpf::reconstruct(*dpf::eval_point(target, s0, static_cast<uint8_t>(x)),
|
||||||
|
*dpf::eval_point(target, s1, static_cast<uint8_t>(x)));
|
||||||
|
};
|
||||||
|
EXPECT_EQ(at(dpf::out<0>), d >= 4 ? 9u : 0u);
|
||||||
|
EXPECT_EQ(at(dpf::out<1>), d >= 7 ? 8u : 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PathRecipe, IdcfMatchesComparisonAtEveryPrefix)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0x6E;
|
||||||
|
auto check = [&](auto idcf_spec, auto at_spec, auto full_spec, std::size_t L,
|
||||||
|
auto target) {
|
||||||
|
auto [i0, i1] = dpf::make_dpf(alpha, idcf_spec);
|
||||||
|
auto [n0, n1] = dpf::make_dpf(alpha, at_spec);
|
||||||
|
auto [f0, f1] = dpf::make_dpf(alpha, full_spec);
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
EXPECT_EQ(recon_cmp(i0, i1, target, q), recon_cmp(n0, n1, dpf::cmp, q))
|
||||||
|
<< "L=" << L << " x=" << x;
|
||||||
|
EXPECT_EQ(recon_cmp(i0, i1, dpf::cmp, q), recon_cmp(f0, f1, dpf::cmp, q))
|
||||||
|
<< "full x=" << x;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(i0.prefix_cw(8), i0.cw_last());
|
||||||
|
};
|
||||||
|
check(dpf::idcf(dpf::lt(uint64_t{1})), dpf::lt_at<4>(uint64_t{1}),
|
||||||
|
dpf::lt(uint64_t{1}), 4, dpf::cmp_prefix<4>);
|
||||||
|
check(dpf::idcf(dpf::leq(uint64_t{1})), dpf::leq_at<3>(uint64_t{1}),
|
||||||
|
dpf::leq(uint64_t{1}), 3, dpf::cmp_prefix<3>);
|
||||||
|
check(dpf::idcf(dpf::gt(uint64_t{1})), dpf::gt_at<5>(uint64_t{1}),
|
||||||
|
dpf::gt(uint64_t{1}), 5, dpf::cmp_prefix<5>);
|
||||||
|
check(dpf::idcf(dpf::geq(uint64_t{1})), dpf::geq_at<1>(uint64_t{1}),
|
||||||
|
dpf::geq(uint64_t{1}), 1, dpf::cmp_prefix<1>);
|
||||||
|
|
||||||
|
auto [z0, z1] = dpf::make_dpf(alpha, dpf::idcf(dpf::lt(uint64_t{1})));
|
||||||
|
auto [e0, e1] = dpf::make_dpf(alpha, dpf::idcf(dpf::leq(uint64_t{1})));
|
||||||
|
auto [g0, g1] = dpf::make_dpf(alpha, dpf::idcf(dpf::gt(uint64_t{1})));
|
||||||
|
auto [q0, q1] = dpf::make_dpf(alpha, dpf::idcf(dpf::geq(uint64_t{1})));
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
const auto q = static_cast<uint8_t>(x);
|
||||||
|
EXPECT_EQ(recon_cmp(z0, z1, dpf::cmp_prefix<0>, q), 0u);
|
||||||
|
EXPECT_EQ(recon_cmp(e0, e1, dpf::cmp_prefix<0>, q), 1u);
|
||||||
|
EXPECT_EQ(recon_cmp(g0, g1, dpf::cmp_prefix<0>, q), 0u);
|
||||||
|
EXPECT_EQ(recon_cmp(q0, q1, dpf::cmp_prefix<0>, q), 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PathRecipe, DoernerShelatAndGenevalMatchLength)
|
||||||
|
{
|
||||||
|
constexpr uint8_t alpha = 0x91;
|
||||||
|
const uint8_t x0 = 0x10;
|
||||||
|
const uint8_t x1 = static_cast<uint8_t>(alpha ^ x0);
|
||||||
|
auto [k0, k1] = dpf::make_dpf_doerner_shelat(x0, x1, dpf::lcp(uint64_t{1}));
|
||||||
|
for (int x = 0; x < 256; ++x)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(recon_cmp(k0, k1, dpf::cmp, static_cast<uint8_t>(x)),
|
||||||
|
lcp_len(static_cast<uint8_t>(x), alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
dpf::ds_randomness<simde__m128i (*)(), dpf::detail::urandom_pad_rng> rng{
|
||||||
|
dpf::uniform_sample<simde__m128i>, {}};
|
||||||
|
const uint8_t ends[] = {0x00, 0x91, 0xFF};
|
||||||
|
auto g = dpf::geneval_cmp(x0, x1, std::begin(ends), std::end(ends), rng,
|
||||||
|
dpf::lcp(uint64_t{1}));
|
||||||
|
ASSERT_EQ(g.party0.size(), 3u);
|
||||||
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
EXPECT_EQ((g.party0[i] + g.party1[i]) & g.mask,
|
||||||
|
lcp_len(ends[i], alpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
245
test/tests/prg_chacha_test.cpp
Normal file
245
test/tests/prg_chacha_test.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "dpf.hpp"
|
||||||
|
#include "simde/simde/x86/avx2.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bool blocks_equal(simde__m128i a, simde__m128i b)
|
||||||
|
{
|
||||||
|
return simde_mm_movemask_epi8(simde_mm_cmpeq_epi8(a, b)) == 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
simde__m128i block_from_lanes(std::uint64_t lo, std::uint64_t hi)
|
||||||
|
{
|
||||||
|
simde__m128i x;
|
||||||
|
std::uint64_t lane[2] = {lo, hi};
|
||||||
|
std::memcpy(&x, lane, sizeof(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
simde__m128i block_from_bytes(const std::uint8_t * p)
|
||||||
|
{
|
||||||
|
simde__m128i x;
|
||||||
|
std::memcpy(&x, p, sizeof(x));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 8439 §2.3.2. Counter = 1, nonce = 00:00:00:09:00:00:00:4a:00:00:00:00.
|
||||||
|
constexpr std::uint8_t k_rfc_keystream[64] = {
|
||||||
|
0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
|
||||||
|
0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
|
||||||
|
0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
|
||||||
|
0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
|
||||||
|
0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
|
||||||
|
0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
|
||||||
|
0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
|
||||||
|
0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(ChachaPrg, Rfc8439Block)
|
||||||
|
{
|
||||||
|
const std::uint32_t key[8] = {
|
||||||
|
0x03020100u, 0x07060504u, 0x0b0a0908u, 0x0f0e0d0cu,
|
||||||
|
0x13121110u, 0x17161514u, 0x1b1a1918u, 0x1f1e1d1cu
|
||||||
|
};
|
||||||
|
const std::uint32_t nonce[3] = {0x09000000u, 0x4a000000u, 0x00000000u};
|
||||||
|
std::uint8_t out[64];
|
||||||
|
dpf::prg::chacha_detail::block<20>(key, 1, nonce, out);
|
||||||
|
EXPECT_EQ(0, std::memcmp(out, k_rfc_keystream, sizeof(out)));
|
||||||
|
|
||||||
|
std::uint8_t fewer[64];
|
||||||
|
dpf::prg::chacha_detail::block<8>(key, 1, nonce, fewer);
|
||||||
|
EXPECT_NE(0, std::memcmp(fewer, k_rfc_keystream, sizeof(fewer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, WideBlockMatchesScalar)
|
||||||
|
{
|
||||||
|
std::uint32_t keys[4][8];
|
||||||
|
std::uint32_t counters[4] = {0u, 1u, 5u, 0x00fffff0u};
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
for (int w = 0; w < 8; ++w)
|
||||||
|
{
|
||||||
|
keys[lane][w] = 0x9e3779b9u * static_cast<std::uint32_t>(lane + 1)
|
||||||
|
+ static_cast<std::uint32_t>(w) * 0x01000193u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::uint8_t wide[4][64];
|
||||||
|
dpf::prg::chacha_detail::block4<20>(keys, counters, wide);
|
||||||
|
for (int lane = 0; lane < 4; ++lane)
|
||||||
|
{
|
||||||
|
std::uint8_t scalar[64];
|
||||||
|
dpf::prg::chacha_detail::block<20>(keys[lane], counters[lane],
|
||||||
|
dpf::prg::chacha_detail::zero_nonce, scalar);
|
||||||
|
EXPECT_EQ(0, std::memcmp(wide[lane], scalar, 64)) << "lane=" << lane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, EvalIsKeystreamChunk)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::chacha20;
|
||||||
|
simde__m128i seed = block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull);
|
||||||
|
std::uint32_t key[8];
|
||||||
|
dpf::prg::chacha_detail::seed_key(seed, key);
|
||||||
|
|
||||||
|
for (psnip_uint32_t pos = 0; pos < 8; ++pos)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[64];
|
||||||
|
dpf::prg::chacha_detail::block<20>(key, pos >> 2,
|
||||||
|
dpf::prg::chacha_detail::zero_nonce, buf);
|
||||||
|
EXPECT_TRUE(blocks_equal(prg::eval(seed, pos),
|
||||||
|
block_from_bytes(buf + 16 * (pos & 3u)))) << "pos=" << pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto kids = prg::eval01(seed);
|
||||||
|
EXPECT_TRUE(blocks_equal(kids[0], prg::eval(seed, 0)));
|
||||||
|
EXPECT_TRUE(blocks_equal(kids[1], prg::eval(seed, 1)));
|
||||||
|
EXPECT_FALSE(blocks_equal(kids[0], kids[1]));
|
||||||
|
EXPECT_FALSE(blocks_equal(kids[0], seed));
|
||||||
|
EXPECT_FALSE(blocks_equal(dpf::prg::chacha12::eval(seed, 0), kids[0]));
|
||||||
|
EXPECT_FALSE(blocks_equal(dpf::prg::chacha8::eval(seed, 0), kids[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, BulkAndWideAgree)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::chacha20;
|
||||||
|
simde__m128i seed = block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull);
|
||||||
|
|
||||||
|
auto check_bulk = [&](psnip_uint32_t pos, psnip_uint32_t count)
|
||||||
|
{
|
||||||
|
alignas(16) simde__m128i bulk[32];
|
||||||
|
prg::eval(seed, bulk, count, pos);
|
||||||
|
for (psnip_uint32_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(blocks_equal(bulk[i], prg::eval(seed, pos + i)))
|
||||||
|
<< "pos=" << pos << " i=" << i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
check_bulk(0, 1);
|
||||||
|
check_bulk(0, 2);
|
||||||
|
check_bulk(0, 4);
|
||||||
|
check_bulk(0, 16);
|
||||||
|
check_bulk(1, 20);
|
||||||
|
check_bulk(3, 6);
|
||||||
|
check_bulk(4, 7);
|
||||||
|
check_bulk(0xfffffffeu, 2);
|
||||||
|
|
||||||
|
alignas(16) simde__m128i seeds[8];
|
||||||
|
alignas(16) simde__m128i out4[4];
|
||||||
|
alignas(16) simde__m128i out8[8];
|
||||||
|
alignas(16) simde__m128i left[4];
|
||||||
|
alignas(16) simde__m128i right[4];
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
seeds[i] = block_from_lanes(0x1000u + static_cast<unsigned>(i), 0x2000u);
|
||||||
|
}
|
||||||
|
prg::eval_x4(seeds, out4, 5);
|
||||||
|
prg::eval_x8(seeds, out8, 0);
|
||||||
|
prg::eval01_x4(seeds, left, right);
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(blocks_equal(out4[i], prg::eval(seeds[i], 5)));
|
||||||
|
EXPECT_TRUE(blocks_equal(left[i], prg::eval(seeds[i], 0)));
|
||||||
|
EXPECT_TRUE(blocks_equal(right[i], prg::eval(seeds[i], 1)));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(blocks_equal(out8[i], prg::eval(seeds[i], 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
alignas(16) simde__m128i one[1];
|
||||||
|
EXPECT_NO_THROW(prg::eval(seed, one, 1, 0xffffffffu));
|
||||||
|
EXPECT_TRUE(blocks_equal(one[0], prg::eval(seed, 0xffffffffu)));
|
||||||
|
EXPECT_THROW(prg::eval(seed, out4, 2, 0xffffffffu), std::invalid_argument);
|
||||||
|
prg::eval(seed, out4, 0, 0xffffffffu);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, DpfPointAndFull)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::chacha20;
|
||||||
|
const std::uint8_t x = 0x2a;
|
||||||
|
const std::uint32_t y = 0x01020304;
|
||||||
|
auto [k0, k1] = dpf::make_dpf<prg>(x, y);
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
auto y0 = *dpf::eval_point(k0, static_cast<std::uint8_t>(i));
|
||||||
|
auto y1 = *dpf::eval_point(k1, static_cast<std::uint8_t>(i));
|
||||||
|
auto sum = dpf::reconstruct(y0, y1);
|
||||||
|
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [buf0, iter0] = dpf::eval_full(k0);
|
||||||
|
auto [buf1, iter1] = dpf::eval_full(k1);
|
||||||
|
(void)buf0;
|
||||||
|
(void)buf1;
|
||||||
|
std::size_t i = 0;
|
||||||
|
auto it0 = std::begin(iter0);
|
||||||
|
auto it1 = std::begin(iter1);
|
||||||
|
for (; it0 != std::end(iter0); ++it0, ++it1, ++i)
|
||||||
|
{
|
||||||
|
auto sum = dpf::reconstruct(*it0, *it1);
|
||||||
|
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(i, std::size_t{256});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, ReducedRoundsStillCorrect)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::chacha8;
|
||||||
|
const std::uint8_t x = 0x11;
|
||||||
|
const std::uint32_t y = 0xabcdu;
|
||||||
|
auto [k0, k1] = dpf::make_dpf<prg, dpf::prg::chacha12>(x, y);
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
auto sum = dpf::reconstruct(
|
||||||
|
*dpf::eval_point(k0, static_cast<std::uint8_t>(i)),
|
||||||
|
*dpf::eval_point(k1, static_cast<std::uint8_t>(i)));
|
||||||
|
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, CounterWrapperCountsEval01)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::counter_wrapper<dpf::prg::chacha20>;
|
||||||
|
const auto before = prg::count();
|
||||||
|
simde__m128i seed = block_from_lanes(0x1111ull, 0x2222ull);
|
||||||
|
auto kids = prg::eval01(seed);
|
||||||
|
EXPECT_FALSE(blocks_equal(kids[0], kids[1]));
|
||||||
|
EXPECT_EQ(prg::count(), before + 2u);
|
||||||
|
|
||||||
|
alignas(16) simde__m128i bulk[4];
|
||||||
|
prg::eval(seed, bulk, 4, 0);
|
||||||
|
EXPECT_EQ(prg::count(), before + 2u + 4u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChachaPrg, ExpandAndBufferedReplay)
|
||||||
|
{
|
||||||
|
using prg = dpf::prg::chacha20;
|
||||||
|
simde__m128i seed = block_from_lanes(0x1111ull, 0x2222ull);
|
||||||
|
auto t0 = prg::expand<std::uint32_t, 0>(seed, 3);
|
||||||
|
auto t1 = prg::expand<std::uint32_t, 1>(seed, 3);
|
||||||
|
EXPECT_EQ(dpf::reconstruct(t0, t1), 0u);
|
||||||
|
|
||||||
|
auto u0 = prg::expand<std::uint64_t, 0>(seed, 0);
|
||||||
|
auto u1 = prg::expand<std::uint64_t, 1>(seed, 1);
|
||||||
|
EXPECT_NE(u0.raw(), u1.raw());
|
||||||
|
|
||||||
|
dpf::randomness::buffered_prg<prg, std::uint64_t, std::uint64_t> streamed(seed, 8);
|
||||||
|
auto s0 = streamed.get<0>();
|
||||||
|
auto s1 = streamed.get<1>();
|
||||||
|
dpf::randomness::buffered_prg<prg, std::uint64_t, std::uint64_t> replay(seed, 8);
|
||||||
|
EXPECT_EQ(replay.at<0>(0), s0);
|
||||||
|
EXPECT_EQ(replay.at<1>(0), s1);
|
||||||
|
EXPECT_EQ(replay.get<0>(), s0);
|
||||||
|
EXPECT_NE(s0, streamed.get<0>());
|
||||||
|
}
|
||||||
246
test/tests/range_lut_test.cpp
Normal file
246
test/tests/range_lut_test.cpp
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "grotto/range_lut.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
long double truth_of(grotto::reduced which, long double x)
|
||||||
|
{
|
||||||
|
switch (which)
|
||||||
|
{
|
||||||
|
case grotto::reduced::ln: return std::log(x);
|
||||||
|
case grotto::reduced::lg: return std::log2(x);
|
||||||
|
case grotto::reduced::log10: return std::log10(x);
|
||||||
|
case grotto::reduced::exp: return std::exp(x);
|
||||||
|
case grotto::reduced::exp2: return std::exp2(x);
|
||||||
|
case grotto::reduced::exp10: return std::exp(x * std::log(10.0L));
|
||||||
|
case grotto::reduced::sin: return std::sin(x);
|
||||||
|
case grotto::reduced::cos: return std::cos(x);
|
||||||
|
case grotto::reduced::tan: return std::tan(x);
|
||||||
|
case grotto::reduced::cot: return 1.0L / std::tan(x);
|
||||||
|
case grotto::reduced::sec: return 1.0L / std::cos(x);
|
||||||
|
case grotto::reduced::csc: return 1.0L / std::sin(x);
|
||||||
|
case grotto::reduced::sinh: return std::sinh(x);
|
||||||
|
case grotto::reduced::cosh: return std::cosh(x);
|
||||||
|
case grotto::reduced::tanh: return std::tanh(x);
|
||||||
|
case grotto::reduced::coth: return 1.0L / std::tanh(x);
|
||||||
|
case grotto::reduced::sech: return 1.0L / std::cosh(x);
|
||||||
|
case grotto::reduced::csch: return 1.0L / std::sinh(x);
|
||||||
|
case grotto::reduced::sqrt: return std::sqrt(x);
|
||||||
|
case grotto::reduced::inv: return 1.0L / x;
|
||||||
|
case grotto::reduced::rsqrt: return 1.0L / std::sqrt(x);
|
||||||
|
case grotto::reduced::invsq: return 1.0L / (x * x);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * name_of(grotto::reduced which)
|
||||||
|
{
|
||||||
|
switch (which)
|
||||||
|
{
|
||||||
|
case grotto::reduced::ln: return "ln";
|
||||||
|
case grotto::reduced::lg: return "lg";
|
||||||
|
case grotto::reduced::log10: return "log10";
|
||||||
|
case grotto::reduced::exp: return "exp";
|
||||||
|
case grotto::reduced::exp2: return "exp2";
|
||||||
|
case grotto::reduced::exp10: return "exp10";
|
||||||
|
case grotto::reduced::sin: return "sin";
|
||||||
|
case grotto::reduced::cos: return "cos";
|
||||||
|
case grotto::reduced::tan: return "tan";
|
||||||
|
case grotto::reduced::cot: return "cot";
|
||||||
|
case grotto::reduced::sec: return "sec";
|
||||||
|
case grotto::reduced::csc: return "csc";
|
||||||
|
case grotto::reduced::sinh: return "sinh";
|
||||||
|
case grotto::reduced::cosh: return "cosh";
|
||||||
|
case grotto::reduced::tanh: return "tanh";
|
||||||
|
case grotto::reduced::coth: return "coth";
|
||||||
|
case grotto::reduced::sech: return "sech";
|
||||||
|
case grotto::reduced::csch: return "csch";
|
||||||
|
case grotto::reduced::sqrt: return "sqrt";
|
||||||
|
case grotto::reduced::inv: return "inv";
|
||||||
|
case grotto::reduced::rsqrt: return "rsqrt";
|
||||||
|
case grotto::reduced::invsq: return "invsq";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int64_t raw_of(long double x, unsigned k)
|
||||||
|
{
|
||||||
|
const long double scaled = std::ldexp(x, static_cast<int>(k));
|
||||||
|
return std::llround(scaled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void expect_ulps(grotto::reduced which, unsigned k, long double x, long double ulps)
|
||||||
|
{
|
||||||
|
const std::int64_t raw = raw_of(x, k);
|
||||||
|
std::int64_t got = 0;
|
||||||
|
ASSERT_NO_THROW(got = grotto::eval_reduced(which, k, raw))
|
||||||
|
<< name_of(which) << " k=" << k << " x=" << static_cast<double>(x);
|
||||||
|
const long double truth = truth_of(which, std::ldexp(static_cast<long double>(raw), -static_cast<int>(k)));
|
||||||
|
const long double want = truth * std::ldexp(1.0L, static_cast<int>(k));
|
||||||
|
EXPECT_LE(std::fabsl(static_cast<long double>(got) - want), ulps)
|
||||||
|
<< name_of(which) << " k=" << k << " x=" << static_cast<double>(x)
|
||||||
|
<< " got=" << got << " want=" << static_cast<double>(want);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool near_odd_multiple_of_half_pi(long double x)
|
||||||
|
{
|
||||||
|
const long double turn = std::fmod(std::fabsl(x), 3.14159265358979323846L);
|
||||||
|
const long double dist = std::fmod(turn + 1.5707963267948966L, 3.14159265358979323846L);
|
||||||
|
const long double folded = dist > 1.5707963267948966L ? 3.14159265358979323846L - dist : dist;
|
||||||
|
return folded < 0.15L;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(RangeLut, LogarithmsTrackLibmOnEveryPrecision)
|
||||||
|
{
|
||||||
|
const grotto::reduced maps[] = {
|
||||||
|
grotto::reduced::ln, grotto::reduced::lg, grotto::reduced::log10,
|
||||||
|
};
|
||||||
|
const long double samples[] = {
|
||||||
|
0.125L, 0.3L, 0.5L, 0.75L, 1.0L, 1.5L, 2.0L, 3.0L, 7.5L, 16.0L, 24.0L, 100.0L,
|
||||||
|
};
|
||||||
|
for (auto which : maps)
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
expect_ulps(which, k, x, 6.0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, ExponentialsTrackLibmOnEveryPrecision)
|
||||||
|
{
|
||||||
|
const long double samples[] = {
|
||||||
|
-2.0L, -1.5L, -0.5L, -0.1L, 0.0L, 0.1L, 0.5L, 1.0L, 1.5L, 2.0L,
|
||||||
|
};
|
||||||
|
for (auto which : {grotto::reduced::exp, grotto::reduced::exp2})
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
expect_ulps(which, k, x, 16.0L);
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
{
|
||||||
|
for (long double x : {-0.9L, -0.25L, 0.0L, 0.25L, 0.9L})
|
||||||
|
expect_ulps(grotto::reduced::exp10, k, x, 24.0L);
|
||||||
|
// 10^q scales the absolute error of exp(f ln 10) by the integer power.
|
||||||
|
for (long double x : {-1.5L, 1.5L})
|
||||||
|
expect_ulps(grotto::reduced::exp10, k, x, 80.0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, QuarterTurnTrigTracksLibm)
|
||||||
|
{
|
||||||
|
const long double samples[] = {
|
||||||
|
-3.5L, -2.2L, -1.2L, -0.7L, -0.3L, 0.0L, 0.2L, 0.4L, 0.7L, 1.0L, 1.2L, 2.5L, 3.5L,
|
||||||
|
};
|
||||||
|
for (auto which : {grotto::reduced::sin, grotto::reduced::cos})
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
expect_ulps(which, k, x, 3.0L);
|
||||||
|
for (auto which : {grotto::reduced::tan, grotto::reduced::sec})
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
{
|
||||||
|
if (near_odd_multiple_of_half_pi(x))
|
||||||
|
continue;
|
||||||
|
expect_ulps(which, k, x, 8.0L);
|
||||||
|
}
|
||||||
|
for (auto which : {grotto::reduced::cot, grotto::reduced::csc})
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
{
|
||||||
|
if (x == 0.0L || near_odd_multiple_of_half_pi(x))
|
||||||
|
continue;
|
||||||
|
expect_ulps(which, k, x, 8.0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, HyperbolicReductionsTrackLibm)
|
||||||
|
{
|
||||||
|
const long double samples[] = {
|
||||||
|
-2.0L, -1.2L, -0.4L, -0.05L, 0.05L, 0.4L, 0.7L, 1.2L, 2.0L,
|
||||||
|
};
|
||||||
|
for (auto which : {grotto::reduced::sinh, grotto::reduced::cosh})
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
expect_ulps(which, k, x, 12.0L);
|
||||||
|
for (auto which : {
|
||||||
|
grotto::reduced::tanh, grotto::reduced::sech, grotto::reduced::coth, grotto::reduced::csch,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
{
|
||||||
|
if ((which == grotto::reduced::coth || which == grotto::reduced::csch) && std::fabsl(x) < 0.2L)
|
||||||
|
continue;
|
||||||
|
expect_ulps(which, k, x, 6.0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, DyadicLiftsCoverOddAndEvenExponents)
|
||||||
|
{
|
||||||
|
const grotto::reduced maps[] = {
|
||||||
|
grotto::reduced::sqrt, grotto::reduced::inv, grotto::reduced::rsqrt, grotto::reduced::invsq,
|
||||||
|
};
|
||||||
|
const long double samples[] = {
|
||||||
|
0.125L, 0.3L, 0.5L, 0.75L, 1.0L, 1.5L, 2.0L, 3.0L, 6.0L, 7.5L, 16.0L, 24.0L, 48.0L, 100.0L,
|
||||||
|
};
|
||||||
|
for (auto which : maps)
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
for (long double x : samples)
|
||||||
|
{
|
||||||
|
const long double budget = which == grotto::reduced::sqrt ? 12.0L : 4.0L;
|
||||||
|
expect_ulps(which, k, x, budget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, TinyHyperbolicArgumentsUseThePrincipalTables)
|
||||||
|
{
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
{
|
||||||
|
if (k < 13)
|
||||||
|
continue;
|
||||||
|
const long double tiny = std::ldexp(1.0L, -16);
|
||||||
|
expect_ulps(grotto::reduced::sinh, k, tiny, 2.0L);
|
||||||
|
expect_ulps(grotto::reduced::cosh, k, tiny, 2.0L);
|
||||||
|
expect_ulps(grotto::reduced::sinh, k, -tiny, 2.0L);
|
||||||
|
expect_ulps(grotto::reduced::cosh, k, -tiny, 2.0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, TanhAndCothSaturatePastBeta)
|
||||||
|
{
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
{
|
||||||
|
const std::int64_t one = std::int64_t{1} << k;
|
||||||
|
const std::int64_t raw = raw_of(20.0L, k);
|
||||||
|
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::tanh, k, raw), one);
|
||||||
|
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::tanh, k, -raw), -one);
|
||||||
|
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::coth, k, raw), one);
|
||||||
|
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::coth, k, -raw), -one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, SquareRootOfZeroIsZero)
|
||||||
|
{
|
||||||
|
for (unsigned k : grotto::principal_precisions)
|
||||||
|
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::sqrt, k, 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeLut, RejectsPolesAndNonPositiveLogarithms)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::ln, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::ln, 16, -4), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::lg, 16, -1), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::sqrt, 16, -4), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::inv, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::rsqrt, 16, -8), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::cot, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::csc, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::coth, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::csch, 16, 0), std::domain_error);
|
||||||
|
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::ln, 7, 32), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
@ -141,3 +142,54 @@ TEST(SignedPrefix, RejectsAKeyWithoutAComparison)
|
||||||
EXPECT_THROW(grotto::signed_prefix_parities(k0, ends), std::invalid_argument);
|
EXPECT_THROW(grotto::signed_prefix_parities(k0, ends), std::invalid_argument);
|
||||||
EXPECT_THROW(grotto::signed_segment_parities(k1, ends), std::invalid_argument);
|
EXPECT_THROW(grotto::signed_segment_parities(k1, ends), std::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
void expect_ilogb_segments(const grotto::constant_lut<int8_t> & lut, int8_t alpha)
|
||||||
|
{
|
||||||
|
if (lut.bounds.size() != N)
|
||||||
|
return;
|
||||||
|
std::array<int8_t, N> ends{};
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
ends[i] = lut.bounds[i];
|
||||||
|
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(uint64_t{1}));
|
||||||
|
const uint64_t mask = k0.cmp().mask;
|
||||||
|
const auto s0 = grotto::signed_segment_parities(k0, ends);
|
||||||
|
const auto s1 = grotto::signed_segment_parities(k1, ends);
|
||||||
|
uint64_t acc = 0;
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
{
|
||||||
|
const uint64_t bit = opened(s0[i], s1[i], mask);
|
||||||
|
acc += bit * static_cast<uint64_t>(lut.values[i]);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(acc, static_cast<uint64_t>(lut(alpha))) << int(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
void dispatch_ilogb(const grotto::constant_lut<int8_t> & lut, int8_t alpha, bool & matched)
|
||||||
|
{
|
||||||
|
if (lut.bounds.size() == N)
|
||||||
|
{
|
||||||
|
expect_ilogb_segments<N>(lut, alpha);
|
||||||
|
matched = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if constexpr (N > 1)
|
||||||
|
dispatch_ilogb<N - 1>(lut, alpha, matched);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SignedPrefix, SegmentsRecoverIlogbInt8)
|
||||||
|
{
|
||||||
|
for (unsigned frac : {0u, 4u})
|
||||||
|
{
|
||||||
|
const auto lut = grotto::make_exact_constant_lut<int8_t>(
|
||||||
|
grotto::exact_constant::ilogb, frac);
|
||||||
|
ASSERT_GE(lut.bounds.size(), 3u);
|
||||||
|
ASSERT_LE(lut.bounds.size(), 40u);
|
||||||
|
for (int v = -128; v <= 127; ++v)
|
||||||
|
{
|
||||||
|
bool matched = false;
|
||||||
|
dispatch_ilogb<40>(lut, static_cast<int8_t>(v), matched);
|
||||||
|
ASSERT_TRUE(matched) << lut.bounds.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,8 @@ bool same_cmp_channel(const Key & a, const Key & b)
|
||||||
const auto & cb = b.cmp();
|
const auto & cb = b.cmp();
|
||||||
if (ca.nbits != cb.nbits || ca.mask != cb.mask || ca.kind != cb.kind
|
if (ca.nbits != cb.nbits || ca.mask != cb.mask || ca.kind != cb.kind
|
||||||
|| ca.trivial != cb.trivial || ca.eval_as_ge != cb.eval_as_ge
|
|| ca.trivial != cb.trivial || ca.eval_as_ge != cb.eval_as_ge
|
||||||
|| ca.include_eq != cb.include_eq || ca.active != cb.active)
|
|| ca.include_eq != cb.include_eq || ca.active != cb.active
|
||||||
|
|| ca.incremental != cb.incremental)
|
||||||
return false;
|
return false;
|
||||||
using word = typename Key::value_cw_word;
|
using word = typename Key::value_cw_word;
|
||||||
if (!same_bytes(a.value_cw().data(), b.value_cw().data(),
|
if (!same_bytes(a.value_cw().data(), b.value_cw().data(),
|
||||||
|
|
@ -874,6 +875,32 @@ TEST_F(StressScenariosTest, PrgDummyAesClassicPoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(StressScenariosTest, PrgChachaInteriorAesExteriorClassicPoint)
|
||||||
|
{
|
||||||
|
uint8_t x = 0x2a;
|
||||||
|
auto [k0, k1] = dpf::make_dpf<dpf::prg::chacha20, dpf::prg::aes128>(x, uint32_t{0x01020304});
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
auto s = recon(*dpf::eval_point(k0, static_cast<uint8_t>(i)),
|
||||||
|
*dpf::eval_point(k1, static_cast<uint8_t>(i)));
|
||||||
|
EXPECT_EQ(static_cast<uint32_t>(s),
|
||||||
|
static_cast<uint8_t>(i) == x ? 0x01020304u : 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StressScenariosTest, PrgAesInteriorChachaExteriorClassicPoint)
|
||||||
|
{
|
||||||
|
uint8_t x = 0x91;
|
||||||
|
auto [k0, k1] = dpf::make_dpf<dpf::prg::aes128, dpf::prg::chacha20>(x, uint32_t{0xdeadbeef});
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
auto s = recon(*dpf::eval_point(k0, static_cast<uint8_t>(i)),
|
||||||
|
*dpf::eval_point(k1, static_cast<uint8_t>(i)));
|
||||||
|
EXPECT_EQ(static_cast<uint32_t>(s),
|
||||||
|
static_cast<uint8_t>(i) == x ? 0xdeadbeefu : 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(StressScenariosTest, PrgLowmcLowmcMultilevelPacking)
|
TEST_F(StressScenariosTest, PrgLowmcLowmcMultilevelPacking)
|
||||||
{
|
{
|
||||||
uint16_t x = 0x4c1d;
|
uint16_t x = 0x4c1d;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue