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:
Ryan Henry 2026-09-24 20:44:07 -06:00
parent 875f09fec1
commit 0d8a5a8131
97 changed files with 9212 additions and 1159 deletions

View file

@ -0,0 +1,215 @@
#include <gtest/gtest.h>
#include "grotto/dyadic_lut.hpp"
#include <cstdint>
#include <limits>
namespace
{
int ref_clz(std::int16_t raw)
{
const auto bits = static_cast<std::uint16_t>(raw);
if (bits == 0)
return 16;
return __builtin_clz(static_cast<unsigned>(bits)) - (32 - 16);
}
int ref_clrsb(std::int16_t raw)
{
const auto bits = static_cast<std::uint16_t>(raw);
const bool neg = (bits & 0x8000u) != 0;
int count = 0;
for (int b = 14; b >= 0; --b)
{
const bool bit = ((bits >> b) & 1u) != 0;
if (bit != neg)
break;
++count;
}
return count;
}
int ref_ilogb(std::int16_t raw, unsigned k)
{
if (raw == 0)
return 0;
unsigned mag;
if (raw == std::numeric_limits<std::int16_t>::min())
mag = 1u << 15;
else
mag = static_cast<unsigned>(raw < 0 ? -raw : raw);
int log = 0;
while (mag > 1)
{
mag >>= 1;
++log;
}
return log - static_cast<int>(k);
}
int ref_ilog10(std::int64_t raw, unsigned k)
{
if (raw == 0)
return 0;
using u128 = unsigned __int128;
u128 mag;
if (raw == std::numeric_limits<std::int64_t>::min())
mag = u128{1} << 63;
else
mag = static_cast<u128>(raw < 0 ? -raw : raw);
int e = -static_cast<int>(k) - 2;
for (;;)
{
bool ge = false;
if (e >= 0)
{
u128 decade = 1;
for (int i = 0; i < e; ++i)
decade *= 10;
ge = mag >= (decade << k);
}
else
{
u128 decade = 1;
for (int i = 0; i < -e; ++i)
decade *= 10;
const u128 scale = u128{1} << k;
u128 threshold = scale / decade;
if (scale % decade != 0)
++threshold;
ge = mag >= threshold;
}
if (!ge)
return e - 1;
++e;
if (e > 40)
return 40;
}
}
std::int64_t enc(std::int64_t units, unsigned k)
{
return units << k;
}
} // namespace
TEST(DyadicLut, SignBundleInt16)
{
const auto positive = grotto::make_positive_lut<std::int16_t>();
const auto negative = grotto::make_negative_lut<std::int16_t>();
const auto nonnegative = grotto::make_nonnegative_lut<std::int16_t>();
const auto nonpositive = grotto::make_nonpositive_lut<std::int16_t>();
const auto zero = grotto::make_zero_lut<std::int16_t>();
const auto nonzero = grotto::make_nonzero_lut<std::int16_t>();
const auto signum = grotto::make_signum_lut<std::int16_t>();
EXPECT_EQ(positive.parts(), 2u);
EXPECT_EQ(signum.parts(), 3u);
EXPECT_EQ(zero.parts(), 3u);
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
{
const auto x = static_cast<std::int16_t>(raw);
EXPECT_EQ(positive(x), x > 0 ? 1 : 0);
EXPECT_EQ(negative(x), x < 0 ? 1 : 0);
EXPECT_EQ(nonnegative(x), x >= 0 ? 1 : 0);
EXPECT_EQ(nonpositive(x), x <= 0 ? 1 : 0);
EXPECT_EQ(zero(x), x == 0 ? 1 : 0);
EXPECT_EQ(nonzero(x), x != 0 ? 1 : 0);
const int sgn = x < 0 ? -1 : (x > 0 ? 1 : 0);
EXPECT_EQ(signum(x), sgn);
}
const auto scaled = grotto::make_positive_lut<std::int16_t>(4);
EXPECT_EQ(scaled(std::int16_t{1}), 16);
EXPECT_EQ(scaled(std::int16_t{0}), 0);
EXPECT_EQ(grotto::make_signum_lut<std::int16_t>(4)(std::int16_t{-3}), -16);
}
TEST(DyadicLut, ClzAndClrsbInt16)
{
const auto clz = grotto::make_clz_lut<std::int16_t>();
const auto clrsb = grotto::make_clrsb_lut<std::int16_t>();
EXPECT_EQ(clz.parts(), 17u);
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
{
const auto x = static_cast<std::int16_t>(raw);
EXPECT_EQ(clz(x), ref_clz(x)) << raw;
EXPECT_EQ(clrsb(x), ref_clrsb(x)) << raw;
}
const auto scaled = grotto::make_clz_lut<std::int16_t>(3);
EXPECT_EQ(scaled(std::int16_t{1}), ref_clz(1) << 3);
}
TEST(DyadicLut, IntegerLogsInt16)
{
for (unsigned k : {0u, 4u})
{
const auto lg = grotto::make_ilogb_lut<std::int16_t>(k);
const auto log10 = grotto::make_ilog10_lut<std::int16_t>(k);
EXPECT_EQ(lg(std::int16_t{0}), grotto::ilog_of_zero);
EXPECT_EQ(log10(std::int16_t{0}), grotto::ilog_of_zero);
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
{
if (raw == 0)
continue;
const auto x = static_cast<std::int16_t>(raw);
EXPECT_EQ(lg(x), enc(ref_ilogb(x, k), k)) << raw << " k=" << k;
EXPECT_EQ(log10(x), enc(ref_ilog10(raw, k), k)) << raw << " k=" << k;
}
}
EXPECT_EQ(grotto::make_ilogb_lut<std::int16_t>()(std::int16_t{1}), 0);
EXPECT_EQ(grotto::make_ilogb_lut<std::int16_t>()(std::int16_t{2}), 1);
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{9}), 0);
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{10}), 1);
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>(4)(std::int16_t{1}), enc(-2, 4));
}
TEST(DyadicLut, MostSignificantBits)
{
for (unsigned i = 0; i < grotto::msb_bit_limit && i < 16; ++i)
{
const auto lut = grotto::make_msb_lut<std::int16_t>(i);
EXPECT_EQ(lut.parts(), std::size_t{1} << (i + 1)) << i;
const int shift = 15 - static_cast<int>(i);
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
{
const auto x = static_cast<std::int16_t>(raw);
const int bit = (static_cast<std::uint16_t>(x) >> shift) & 1;
EXPECT_EQ(lut(x), bit) << raw << " i=" << i;
}
}
EXPECT_THROW(grotto::make_msb_lut<std::int16_t>(grotto::msb_bit_limit),
std::invalid_argument);
const auto scaled = grotto::make_msb_lut<std::int16_t>(0, 4);
EXPECT_EQ(scaled(std::int16_t{-1}), 16);
EXPECT_EQ(scaled(std::int16_t{1}), 0);
}
TEST(DyadicLut, Int64Edges)
{
const auto clz = grotto::make_clz_lut<std::int64_t>();
EXPECT_EQ(clz(std::int64_t{0}), 64);
EXPECT_EQ(clz(std::int64_t{-1}), 0);
EXPECT_EQ(clz(std::int64_t{1}), 63);
EXPECT_EQ(clz(std::numeric_limits<std::int64_t>::min()), 0);
EXPECT_EQ(clz(std::int64_t{1} << 62), 1);
const auto clrsb = grotto::make_clrsb_lut<std::int64_t>();
EXPECT_EQ(clrsb(std::int64_t{0}), 63);
EXPECT_EQ(clrsb(std::int64_t{-1}), 63);
EXPECT_EQ(clrsb(std::int64_t{1}), 62);
EXPECT_EQ(clrsb(std::numeric_limits<std::int64_t>::min()), 0);
const auto lg = grotto::make_ilogb_lut<std::int64_t>(16);
EXPECT_EQ(lg(std::int64_t{1} << 16), 0);
EXPECT_EQ(lg(std::int64_t{1} << 17), enc(1, 16));
EXPECT_EQ(lg(std::numeric_limits<std::int64_t>::min()), enc(63 - 16, 16));
const auto bit = grotto::make_msb_lut<std::int64_t>(0);
EXPECT_EQ(bit.parts(), 2u);
EXPECT_EQ(bit(std::int64_t{-5}), 1);
EXPECT_EQ(bit(std::int64_t{5}), 0);
}