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
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 <limits>
|
||||
|
||||
#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
|
||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>; // This is just to count the number of PRG invocations
|
||||
auto before = prg::count(); // In order to show how much this program cost
|
||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
||||
auto after = prg::count();
|
||||
std::cout << "dpf::make_dpf used " << (after-before) << "\n";
|
||||
const std::uint8_t alpha = 42;
|
||||
const std::uint64_t beta = 7;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, beta);
|
||||
|
||||
before = prg::count();
|
||||
auto [buf0, iter0] = dpf::eval_full(dpf0);
|
||||
after = prg::count();
|
||||
std::cout << "dpf::eval_full(dpf0) used " << (after-before) << "\n";
|
||||
before = prg::count();
|
||||
auto [buf1, iter1] = dpf::eval_full(dpf1);
|
||||
after = prg::count();
|
||||
std::cout << "dpf::eval_full(dpf1) used " << (after-before) << "\n";
|
||||
// Retrieve the original input by iterating over the two buffers
|
||||
for (size_t i = 0; i < buf0.size(); ++i) {
|
||||
bool item1 = buf0[i];
|
||||
bool item2 = buf1[i];
|
||||
if (item1 ^ item2) std::cout << "The original input is: " << i << std::endl;
|
||||
//! [eval-full]
|
||||
auto [buf0, iter0] = dpf::eval_full(k0);
|
||||
auto [buf1, iter1] = dpf::eval_full(k1);
|
||||
|
||||
auto it0 = std::begin(iter0);
|
||||
auto it1 = std::begin(iter1);
|
||||
for (int x = std::numeric_limits<std::uint8_t>::min();
|
||||
x <= std::numeric_limits<std::uint8_t>::max();
|
||||
++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 << "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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,43 @@
|
|||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#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;
|
||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
||||
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);
|
||||
|
||||
// Make the DPF
|
||||
auto before = prg::count();
|
||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
||||
auto after = prg::count();
|
||||
std::cout << "dpf::make_dpf prg invocation: " << (after-before) << "\n";
|
||||
//! [eval-interval]
|
||||
auto [buf0, iter0] = dpf::eval_interval(k0, from, to);
|
||||
auto [buf1, iter1] = dpf::eval_interval(k1, from, to);
|
||||
|
||||
// Evaluate the DPF by interval
|
||||
before = prg::count();
|
||||
int from = 0, to = 49;
|
||||
auto [buf0, iter0] = dpf::eval_interval(dpf0, from, to);
|
||||
auto [buf1, iter1] = dpf::eval_interval(dpf1, from, to);
|
||||
after = prg::count();
|
||||
std::cout << "dpf::eval_interval prg invocation: " << (after-before) << "\n";
|
||||
|
||||
// Retrieve the original input by iterating over the two buffers
|
||||
std::vector<bool> result;
|
||||
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;
|
||||
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 << "eval_interval\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Print out the XOR interval
|
||||
for (const auto& item : result) {
|
||||
std::cout << static_cast<bool>(item);
|
||||
//! [eval-interval]
|
||||
if (it0 != std::end(iter0) || it1 != std::end(iter1))
|
||||
{
|
||||
std::cerr << "eval_interval length\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
if (y == x) std::cout << "The orginal value is: " << x << std::endl;
|
||||
else std::cout << "The evaluated inputs did not match the original value." << std::endl;
|
||||
|
||||
std::cout << "Total PRG invocation: " << prg::count() << std::endl;
|
||||
|
||||
std::cout << dpf::reconstruct(*std::begin(iter0), *std::begin(iter1)) << "\n";
|
||||
(void)buf0;
|
||||
(void)buf1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,54 @@
|
|||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#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;
|
||||
auto [dpf0, dpf1] = dpf::make_dpf(x);
|
||||
const std::uint8_t alpha = 42;
|
||||
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
|
||||
std::cout << *dpf::eval_point(dpf0, x) << " ^ " << *dpf::eval_point(dpf1, x) << " = " << (*dpf::eval_point(dpf0, x) ^ *dpf::eval_point(dpf1, x)) << "\n"; // = 1
|
||||
std::cout << *dpf::eval_point(dpf0, 43) << " ^ " << *dpf::eval_point(dpf1, 43) << " = " << (*dpf::eval_point(dpf0, 43) ^ *dpf::eval_point(dpf1, 43)) << "\n"; // = 0
|
||||
auto off0 = *dpf::eval_point(k0, std::uint8_t{41});
|
||||
auto off1 = *dpf::eval_point(k1, std::uint8_t{41});
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,114 @@
|
|||
#include <chrono>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#include "dpf.hpp"
|
||||
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
/// A sorted point list. `return_output_only_tag_` stores one share per point.
|
||||
/// A `sequence_recipe` compiled from that list is reusable across keys.
|
||||
int main()
|
||||
{
|
||||
using input_type = uint8_t;
|
||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
||||
const std::uint8_t alpha = 42;
|
||||
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;
|
||||
std::array<input_type, N> keys{};
|
||||
for(int i=0; i<N; i++) keys[i] = i; // Create an array of keys
|
||||
//! [eval-sequence]
|
||||
// Nondecreasing. An unsorted range throws std::runtime_error.
|
||||
std::array<std::uint8_t, 5> points{1, 7, 42, 100, 200};
|
||||
|
||||
// eval_sequence with recipe
|
||||
input_type x = 42;
|
||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x); // First DPF to be able to create the recipe
|
||||
auto t1 = high_resolution_clock::now(); // To measure the time of execution
|
||||
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 recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(keys), std::end(keys)); // Create a recipe
|
||||
for (int i=0; i<N; i++)
|
||||
auto [buf0, iter0] = dpf::eval_sequence(k0, points.begin(), points.end(),
|
||||
dpf::return_output_only_tag_{});
|
||||
auto [buf1, iter1] = dpf::eval_sequence(k1, points.begin(), points.end(),
|
||||
dpf::return_output_only_tag_{});
|
||||
|
||||
auto it0 = std::begin(iter0);
|
||||
auto it1 = std::begin(iter1);
|
||||
for (std::uint8_t x : points)
|
||||
{
|
||||
auto [dpf00, dpf11] = dpf::make_dpf<prg>(i); // Make 50 DPFs
|
||||
dpf::eval_sequence(dpf0, recipe0); // Evaluate the DPFs with the recipe
|
||||
dpf::eval_sequence(dpf1, recipe0); // Evaluate the DPFs with the recipe
|
||||
std::uint64_t got = dpf::reconstruct(*it0, *it1);
|
||||
std::uint64_t expect = (x == alpha) ? beta : 0;
|
||||
if (got != expect)
|
||||
{
|
||||
std::cerr << "eval_sequence\n";
|
||||
return 1;
|
||||
}
|
||||
++it0;
|
||||
++it1;
|
||||
}
|
||||
auto after = prg::count(); // Count the number of PRG invocations
|
||||
std::cout << "dpf::eval_sequence with recipe " << (after-before) << "\n";
|
||||
//! [eval-sequence]
|
||||
|
||||
// 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++)
|
||||
//! [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)
|
||||
{
|
||||
auto [dpf00, dpf11] = dpf::make_dpf<prg>(i);
|
||||
dpf::eval_sequence(dpf00, std::begin(keys), std::end(keys));
|
||||
dpf::eval_sequence(dpf11, std::begin(keys), std::end(keys));
|
||||
if (dpf::reconstruct(*it0, *it1) != ((x == alpha) ? beta : 0))
|
||||
{
|
||||
std::cerr << "eval_sequence recipe, default memoizer\n";
|
||||
return 1;
|
||||
}
|
||||
++it0;
|
||||
++it1;
|
||||
}
|
||||
after = prg::count();
|
||||
std::cout << "dpf::eval_sequence used " << (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";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,128 @@
|
|||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
#include "dpf.hpp"
|
||||
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
int main(int arc, char * argv[])
|
||||
/// Memoizers are workspaces of interior nodes. Pass a mutable lvalue.
|
||||
/// A temporary (including the default argument) cannot remember a prefix.
|
||||
int main()
|
||||
{
|
||||
// Making the DPF with an integer value
|
||||
uint16_t x = 42;
|
||||
using prg = dpf::prg::counter_wrapper<dpf::prg::dummy>;
|
||||
auto [dpf0, dpf1] = dpf::make_dpf<prg>(x);
|
||||
const std::uint8_t alpha = 42;
|
||||
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)>>;
|
||||
|
||||
// Evaluating the DPF and counting how much it cost without memoizers
|
||||
auto t1 = high_resolution_clock::now();
|
||||
auto before = prg::count();
|
||||
for (int i = 0; i<1024*1024; i++)
|
||||
//! [path-memoizer]
|
||||
auto path0 = dpf::make_basic_path_memoizer<key_t>();
|
||||
auto path1 = dpf::make_basic_path_memoizer<key_t>();
|
||||
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);
|
||||
std::uint64_t expect = (static_cast<std::uint8_t>(x) == alpha) ? beta : 0;
|
||||
if (got != expect)
|
||||
{
|
||||
std::cerr << "basic_path_memoizer\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Printing out the results
|
||||
auto after = prg::count();
|
||||
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";
|
||||
//! [path-memoizer]
|
||||
|
||||
// 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++)
|
||||
// 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)
|
||||
{
|
||||
dpf::eval_point(dpf0, i, path);
|
||||
std::cerr << "nonmemoizing_path_memoizer\n";
|
||||
return 1;
|
||||
}
|
||||
// Printing out the results
|
||||
after = prg::count();
|
||||
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";
|
||||
|
||||
//! [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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,82 @@
|
|||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue