libdpf/test/tests/corner_gaps_test.cpp

391 lines
13 KiB
C++
Raw Normal View History

#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);
}