libdpf/test/tests/defer_eval_test.cpp

612 lines
22 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <limits>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "dpf.hpp"
namespace
{
template <typename A, typename B>
auto recon(const A & a, const B & b)
{
if constexpr (dpf::is_secret_share_v<std::decay_t<A>>
&& dpf::is_secret_share_v<std::decay_t<B>>)
return dpf::reconstruct(a, b);
else
{
using T = std::common_type_t<std::decay_t<A>, std::decay_t<B>>;
if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>)
return static_cast<T>(a - b);
else
return a - b;
}
}
template <typename Key0, typename Key1, typename InputT>
void assign_input_local(Key0 & k0, Key1 & k1, InputT alpha)
{
using input_type = InputT;
const input_type a0 = static_cast<input_type>(0x12);
const input_type a1 = static_cast<input_type>(alpha - a0);
const auto sh0 = k0.offset_x.compute_and_get_share(a0);
const auto sh1 = k1.offset_x.compute_and_get_share(a1);
k0.offset_x.reconstruct(sh1);
k1.offset_x.reconstruct(sh0);
}
template <typename Def0, typename Def1, typename Eager0, typename Eager1>
void expect_recon_equal(Def0 && deferred0, Def1 && deferred1,
Eager0 && eager0, Eager1 && eager1)
{
auto it_a = std::begin(deferred0);
auto it_b = std::begin(deferred1);
auto it_c = std::begin(eager0);
auto it_d = std::begin(eager1);
const auto end_a = std::end(deferred0);
std::size_t n = 0;
while (it_a != end_a)
{
ASSERT_NE(it_b, std::end(deferred1)) << "at index " << n;
ASSERT_NE(it_c, std::end(eager0)) << "at index " << n;
ASSERT_NE(it_d, std::end(eager1)) << "at index " << n;
EXPECT_EQ(recon(*it_a, *it_b), recon(*it_c, *it_d)) << "at index " << n;
++it_a;
++it_b;
++it_c;
++it_d;
++n;
}
EXPECT_EQ(it_b, std::end(deferred1));
EXPECT_EQ(it_c, std::end(eager0));
EXPECT_EQ(it_d, std::end(eager1));
EXPECT_GT(n, std::size_t{0});
}
template <typename InputT>
std::size_t inclusive_span(InputT from, InputT to)
{
constexpr auto bits = dpf::utils::bitlength_of_v<InputT>;
constexpr auto to_int = dpf::utils::to_integral_type<InputT>{};
auto span = to_int(to) - to_int(from);
if constexpr (bits < dpf::utils::bitlength_of_v<decltype(span)>)
span &= (decltype(span){1} << bits) - 1;
return static_cast<std::size_t>(span) + 1;
}
template <typename View0, typename View1, typename InputT, typename OutputT>
void expect_point_mass(View0 && v0, View1 && v1, InputT from, InputT to,
InputT alpha, OutputT beta)
{
auto it0 = std::begin(v0);
auto it1 = std::begin(v1);
bool saw = false;
InputT x = from;
for (std::size_t i = 0; i < inclusive_span(from, to); ++i)
{
ASSERT_NE(it0, std::end(v0));
ASSERT_NE(it1, std::end(v1));
const auto y = recon(*it0, *it1);
if (x == alpha)
{
EXPECT_EQ(y, beta) << "x=" << +x;
saw = true;
}
else
{
EXPECT_EQ(y, OutputT{0}) << "x=" << +x;
}
++it0;
++it1;
++x;
}
EXPECT_EQ(it0, std::end(v0));
EXPECT_EQ(it1, std::end(v1));
bool expect_hit = false;
if constexpr (std::is_unsigned_v<InputT>)
{
if (from <= to)
expect_hit = (alpha >= from && alpha <= to);
else
expect_hit = (alpha >= from) || (alpha <= to);
}
else
{
expect_hit = (alpha >= from && alpha <= to);
}
EXPECT_EQ(saw, expect_hit);
}
template <typename Key0, typename Key1, typename InputT, typename OutputT>
void compare_deferred_interval(Key0 & d0, Key1 & d1, InputT from, InputT to,
InputT alpha, OutputT beta)
{
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_interval(d0, from, to, buf0);
auto deferred1 = dpf::defer_eval_interval(d1, from, to, buf1);
assign_input_local(d0, d1, alpha);
auto eager_buf0 = dpf::make_output_buffer_for_interval(d0, from, to);
auto eager_buf1 = dpf::make_output_buffer_for_interval(d1, from, to);
auto eager_memo0 = dpf::make_basic_full_memoizer(d0);
auto eager_memo1 = dpf::make_basic_full_memoizer(d1);
auto eager0 = dpf::eval_interval(d0, from, to, eager_buf0, eager_memo0);
auto eager1 = dpf::eval_interval(d1, from, to, eager_buf1, eager_memo1);
auto view0 = deferred0.get();
auto view1 = deferred1.get();
expect_recon_equal(view0, view1, eager0, eager1);
expect_point_mass(view0, view1, from, to, alpha, beta);
}
} // namespace
// ---------------------------------------------------------------------------
// Happy paths already covered lightly; keep regressions + expand corners.
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, IntervalMatchesEagerAfterAssignUint8)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{7});
compare_deferred_interval(d0, d1, input_type{0x10}, input_type{0x40},
input_type{0x2A}, output_type{7});
}
TEST(DeferEvalTest, FullMatchesEagerAfterAssignUint8)
{
using input_type = std::uint8_t;
using output_type = std::uint64_t;
constexpr output_type beta{0x1111};
constexpr input_type alpha{0x33};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, beta);
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_full(d0, buf0);
auto deferred1 = dpf::defer_eval_full(d1, buf1);
assign_input_local(d0, d1, alpha);
auto eager_buf0 = dpf::make_output_buffer_for_full(d0);
auto eager_buf1 = dpf::make_output_buffer_for_full(d1);
auto eager0 = dpf::eval_full(d0, eager_buf0);
auto eager1 = dpf::eval_full(d1, eager_buf1);
auto view0 = deferred0.get();
auto view1 = deferred1.get();
expect_recon_equal(view0, view1, eager0, eager1);
expect_point_mass(view0, view1,
std::numeric_limits<input_type>::min(),
std::numeric_limits<input_type>::max(), alpha, beta);
}
TEST(DeferEvalTest, IntervalMatchesEagerAfterAssignSigned)
{
using input_type = std::int8_t;
using output_type = std::uint32_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{9});
compare_deferred_interval(d0, d1, input_type{-40}, input_type{10},
input_type{-20}, output_type{9});
}
TEST(DeferEvalTest, MultiOutputLeafMatchesEager)
{
using input_type = std::uint8_t;
using output_type = std::uint64_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{0xABCDEF0123456789ull});
ASSERT_GT(decltype(d0)::outputs_per_leaf, std::size_t{1});
compare_deferred_interval(d0, d1, input_type{0x70}, input_type{0x8F},
input_type{0x7E}, output_type{0xABCDEF0123456789ull});
}
// ---------------------------------------------------------------------------
// Assert / misuse corners
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, GetBeforeAssignThrows)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
(void)d1;
auto buf = dpf::make_output_buffer_for_full(d0);
auto deferred = dpf::defer_eval_interval(d0, input_type{0}, input_type{3},
buf);
EXPECT_THROW(static_cast<void>(deferred.get()), std::runtime_error);
EXPECT_THROW(static_cast<void>(deferred.begin()), std::runtime_error);
}
TEST(DeferEvalTest, DeferAfterAssignThrows)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
assign_input_local(d0, d1, input_type{9});
auto buf = dpf::make_output_buffer_for_full(d0);
EXPECT_THROW(
static_cast<void>(dpf::defer_eval_interval(d0, input_type{0},
input_type{3}, buf)),
std::runtime_error);
EXPECT_THROW(static_cast<void>(dpf::defer_eval_full(d0, buf)),
std::runtime_error);
}
TEST(DeferEvalTest, EagerEvalBeforeAssignThrows)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
(void)d1;
EXPECT_THROW(static_cast<void>(dpf::eval_point(d0, input_type{0})),
std::runtime_error);
EXPECT_THROW(static_cast<void>(dpf::eval_interval(d0, input_type{0},
input_type{1})),
std::runtime_error);
EXPECT_THROW(static_cast<void>(dpf::eval_full(d0)), std::runtime_error);
}
TEST(DeferEvalTest, DeferTraverseIntervalRequiresAssignedInput)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
auto memo = dpf::make_basic_full_memoizer(d0);
EXPECT_THROW(
dpf::defer_traverse_interval(d0, input_type{0}, input_type{10}, memo),
std::runtime_error);
assign_input_local(d0, d1, input_type{4});
EXPECT_NO_THROW(
dpf::defer_traverse_interval(d0, input_type{0}, input_type{10}, memo));
}
TEST(DeferEvalTest, DeferTraverseFullAllowsUnassignedInput)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{3});
auto memo0 = dpf::make_basic_full_memoizer(d0);
auto memo1 = dpf::make_basic_full_memoizer(d1);
EXPECT_NO_THROW(dpf::defer_traverse_full(d0, memo0));
EXPECT_NO_THROW(dpf::defer_traverse_full(d1, memo1));
assign_input_local(d0, d1, input_type{0x55});
EXPECT_EQ(recon(*dpf::eval_point(d0, input_type{0x55}),
*dpf::eval_point(d1, input_type{0x55})),
std::uint32_t{3});
}
// ---------------------------------------------------------------------------
// Boundary / length / caching
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, SinglePointInterval)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
constexpr input_type alpha{0x77};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{42});
compare_deferred_interval(d0, d1, alpha, alpha, alpha, output_type{42});
}
TEST(DeferEvalTest, SpikeAtFromAndToBoundaries)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
{
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{5});
compare_deferred_interval(d0, d1, input_type{0x20}, input_type{0x30},
input_type{0x20}, output_type{5});
}
{
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{6});
compare_deferred_interval(d0, d1, input_type{0x20}, input_type{0x30},
input_type{0x30}, output_type{6});
}
}
TEST(DeferEvalTest, SpikeOutsideIntervalIsZero)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{99});
compare_deferred_interval(d0, d1, input_type{0x10}, input_type{0x20},
input_type{0x80}, output_type{99});
}
TEST(DeferEvalTest, ViewLengthMatchesInclusiveSpan)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
constexpr input_type from{5};
constexpr input_type to{12};
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_interval(d0, from, to, buf0);
auto deferred1 = dpf::defer_eval_interval(d1, from, to, buf1);
assign_input_local(d0, d1, input_type{7});
auto view0 = deferred0.get();
auto view1 = deferred1.get();
EXPECT_EQ(static_cast<std::size_t>(std::distance(std::begin(view0),
std::end(view0))),
inclusive_span(from, to));
EXPECT_EQ(static_cast<std::size_t>(std::distance(std::begin(view1),
std::end(view1))),
inclusive_span(from, to));
}
TEST(DeferEvalTest, GetCachesRotationSecondCallMatches)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{11});
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_interval(d0, input_type{1}, input_type{20},
buf0);
auto deferred1 = dpf::defer_eval_interval(d1, input_type{1}, input_type{20},
buf1);
assign_input_local(d0, d1, input_type{9});
auto a0 = deferred0.get();
auto a1 = deferred1.get();
auto b0 = deferred0.get();
auto b1 = deferred1.get();
expect_recon_equal(a0, a1, b0, b1);
}
TEST(DeferEvalTest, BeginEndOnDeferredObject)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{2});
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_interval(d0, input_type{0}, input_type{4},
buf0);
auto deferred1 = dpf::defer_eval_interval(d1, input_type{0}, input_type{4},
buf1);
assign_input_local(d0, d1, input_type{2});
std::size_t n = 0;
auto it0 = deferred0.begin();
auto it1 = deferred1.begin();
for (; it0 != deferred0.end(); ++it0, ++it1, ++n)
(void)recon(*it0, *it1);
EXPECT_EQ(it1, deferred1.end());
EXPECT_EQ(n, inclusive_span(input_type{0}, input_type{4}));
}
TEST(DeferEvalTest, DeferEvalIntervalMinMaxMatchesDeferFull)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
constexpr output_type beta{13};
constexpr input_type alpha{0x01};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, beta);
auto buf_i0 = dpf::make_output_buffer_for_full(d0);
auto buf_i1 = dpf::make_output_buffer_for_full(d1);
auto buf_f0 = dpf::make_output_buffer_for_full(d0);
auto buf_f1 = dpf::make_output_buffer_for_full(d1);
auto as_interval0 = dpf::defer_eval_interval(d0,
std::numeric_limits<input_type>::min(),
std::numeric_limits<input_type>::max(), buf_i0);
auto as_interval1 = dpf::defer_eval_interval(d1,
std::numeric_limits<input_type>::min(),
std::numeric_limits<input_type>::max(), buf_i1);
auto as_full0 = dpf::defer_eval_full(d0, buf_f0);
auto as_full1 = dpf::defer_eval_full(d1, buf_f1);
assign_input_local(d0, d1, alpha);
expect_recon_equal(as_interval0.get(), as_interval1.get(),
as_full0.get(), as_full1.get());
}
// ---------------------------------------------------------------------------
// Wrapping intervals and offset stress
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, WrappingLogicalIntervalUint8)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
// [200, 10] wraps across 0.
constexpr input_type from{200};
constexpr input_type to{10};
ASSERT_GT(from, to);
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{8});
compare_deferred_interval(d0, d1, from, to, input_type{250},
output_type{8});
}
TEST(DeferEvalTest, WrappingIntervalSpikeInLowHalf)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{4});
compare_deferred_interval(d0, d1, input_type{200}, input_type{10},
input_type{5}, std::uint32_t{4});
}
TEST(DeferEvalTest, ManyRandomOffsetsMatchEager)
{
using input_type = std::uint8_t;
using output_type = std::uint32_t;
constexpr input_type from{30};
constexpr input_type to{90};
constexpr output_type beta{77};
// Sweep alphas; each keygen draws a fresh mask so offsets differ.
for (unsigned a = 0; a < 256; a += 17)
{
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, beta);
compare_deferred_interval(d0, d1, from, to,
static_cast<input_type>(a), beta);
}
}
TEST(DeferEvalTest, SignedFullDomain)
{
using input_type = std::int8_t;
using output_type = std::uint32_t;
constexpr output_type beta{21};
constexpr input_type alpha{-128};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, beta);
auto buf0 = dpf::make_output_buffer_for_full(d0);
auto buf1 = dpf::make_output_buffer_for_full(d1);
auto deferred0 = dpf::defer_eval_full(d0, buf0);
auto deferred1 = dpf::defer_eval_full(d1, buf1);
assign_input_local(d0, d1, alpha);
auto eager_buf0 = dpf::make_output_buffer_for_full(d0);
auto eager_buf1 = dpf::make_output_buffer_for_full(d1);
auto eager0 = dpf::eval_full(d0, eager_buf0);
auto eager1 = dpf::eval_full(d1, eager_buf1);
expect_recon_equal(deferred0.get(), deferred1.get(), eager0, eager1);
}
TEST(DeferEvalTest, SignedSpanCrossingZero)
{
using input_type = std::int8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{3});
compare_deferred_interval(d0, d1, input_type{-5}, input_type{5},
input_type{0}, std::uint32_t{3});
}
TEST(DeferEvalTest, UnalignedMultiOutputLeafInterval)
{
using input_type = std::uint8_t;
using output_type = std::uint64_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
output_type{0x55});
ASSERT_GT(decltype(d0)::outputs_per_leaf, std::size_t{1});
// Odd endpoints: not leaf-aligned when opl == 2.
compare_deferred_interval(d0, d1, input_type{0x11}, input_type{0x2A},
input_type{0x1F}, output_type{0x55});
}
// ---------------------------------------------------------------------------
// Multi-output keys
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, TwoOutputSlotsIndependent)
{
using input_type = std::uint8_t;
using out0 = std::uint32_t;
using out1 = std::uint32_t;
constexpr input_type alpha{0x44};
constexpr out0 beta0{100};
constexpr out1 beta1{200};
constexpr input_type from{0x40};
constexpr input_type to{0x50};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{}, beta0,
beta1);
auto bufs0 = dpf::make_output_buffer_for_full<0, 1>(d0);
auto bufs1 = dpf::make_output_buffer_for_full<0, 1>(d1);
auto def0 = dpf::defer_eval_interval<0, 1>(d0, from, to, bufs0);
auto def1 = dpf::defer_eval_interval<0, 1>(d1, from, to, bufs1);
assign_input_local(d0, d1, alpha);
auto eager_bufs0 = dpf::make_output_buffer_for_interval<0, 1>(d0, from, to);
auto eager_bufs1 = dpf::make_output_buffer_for_interval<0, 1>(d1, from, to);
auto memo0 = dpf::make_basic_full_memoizer(d0);
auto memo1 = dpf::make_basic_full_memoizer(d1);
auto eager0 = dpf::eval_interval<0, 1>(d0, from, to, eager_bufs0, memo0);
auto eager1 = dpf::eval_interval<0, 1>(d1, from, to, eager_bufs1, memo1);
expect_recon_equal(std::get<0>(def0).get(), std::get<0>(def1).get(),
std::get<0>(eager0), std::get<0>(eager1));
expect_recon_equal(std::get<1>(def0).get(), std::get<1>(def1).get(),
std::get<1>(eager0), std::get<1>(eager1));
expect_point_mass(std::get<0>(def0).get(), std::get<0>(def1).get(),
from, to, alpha, beta0);
expect_point_mass(std::get<1>(def0).get(), std::get<1>(def1).get(),
from, to, alpha, beta1);
}
TEST(DeferEvalTest, SelectSecondOutputOnly)
{
using input_type = std::uint8_t;
constexpr input_type alpha{0x08};
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1}, std::uint32_t{99});
auto buf0 = dpf::make_output_buffer_for_full<1>(d0);
auto buf1 = dpf::make_output_buffer_for_full<1>(d1);
auto def0 = dpf::defer_eval_interval<1>(d0, input_type{0}, input_type{15},
buf0);
auto def1 = dpf::defer_eval_interval<1>(d1, input_type{0}, input_type{15},
buf1);
assign_input_local(d0, d1, alpha);
auto eager_buf0 = dpf::make_output_buffer_for_interval<1>(d0, input_type{0},
input_type{15});
auto eager_buf1 = dpf::make_output_buffer_for_interval<1>(d1, input_type{0},
input_type{15});
auto memo0 = dpf::make_basic_full_memoizer(d0);
auto memo1 = dpf::make_basic_full_memoizer(d1);
auto eager0 = dpf::eval_interval<1>(d0, input_type{0}, input_type{15},
eager_buf0, memo0);
auto eager1 = dpf::eval_interval<1>(d1, input_type{0}, input_type{15},
eager_buf1, memo1);
expect_recon_equal(def0.get(), def1.get(), eager0, eager1);
expect_point_mass(def0.get(), def1.get(), input_type{0}, input_type{15},
alpha, std::uint32_t{99});
}
// ---------------------------------------------------------------------------
// Metadata on the deferred object
// ---------------------------------------------------------------------------
TEST(DeferEvalTest, DeferredStoresFromToAndKey)
{
using input_type = std::uint8_t;
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
(void)d1;
auto buf = dpf::make_output_buffer_for_full(d0);
auto deferred = dpf::defer_eval_interval(d0, input_type{3}, input_type{9},
buf);
EXPECT_EQ(deferred.from(), input_type{3});
EXPECT_EQ(deferred.to(), input_type{9});
EXPECT_EQ(&deferred.dpf(), &d0);
}
TEST(DeferEvalTest, DomainMinMaxSpike)
{
using input_type = std::uint8_t;
{
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{1});
compare_deferred_interval(d0, d1, input_type{0}, input_type{255},
input_type{0}, std::uint32_t{1});
}
{
auto [d0, d1] = dpf::make_dpf(dpf::wildcard_value<input_type>{},
std::uint32_t{2});
compare_deferred_interval(d0, d1, input_type{0}, input_type{255},
input_type{255}, std::uint32_t{2});
}
}