2026-09-24 14:08:32 -06:00
|
|
|
|
#include <gtest/gtest.h>
|
2026-09-28 05:59:19 -06:00
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
#include <cstdint>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
#include "asio.hpp"
|
|
|
|
|
|
#define LIBDPF_HAS_ASIO
|
|
|
|
|
|
#include "dpf.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0,
|
|
|
|
|
|
typename DpfKey0,
|
|
|
|
|
|
typename DpfKey1,
|
|
|
|
|
|
typename ConcreteT>
|
|
|
|
|
|
void assign_leaf(DpfKey0 & dpf0, DpfKey1 & dpf1, const ConcreteT y_shr0, const ConcreteT y_shr1)
|
|
|
|
|
|
{
|
|
|
|
|
|
asio::io_context io_context;
|
|
|
|
|
|
std::thread server([&io_context, &dpf0, &y_shr0]()
|
|
|
|
|
|
{
|
|
|
|
|
|
asio::ip::tcp::acceptor acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 31337));
|
|
|
|
|
|
asio::ip::tcp::socket peer{io_context};
|
|
|
|
|
|
asio::ip::tcp::endpoint ep{};
|
|
|
|
|
|
acceptor.accept(peer, ep);
|
|
|
|
|
|
auto future = dpf0.template async_assign_leaf<I>(peer, y_shr0, asio::use_future);
|
|
|
|
|
|
io_context.run();
|
|
|
|
|
|
future.wait();
|
|
|
|
|
|
});
|
|
|
|
|
|
std::thread client([&io_context, &dpf1, &y_shr1]()
|
|
|
|
|
|
{
|
|
|
|
|
|
asio::ip::tcp::socket peer(io_context);
|
|
|
|
|
|
asio::ip::tcp::resolver resolver(io_context);
|
|
|
|
|
|
asio::connect(peer, resolver.resolve("localhost", "31337"));
|
|
|
|
|
|
auto future = dpf1.template async_assign_leaf<I>(peer, y_shr1, asio::use_future);
|
|
|
|
|
|
io_context.run();
|
|
|
|
|
|
future.wait();
|
|
|
|
|
|
});
|
|
|
|
|
|
client.join();
|
|
|
|
|
|
server.join();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, SingleLeafFailEvalBeforeVernalization)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using output_type = dpf::wildcard_value<uint32_t>;
|
|
|
|
|
|
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type>;
|
|
|
|
|
|
input_type x = 0xAA;
|
|
|
|
|
|
output_type y;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_point(dpf0, x), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_point(dpf1, x), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
input_type from = 0x33, to = 0xCC;
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_interval(dpf0, from, to), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_interval(dpf1, from, to), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_full(dpf0), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_full(dpf1), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
|
|
|
|
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence(dpf0, std::begin(points), std::end(points)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence(dpf1, std::begin(points), std::end(points)), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points)), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence(dpf0, recipe0), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW(dpf::eval_sequence(dpf1, recipe1), std::runtime_error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, MultiLeafFailEvalBeforeVernalization)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using output_type0 = uint32_t;
|
|
|
|
|
|
using output_type1 = dpf::wildcard_value<uint32_t>;
|
|
|
|
|
|
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type0, output_type1>;
|
|
|
|
|
|
input_type x = 0xAA;
|
|
|
|
|
|
output_type0 y0 = 0xAAAAAAAA;
|
|
|
|
|
|
output_type1 y1;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1);
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_point<0>(dpf0, x)));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_point<0>(dpf1, x)));
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_point<1>(dpf0, x)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_point<1>(dpf1, x)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_point<0, 1>(dpf0, x)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_point<0, 1>(dpf1, x)), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
input_type from = 0x33, to = 0xCC;
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_interval<0>(dpf0, from, to)));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_interval<0>(dpf1, from, to)));
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_interval<1>(dpf0, from, to)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_interval<1>(dpf1, from, to)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_interval<0, 1>(dpf0, from, to)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_interval<0, 1>(dpf1, from, to)), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_full<0>(dpf0)));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_full<0>(dpf1)));
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_full<1>(dpf0)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_full<1>(dpf1)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_full<0, 1>(dpf0)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_full<0, 1>(dpf1)), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
|
|
|
|
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points))));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points))));
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points))), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points))), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points))), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points))), std::runtime_error);
|
|
|
|
|
|
|
|
|
|
|
|
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, recipe0)));
|
|
|
|
|
|
ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, recipe1)));
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<1>(dpf0, recipe0)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<1>(dpf1, recipe1)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, recipe0)), std::runtime_error);
|
|
|
|
|
|
ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, recipe1)), std::runtime_error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, SingleLeafSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using concrete_type = uint32_t;
|
|
|
|
|
|
using output_type = dpf::wildcard_value<concrete_type>;
|
|
|
|
|
|
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type>;
|
|
|
|
|
|
input_type x = 0xAA, from = 0x33, to = 0xCC;
|
|
|
|
|
|
output_type y;
|
|
|
|
|
|
concrete_type y_exp = 0xAAAAAAAA,
|
|
|
|
|
|
y_shr0 = 0x12345678,
|
|
|
|
|
|
y_shr1 = y_exp - y_shr0,
|
|
|
|
|
|
zero_output = 0;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
|
|
|
|
|
|
|
|
|
|
|
|
assign_leaf(dpf0, dpf1, y_shr0, y_shr1);
|
|
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto y0 = *dpf::eval_point(dpf0, i);
|
|
|
|
|
|
auto y1 = *dpf::eval_point(dpf1, i);
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(y0, y1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(y0, y1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufint0, iterint0] = dpf::eval_interval(dpf0, from, to);
|
|
|
|
|
|
auto [bufint1, iterint1] = dpf::eval_interval(dpf1, from, to);
|
|
|
|
|
|
auto itint0 = std::begin(iterint0);
|
|
|
|
|
|
auto itint1 = std::begin(iterint1);
|
|
|
|
|
|
for (std::size_t i = from; i <= to; ++i, ++itint0, ++itint1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itint0, std::end(iterint0));
|
|
|
|
|
|
ASSERT_EQ(itint1, std::end(iterint1));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufful0, iterful0] = dpf::eval_full(dpf0);
|
|
|
|
|
|
auto [bufful1, iterful1] = dpf::eval_full(dpf1);
|
|
|
|
|
|
auto itful0 = std::begin(iterful0);
|
|
|
|
|
|
auto itful1 = std::begin(iterful1);
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful0, ++itful1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itful0, std::end(iterful0));
|
|
|
|
|
|
ASSERT_EQ(itful1, std::end(iterful1));
|
|
|
|
|
|
|
|
|
|
|
|
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
|
|
|
|
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
|
|
|
|
|
auto [bufseq0, iterseq0] = dpf::eval_sequence(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufseq1, iterseq1] = dpf::eval_sequence(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itseq0 = std::begin(iterseq0);
|
|
|
|
|
|
auto itseq1 = std::begin(iterseq1);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itseq0, ++itseq1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itseq0, std::end(iterseq0));
|
|
|
|
|
|
ASSERT_EQ(itseq1, std::end(iterseq1));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufbre0, iterbre0] = dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufbre1, iterbre1] = dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itbre0 = std::begin(iterbre0);
|
|
|
|
|
|
auto itbre1 = std::begin(iterbre1);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itbre0, ++itbre1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itbre0, std::end(iterbre0));
|
|
|
|
|
|
ASSERT_EQ(itbre1, std::end(iterbre1));
|
|
|
|
|
|
|
|
|
|
|
|
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufrec0, iterrec0] = dpf::eval_sequence(dpf0, recipe0);
|
|
|
|
|
|
auto [bufrec1, iterrec1] = dpf::eval_sequence(dpf1, recipe1);
|
|
|
|
|
|
auto itrec0 = std::begin(iterrec0);
|
|
|
|
|
|
auto itrec1 = std::begin(iterrec1);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itrec0, ++itrec1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), zero_output);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itrec0, std::end(iterrec0));
|
|
|
|
|
|
ASSERT_EQ(itrec1, std::end(iterrec1));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, MultiLeafSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using output_type0 = uint32_t;
|
|
|
|
|
|
using concrete_type = uint32_t;
|
|
|
|
|
|
using output_type1 = dpf::wildcard_value<concrete_type>;
|
|
|
|
|
|
using dpf_type = dpf::utils::dpf_type_t<dpf::prg::aes128, dpf::prg::aes128, input_type, output_type0, output_type1>;
|
|
|
|
|
|
input_type x = 0xAA, from = 0x33, to = 0xCC;
|
|
|
|
|
|
output_type0 y0 = 0x55555555,
|
|
|
|
|
|
zero_output0 = 0;
|
|
|
|
|
|
output_type1 y1;
|
|
|
|
|
|
concrete_type y_exp = 0xAAAAAAAA,
|
|
|
|
|
|
y_shr0 = 0x12345678,
|
|
|
|
|
|
y_shr1 = y_exp - y_shr0,
|
|
|
|
|
|
zero_output1 = 0;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1);
|
|
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto ypoi0 = *dpf::eval_point<0>(dpf0, i);
|
|
|
|
|
|
auto ypoi1 = *dpf::eval_point<0>(dpf1, i);
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufint00, iterint00] = dpf::eval_interval<0>(dpf0, from, to);
|
|
|
|
|
|
auto [bufint01, iterint01] = dpf::eval_interval<0>(dpf1, from, to);
|
|
|
|
|
|
auto itint00 = std::begin(iterint00);
|
|
|
|
|
|
auto itint01 = std::begin(iterint01);
|
|
|
|
|
|
for (std::size_t i = from; i <= to; ++i, ++itint00, ++itint01)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itint00, std::end(iterint00));
|
|
|
|
|
|
ASSERT_EQ(itint01, std::end(iterint01));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufful00, iterful00] = dpf::eval_full<0>(dpf0);
|
|
|
|
|
|
auto [bufful01, iterful01] = dpf::eval_full<0>(dpf1);
|
|
|
|
|
|
auto itful00 = std::begin(iterful00);
|
|
|
|
|
|
auto itful01 = std::begin(iterful01);
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful00, ++itful01)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itful00, std::end(iterful00));
|
|
|
|
|
|
ASSERT_EQ(itful01, std::end(iterful01));
|
|
|
|
|
|
|
|
|
|
|
|
std::array<input_type, 16> points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
|
|
|
|
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
|
|
|
|
|
auto [bufseq00, iterseq00] = dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufseq01, iterseq01] = dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itseq00 = std::begin(iterseq00);
|
|
|
|
|
|
auto itseq01 = std::begin(iterseq01);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itseq00, ++itseq01)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itseq00, std::end(iterseq00));
|
|
|
|
|
|
ASSERT_EQ(itseq01, std::end(iterseq01));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufbre00, iterbre00] = dpf::eval_sequence_breadth_first<0>(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufbre01, iterbre01] = dpf::eval_sequence_breadth_first<0>(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itbre00 = std::begin(iterbre00);
|
|
|
|
|
|
auto itbre01 = std::begin(iterbre01);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itbre00, ++itbre01)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itbre00, std::end(iterbre00));
|
|
|
|
|
|
ASSERT_EQ(itbre01, std::end(iterbre01));
|
|
|
|
|
|
|
|
|
|
|
|
auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufrec00, iterrec00] = dpf::eval_sequence<0>(dpf0, recipe0);
|
|
|
|
|
|
auto [bufrec01, iterrec01] = dpf::eval_sequence<0>(dpf1, recipe1);
|
|
|
|
|
|
auto itrec00 = std::begin(iterrec00);
|
|
|
|
|
|
auto itrec01 = std::begin(iterrec01);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itrec00, ++itrec01)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), y0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), zero_output0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itrec00, std::end(iterrec00));
|
|
|
|
|
|
ASSERT_EQ(itrec01, std::end(iterrec01));
|
|
|
|
|
|
|
|
|
|
|
|
assign_leaf<1>(dpf0, dpf1, y_shr0, y_shr1);
|
|
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto ypoi0 = *dpf::eval_point<1>(dpf0, i);
|
|
|
|
|
|
auto ypoi1 = *dpf::eval_point<1>(dpf1, i);
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufint10, iterint10] = dpf::eval_interval<1>(dpf0, from, to);
|
|
|
|
|
|
auto [bufint11, iterint11] = dpf::eval_interval<1>(dpf1, from, to);
|
|
|
|
|
|
auto itint10 = std::begin(iterint10);
|
|
|
|
|
|
auto itint11 = std::begin(iterint11);
|
|
|
|
|
|
for (std::size_t i = from; i <= to; ++i, ++itint10, ++itint11)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itint10, std::end(iterint10));
|
|
|
|
|
|
ASSERT_EQ(itint11, std::end(iterint11));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufful10, iterful10] = dpf::eval_full<1>(dpf0);
|
|
|
|
|
|
auto [bufful11, iterful11] = dpf::eval_full<1>(dpf1);
|
|
|
|
|
|
auto itful10 = std::begin(iterful10);
|
|
|
|
|
|
auto itful11 = std::begin(iterful11);
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itful10, ++itful11)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itful10, std::end(iterful10));
|
|
|
|
|
|
ASSERT_EQ(itful11, std::end(iterful11));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufseq10, iterseq10] = dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufseq11, iterseq11] = dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itseq10 = std::begin(iterseq10);
|
|
|
|
|
|
auto itseq11 = std::begin(iterseq11);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itseq10, ++itseq11)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itseq10, std::end(iterseq10));
|
|
|
|
|
|
ASSERT_EQ(itseq11, std::end(iterseq11));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufbre10, iterbre10] = dpf::eval_sequence_breadth_first<1>(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufbre11, iterbre11] = dpf::eval_sequence_breadth_first<1>(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto itbre10 = std::begin(iterbre10);
|
|
|
|
|
|
auto itbre11 = std::begin(iterbre11);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itbre10, ++itbre11)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itbre10, std::end(iterbre10));
|
|
|
|
|
|
ASSERT_EQ(itbre11, std::end(iterbre11));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufrec10, iterrec10] = dpf::eval_sequence<1>(dpf0, recipe0);
|
|
|
|
|
|
auto [bufrec11, iterrec11] = dpf::eval_sequence<1>(dpf1, recipe1);
|
|
|
|
|
|
auto itrec10 = std::begin(iterrec10);
|
|
|
|
|
|
auto itrec11 = std::begin(iterrec11);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itrec10, ++itrec11)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itrec10, std::end(iterrec10));
|
|
|
|
|
|
ASSERT_EQ(itrec11, std::end(iterrec11));
|
|
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto ypoi0 = dpf::eval_point<0, 1>(dpf0, i);
|
|
|
|
|
|
auto ypoi1 = dpf::eval_point<0, 1>(dpf1, i);
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(static_cast<output_type0>(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), y0);
|
|
|
|
|
|
ASSERT_EQ(static_cast<concrete_type>(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(static_cast<output_type0>(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), zero_output0);
|
|
|
|
|
|
ASSERT_EQ(static_cast<concrete_type>(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufintmul0, iterintmul0] = dpf::eval_interval<0, 1>(dpf0, from, to);
|
|
|
|
|
|
auto [bufintmul1, iterintmul1] = dpf::eval_interval<0, 1>(dpf1, from, to);
|
|
|
|
|
|
auto zipint0 = dpf::tuple_as_zip(iterintmul0);
|
|
|
|
|
|
auto zipint1 = dpf::tuple_as_zip(iterintmul1);
|
|
|
|
|
|
auto itintmul0 = std::begin(zipint0);
|
|
|
|
|
|
auto itintmul1 = std::begin(zipint1);
|
|
|
|
|
|
for (std::size_t i = from; i <= to; ++i, ++itintmul0, ++itintmul1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), y0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), zero_output0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itintmul0, std::end(zipint0));
|
|
|
|
|
|
ASSERT_EQ(itintmul1, std::end(zipint1));
|
|
|
|
|
|
|
|
|
|
|
|
auto [buffulmul0, iterfulmul0] = dpf::eval_full<0, 1>(dpf0);
|
|
|
|
|
|
auto [buffulmul1, iterfulmul1] = dpf::eval_full<0, 1>(dpf1);
|
|
|
|
|
|
auto zipful0 = dpf::tuple_as_zip(iterfulmul0);
|
|
|
|
|
|
auto zipful1 = dpf::tuple_as_zip(iterfulmul1);
|
|
|
|
|
|
auto itfulmul0 = std::begin(zipful0);
|
|
|
|
|
|
auto itfulmul1 = std::begin(zipful1);
|
|
|
|
|
|
for (std::size_t i = std::numeric_limits<input_type>::min(); i <= std::numeric_limits<input_type>::max(); ++i, ++itfulmul0, ++itfulmul1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), y0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), zero_output0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itfulmul0, std::end(zipful0));
|
|
|
|
|
|
ASSERT_EQ(itfulmul1, std::end(zipful1));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufseqmul0, iterseqmul0] = dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points));
|
|
|
|
|
|
auto [bufseqmul1, iterseqmul1] = dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points));
|
|
|
|
|
|
auto zipseq0 = dpf::tuple_as_zip(iterseqmul0);
|
|
|
|
|
|
auto zipseq1 = dpf::tuple_as_zip(iterseqmul1);
|
|
|
|
|
|
auto itseqmul0 = std::begin(zipseq0);
|
|
|
|
|
|
auto itseqmul1 = std::begin(zipseq1);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itseqmul0, ++itseqmul1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), y0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), zero_output0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itseqmul0, std::end(zipseq0));
|
|
|
|
|
|
ASSERT_EQ(itseqmul1, std::end(zipseq1));
|
|
|
|
|
|
|
|
|
|
|
|
auto [bufrecmul0, iterrecmul0] = dpf::eval_sequence<0, 1>(dpf0, recipe0);
|
|
|
|
|
|
auto [bufrecmul1, iterrecmul1] = dpf::eval_sequence<0, 1>(dpf1, recipe1);
|
|
|
|
|
|
auto ziprec0 = dpf::tuple_as_zip(iterrecmul0);
|
|
|
|
|
|
auto ziprec1 = dpf::tuple_as_zip(iterrecmul1);
|
|
|
|
|
|
auto itrecmul0 = std::begin(ziprec0);
|
|
|
|
|
|
auto itrecmul1 = std::begin(ziprec1);
|
|
|
|
|
|
for (std::size_t i = 0; i < points.size(); ++i, ++itrecmul0, ++itrecmul1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (points[i] == x)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), y0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), y_exp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), zero_output0);
|
|
|
|
|
|
ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), zero_output1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ASSERT_EQ(itrecmul0, std::end(ziprec0));
|
|
|
|
|
|
ASSERT_EQ(itrecmul1, std::end(ziprec1));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, PackedSmallWildcardsAtNonTerminalAssignAll)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Four packed uint8 wildcards at a non-terminal prefix, plus a concrete
|
|
|
|
|
|
// deepest output. Assign every wildcard leaf through the asio path.
|
|
|
|
|
|
using concrete_t = uint8_t;
|
|
|
|
|
|
dpf::wildcard_value<concrete_t> w;
|
|
|
|
|
|
uint16_t x = 0x55aa;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x,
|
|
|
|
|
|
dpf::at<10>(w, w, w, w),
|
|
|
|
|
|
uint16_t{99});
|
|
|
|
|
|
|
2026-09-28 05:59:19 -06:00
|
|
|
|
ASSERT_THROW(dpf::eval_point(dpf::out<0, 10>, dpf0, x), std::runtime_error);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
ASSERT_EQ(static_cast<uint16_t>(
|
|
|
|
|
|
dpf::reconstruct(*dpf::eval_point(dpf::out<4>, dpf0, x), *dpf::eval_point(dpf::out<4>, dpf1, x))),
|
|
|
|
|
|
uint16_t{99});
|
|
|
|
|
|
|
|
|
|
|
|
const concrete_t want[4] = {1, 2, 3, 4};
|
|
|
|
|
|
const concrete_t shr0[4] = {10, 20, 30, 40};
|
|
|
|
|
|
assign_leaf<0>(dpf0, dpf1, shr0[0], static_cast<concrete_t>(want[0] - shr0[0]));
|
|
|
|
|
|
assign_leaf<1>(dpf0, dpf1, shr0[1], static_cast<concrete_t>(want[1] - shr0[1]));
|
|
|
|
|
|
assign_leaf<2>(dpf0, dpf1, shr0[2], static_cast<concrete_t>(want[2] - shr0[2]));
|
|
|
|
|
|
assign_leaf<3>(dpf0, dpf1, shr0[3], static_cast<concrete_t>(want[3] - shr0[3]));
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(static_cast<concrete_t>(
|
|
|
|
|
|
dpf::reconstruct(*dpf::eval_point(dpf::out<0, 10>, dpf0, x), *dpf::eval_point(dpf::out<0, 10>, dpf1, x))),
|
|
|
|
|
|
want[0]);
|
|
|
|
|
|
ASSERT_EQ(static_cast<concrete_t>(
|
|
|
|
|
|
dpf::reconstruct(*dpf::eval_point(dpf::out<3, 10>, dpf0, x), *dpf::eval_point(dpf::out<3, 10>, dpf1, x))),
|
|
|
|
|
|
want[3]);
|
|
|
|
|
|
ASSERT_EQ(static_cast<concrete_t>(
|
|
|
|
|
|
dpf::reconstruct(
|
|
|
|
|
|
*dpf::eval_point(dpf::out<0, 10>, dpf0,
|
|
|
|
|
|
static_cast<uint16_t>(x ^ (1u << 6))),
|
|
|
|
|
|
*dpf::eval_point(dpf::out<0, 10>, dpf1,
|
|
|
|
|
|
static_cast<uint16_t>(x ^ (1u << 6))))),
|
|
|
|
|
|
concrete_t{0});
|
|
|
|
|
|
}
|
2026-09-28 05:59:19 -06:00
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Bits, typename T>
|
|
|
|
|
|
T bits_as(Bits bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
T out{};
|
|
|
|
|
|
std::memcpy(&out, &bits, sizeof(T));
|
|
|
|
|
|
return out;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
|
auto bits_of(T v)
|
|
|
|
|
|
{
|
|
|
|
|
|
using bits_t = std::conditional_t<sizeof(T) == 4, std::uint32_t, std::uint64_t>;
|
|
|
|
|
|
bits_t bits{};
|
|
|
|
|
|
std::memcpy(&bits, &v, sizeof(T));
|
|
|
|
|
|
return bits;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// In-process assign for a wildcard leaf (same messages as asio, no socket).
|
|
|
|
|
|
template <std::size_t I = 0, typename DpfKey0, typename DpfKey1, typename ShareT>
|
|
|
|
|
|
void assign_leaf_local(DpfKey0 & dpf0, DpfKey1 & dpf1, const ShareT & shr0,
|
|
|
|
|
|
const ShareT & shr1)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto & w0 = std::get<I>(dpf0.leaf_nodes);
|
|
|
|
|
|
auto & w1 = std::get<I>(dpf1.leaf_nodes);
|
|
|
|
|
|
if (w0.is_ready())
|
|
|
|
|
|
w0.begin_update();
|
|
|
|
|
|
if (w1.is_ready())
|
|
|
|
|
|
w1.begin_update();
|
|
|
|
|
|
const auto b0 = w0.compute_and_get_blinded_output_share(shr0);
|
|
|
|
|
|
const auto b1 = w1.compute_and_get_blinded_output_share(shr1);
|
|
|
|
|
|
const auto l0 = w0.compute_and_get_leaf_share(b1);
|
|
|
|
|
|
const auto l1 = w1.compute_and_get_leaf_share(b0);
|
|
|
|
|
|
w0.reconstruct_correction_word(l1);
|
|
|
|
|
|
w1.reconstruct_correction_word(l0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, FullWidthXorDoesNotRevealBeta)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using concrete_type = dpf::xints::xint128_t;
|
|
|
|
|
|
using output_type = dpf::wildcard_value<concrete_type>;
|
|
|
|
|
|
input_type x = 0x42;
|
|
|
|
|
|
output_type y;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
|
|
|
|
|
|
|
|
|
|
|
|
auto & w0 = std::get<0>(dpf0.leaf_nodes);
|
|
|
|
|
|
auto & w1 = std::get<0>(dpf1.leaf_nodes);
|
|
|
|
|
|
// Scale Beaver must be planted even for a single full-width XOR lane.
|
|
|
|
|
|
EXPECT_EQ((dpf::outputs_per_leaf_v<concrete_type,
|
|
|
|
|
|
typename std::decay_t<decltype(dpf0)>::exterior_node>), 1u);
|
|
|
|
|
|
EXPECT_TRUE(dpf::utils::has_characteristic_two_v<concrete_type>);
|
|
|
|
|
|
|
|
|
|
|
|
concrete_type y_exp = concrete_type{0x0123456789ABCDEFull};
|
|
|
|
|
|
concrete_type y_shr0 = concrete_type{0x1111111111111111ull};
|
|
|
|
|
|
concrete_type y_shr1 = y_exp + y_shr0; // XOR group
|
|
|
|
|
|
|
|
|
|
|
|
const auto blinded0 = w0.compute_and_get_blinded_output_share(y_shr0);
|
|
|
|
|
|
const auto blinded1 = w1.compute_and_get_blinded_output_share(y_shr1);
|
|
|
|
|
|
// With a non-trivial output blind, the exchanged value is not the share.
|
|
|
|
|
|
EXPECT_NE(blinded0, y_shr0);
|
|
|
|
|
|
EXPECT_NE(blinded1, y_shr1);
|
|
|
|
|
|
// Finish the assign after the privacy check above (state is already blinded).
|
|
|
|
|
|
const auto l0 = w0.compute_and_get_leaf_share(blinded1);
|
|
|
|
|
|
const auto l1 = w1.compute_and_get_leaf_share(blinded0);
|
|
|
|
|
|
w0.reconstruct_correction_word(l1);
|
|
|
|
|
|
w1.reconstruct_correction_word(l0);
|
|
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)),
|
|
|
|
|
|
y_exp);
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast<input_type>(x ^ 1)),
|
|
|
|
|
|
*dpf::eval_point(dpf1, static_cast<input_type>(x ^ 1))),
|
|
|
|
|
|
concrete_type{});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, FloatWildcardRoundTrip)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using concrete_type = float;
|
|
|
|
|
|
using output_type = dpf::wildcard_value<concrete_type>;
|
|
|
|
|
|
input_type x = 0x55;
|
|
|
|
|
|
output_type y;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, y);
|
|
|
|
|
|
|
|
|
|
|
|
const concrete_type y_exp = 3.14159265f;
|
|
|
|
|
|
const auto y_bits = bits_of(y_exp);
|
|
|
|
|
|
const std::uint32_t shr0_bits = 0xA5A5A5A5u;
|
|
|
|
|
|
const concrete_type y_shr0 = bits_as<std::uint32_t, concrete_type>(shr0_bits);
|
|
|
|
|
|
const concrete_type y_shr1 =
|
|
|
|
|
|
bits_as<std::uint32_t, concrete_type>(y_bits ^ shr0_bits);
|
|
|
|
|
|
|
|
|
|
|
|
assign_leaf_local(dpf0, dpf1, y_shr0, y_shr1);
|
|
|
|
|
|
|
|
|
|
|
|
const auto got = dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x));
|
|
|
|
|
|
EXPECT_EQ(bits_of(got), y_bits);
|
|
|
|
|
|
EXPECT_EQ(bits_of(dpf::reconstruct(
|
|
|
|
|
|
*dpf::eval_point(dpf0, static_cast<input_type>(x ^ 1)),
|
|
|
|
|
|
*dpf::eval_point(dpf1, static_cast<input_type>(x ^ 1)))),
|
|
|
|
|
|
0u);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, SecondAssignUpdatesPayload)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using concrete_type = uint32_t;
|
|
|
|
|
|
using output_type = dpf::wildcard_value<concrete_type>;
|
|
|
|
|
|
input_type x = 0x11;
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(x, output_type{});
|
|
|
|
|
|
|
|
|
|
|
|
const concrete_type beta = 0xAAAAAAAAu;
|
|
|
|
|
|
const concrete_type beta2 = 0xBBBBBBBBu;
|
|
|
|
|
|
const concrete_type s0 = 0x12345678u;
|
|
|
|
|
|
assign_leaf_local(dpf0, dpf1, s0, static_cast<concrete_type>(beta - s0));
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)), beta);
|
|
|
|
|
|
|
|
|
|
|
|
// Second assign installs (beta2 - beta) on top of the ready leaf.
|
|
|
|
|
|
const concrete_type delta = static_cast<concrete_type>(beta2 - beta);
|
|
|
|
|
|
const concrete_type d0 = 0x01010101u;
|
|
|
|
|
|
assign_leaf_local(dpf0, dpf1, d0, static_cast<concrete_type>(delta - d0));
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)),
|
|
|
|
|
|
beta2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, AssignWildcardInputOpensPublicShiftNotAlpha)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = uint8_t;
|
|
|
|
|
|
using output_type = uint32_t;
|
|
|
|
|
|
// Wildcard domain: dealer plants a random mask; parties later open (mask - alpha).
|
|
|
|
|
|
auto [dpf0, dpf1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, output_type{7});
|
|
|
|
|
|
|
|
|
|
|
|
const input_type mask = static_cast<input_type>(
|
|
|
|
|
|
dpf0.offset_x.raw() + dpf1.offset_x.raw());
|
|
|
|
|
|
const input_type alpha = 0xAAu;
|
|
|
|
|
|
const input_type a0 = 0x12u;
|
|
|
|
|
|
const input_type a1 = static_cast<input_type>(alpha - a0);
|
|
|
|
|
|
|
|
|
|
|
|
const auto sh0 = dpf0.offset_x.compute_and_get_share(a0);
|
|
|
|
|
|
const auto sh1 = dpf1.offset_x.compute_and_get_share(a1);
|
|
|
|
|
|
const auto open0 = dpf0.offset_x.reconstruct(sh1);
|
|
|
|
|
|
const auto open1 = dpf1.offset_x.reconstruct(sh0);
|
|
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(open0, open1);
|
|
|
|
|
|
const input_type want_shift = static_cast<input_type>(mask - alpha);
|
|
|
|
|
|
EXPECT_EQ(open0, want_shift);
|
|
|
|
|
|
EXPECT_NE(open0, alpha); // public value is the shift, not alpha
|
|
|
|
|
|
EXPECT_EQ(static_cast<input_type>(open0 + alpha), mask);
|
|
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, alpha), *dpf::eval_point(dpf1, alpha)),
|
|
|
|
|
|
output_type{7});
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast<input_type>(alpha ^ 1)),
|
|
|
|
|
|
*dpf::eval_point(dpf1, static_cast<input_type>(alpha ^ 1))),
|
|
|
|
|
|
output_type{0});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WildcardTest, UpdatableTagAssignsThenRewrites)
|
|
|
|
|
|
{
|
|
|
|
|
|
const std::uint8_t alpha = 0x2a;
|
|
|
|
|
|
auto [k0, k1] = dpf::make_dpf(alpha, std::uint64_t{7}, dpf::updatable{});
|
|
|
|
|
|
EXPECT_TRUE(k0.is_wildcard(0));
|
|
|
|
|
|
EXPECT_TRUE(k1.is_wildcard(0));
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)),
|
|
|
|
|
|
std::uint64_t{7});
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, std::uint8_t{0}),
|
|
|
|
|
|
*dpf::eval_point(k1, std::uint8_t{0})),
|
|
|
|
|
|
std::uint64_t{0});
|
|
|
|
|
|
|
|
|
|
|
|
auto & w0 = std::get<0>(k0.leaf_nodes);
|
|
|
|
|
|
auto & w1 = std::get<0>(k1.leaf_nodes);
|
|
|
|
|
|
w0.begin_update();
|
|
|
|
|
|
w1.begin_update();
|
|
|
|
|
|
// A second assign installs the difference β' − β, not the new absolute payload.
|
|
|
|
|
|
const auto shares = dpf::additively_share(std::uint64_t{9} - std::uint64_t{7});
|
|
|
|
|
|
const auto b0 = w0.compute_and_get_blinded_output_share(shares.first.raw());
|
|
|
|
|
|
const auto b1 = w1.compute_and_get_blinded_output_share(shares.second.raw());
|
|
|
|
|
|
const auto l0 = w0.compute_and_get_leaf_share(b1);
|
|
|
|
|
|
const auto l1 = w1.compute_and_get_leaf_share(b0);
|
|
|
|
|
|
w0.reconstruct_correction_word(l1);
|
|
|
|
|
|
w1.reconstruct_correction_word(l0);
|
|
|
|
|
|
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)),
|
|
|
|
|
|
std::uint64_t{9});
|
|
|
|
|
|
}
|