Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make remaining simple functions constexpr #305

Merged
merged 5 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions include/intx/intx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,24 +270,24 @@ inline constexpr uint128 operator-(uint128 x) noexcept
return 0 - x;
}

inline uint128& operator++(uint128& x) noexcept
inline constexpr uint128& operator++(uint128& x) noexcept
{
return x = x + 1;
}

inline uint128& operator--(uint128& x) noexcept
inline constexpr uint128& operator--(uint128& x) noexcept
{
return x = x - 1;
}

inline const uint128 operator++(uint128& x, int) noexcept // NOLINT(readability-const-return-type)
inline constexpr const uint128 operator++(uint128& x, int) noexcept // NOLINT(*-const-return-type)
{
const auto ret = x;
++x;
return ret;
}

inline const uint128 operator--(uint128& x, int) noexcept // NOLINT(readability-const-return-type)
inline constexpr const uint128 operator--(uint128& x, int) noexcept // NOLINT(*-const-return-type)
{
const auto ret = x;
--x;
Expand Down Expand Up @@ -593,6 +593,8 @@ struct div_result
QuotT quot;
RemT rem;

bool operator==(const div_result&) const = default;

/// Conversion to tuple of references, to allow usage with std::tie().
constexpr operator std::tuple<QuotT&, RemT&>() noexcept { return {quot, rem}; }
};
Expand Down Expand Up @@ -627,7 +629,7 @@ constexpr uint16_t reciprocal_table[] = {REPEAT256()};
/// Computes the reciprocal (2^128 - 1) / d - 2^64 for normalized d.
///
/// Based on Algorithm 2 from "Improved division by invariant integers".
inline uint64_t reciprocal_2by1(uint64_t d) noexcept
inline constexpr uint64_t reciprocal_2by1(uint64_t d) noexcept
{
INTX_REQUIRE(d & 0x8000000000000000); // Must be normalized.

Expand All @@ -648,7 +650,7 @@ inline uint64_t reciprocal_2by1(uint64_t d) noexcept
return v4;
}

inline uint64_t reciprocal_3by2(uint128 d) noexcept
inline constexpr uint64_t reciprocal_3by2(uint128 d) noexcept
{
auto v = reciprocal_2by1(d[1]);
auto p = d[1] * v;
Expand Down Expand Up @@ -679,7 +681,7 @@ inline uint64_t reciprocal_3by2(uint128 d) noexcept
return v;
}

inline div_result<uint64_t> udivrem_2by1(uint128 u, uint64_t d, uint64_t v) noexcept
inline constexpr div_result<uint64_t> udivrem_2by1(uint128 u, uint64_t d, uint64_t v) noexcept
{
auto q = umul(v, u[1]);
q = fast_add(q, u);
Expand All @@ -703,7 +705,7 @@ inline div_result<uint64_t> udivrem_2by1(uint128 u, uint64_t d, uint64_t v) noex
return {q[1], r};
}

inline div_result<uint64_t, uint128> udivrem_3by2(
inline constexpr div_result<uint64_t, uint128> udivrem_3by2(
uint64_t u2, uint64_t u1, uint64_t u0, uint128 d, uint64_t v) noexcept
{
auto q = umul(v, u2);
Expand Down Expand Up @@ -733,7 +735,7 @@ inline div_result<uint64_t, uint128> udivrem_3by2(
return {q[1], r};
}

inline div_result<uint128> udivrem(uint128 x, uint128 y) noexcept
inline constexpr div_result<uint128> udivrem(uint128 x, uint128 y) noexcept
{
if (y[1] == 0)
{
Expand Down Expand Up @@ -778,7 +780,7 @@ inline div_result<uint128> udivrem(uint128 x, uint128 y) noexcept
return {res.quot, res.rem >> lsh};
}

inline div_result<uint128> sdivrem(uint128 x, uint128 y) noexcept
inline constexpr div_result<uint128> sdivrem(uint128 x, uint128 y) noexcept
{
constexpr auto sign_mask = uint128{1} << 127;
const auto x_is_neg = (x & sign_mask) != 0;
Expand All @@ -794,22 +796,22 @@ inline div_result<uint128> sdivrem(uint128 x, uint128 y) noexcept
return {q_is_neg ? -res.quot : res.quot, x_is_neg ? -res.rem : res.rem};
}

inline uint128 operator/(uint128 x, uint128 y) noexcept
inline constexpr uint128 operator/(uint128 x, uint128 y) noexcept
{
return udivrem(x, y).quot;
}

inline uint128 operator%(uint128 x, uint128 y) noexcept
inline constexpr uint128 operator%(uint128 x, uint128 y) noexcept
{
return udivrem(x, y).rem;
}

inline uint128& operator/=(uint128& x, uint128 y) noexcept
inline constexpr uint128& operator/=(uint128& x, uint128 y) noexcept
{
return x = x / y;
}

inline uint128& operator%=(uint128& x, uint128 y) noexcept
inline constexpr uint128& operator%=(uint128& x, uint128 y) noexcept
{
return x = x % y;
}
Expand Down Expand Up @@ -1001,8 +1003,9 @@ struct uint

constexpr explicit operator bool() const noexcept { return *this != uint{}; }

/// Explicit converting operator to smaller uint types.
template <unsigned M>
explicit operator uint<M>() const noexcept
constexpr explicit operator uint<M>() const noexcept
requires(M < N)
{
uint<M> r;
Expand All @@ -1013,7 +1016,7 @@ struct uint

/// Explicit converting operator for all builtin integral types.
template <typename Int>
explicit operator Int() const noexcept
constexpr explicit operator Int() const noexcept
requires(std::is_integral_v<Int>)
{
static_assert(sizeof(Int) <= sizeof(uint64_t));
Expand Down
3 changes: 3 additions & 0 deletions test/unittests/test_div.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ inline uint64_t reciprocal_naive(uint64_t d) noexcept

TEST(div, reciprocal)
{
static_assert(reciprocal_2by1(0x8000000000000000) == 0xffffffffffffffff);

constexpr auto n = 1000000;

constexpr auto d_start = uint64_t{1} << 63;
Expand All @@ -466,6 +468,7 @@ TEST(div, reciprocal)
TEST(div, reciprocal_3by2)
{
// Basic inputs for reciprocal_3by2() to make porting to other languages easier.
static_assert(reciprocal_3by2(0x80000000000000000000000000000000_u128) == 0xffffffffffffffff);

EXPECT_EQ(reciprocal_3by2({0x0000000000000000, 0x8000000000000000}), 0xffffffffffffffffu);
EXPECT_EQ(reciprocal_3by2({0x0000000000000001, 0x8000000000000000}), 0xffffffffffffffffu);
Expand Down
34 changes: 34 additions & 0 deletions test/unittests/test_int128.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,26 @@ TEST(int128, mul)

TEST(int128, increment)
{
static_assert([] {
uint128 x;
return ++x;
}() == 1);

static_assert([] {
uint128 x;
return x++;
}() == 0);

static_assert([] {
uint128 x = 1;
return --x;
}() == 0);

static_assert([] {
uint128 x = 1;
return x++;
}() == 1);

constexpr auto IO = uint128{0, 1};
constexpr auto Of = uint128{~uint64_t{0}};

Expand Down Expand Up @@ -307,6 +327,16 @@ TEST(int128, shr)

TEST(int128, div)
{
static_assert(7_u128 / 3_u128 == 2_u128);
static_assert(7_u128 % 3_u128 == 1_u128);
static_assert(udivrem(7, 3) == div_result<uint128>{2, 1});
static_assert(udivrem(0x10000000000000000_u128, 0x20000000000000000_u128) ==
div_result<uint128>{0, 0x10000000000000000_u128});
static_assert(udivrem(0x80000000000000000000000000000002_u128,
0x80000000000000000000000000000000_u128) == div_result<uint128>{1, 2});
static_assert(udivrem(0x70000000000000000_u128, 0x20000000000000000_u128) ==
div_result<uint128>{3, 0x10000000000000000_u128});

int index = 0;
for (const auto& t : div_test_cases)
{
Expand All @@ -326,6 +356,10 @@ TEST(int128, div)

TEST(int128, sdivrem)
{
static_assert(
sdivrem(0x33c7c3442a47d644b0d0b4ca50f8bbe1_u128, 0x5d99de82798cc77d06c05e7aa7c1f54_u128) ==
div_result<uint128>{8, 0x4fad402ed8172862d70858cfd17c141_u128});

const auto x = 0x83017fa6deecda0063b1977_u128;
const auto y = 0x1bc83504ea8f7_u128;

Expand Down
22 changes: 22 additions & 0 deletions test/unittests/test_intx_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,30 @@ TYPED_TEST(uint_api, bitwise_op_assignment)
EXPECT_EQ(x >>= x, 0);
}

TYPED_TEST(uint_api, explicit_conversion_to_smaller_uint)
{
if constexpr (TypeParam::num_bits > 128)
{
using SmallerType = intx::uint<TypeParam::num_bits - 64>;
static_assert(static_cast<SmallerType>(TypeParam{1}) == 1);

TypeParam x;
for (size_t i = 0; i < TypeParam::num_words; ++i)
x[i] = i + 1;

const auto smaller = static_cast<SmallerType>(x);
for (size_t i = 0; i < SmallerType::num_words; ++i)
{
EXPECT_EQ(smaller[i], i + 1);
}
}
}

TYPED_TEST(uint_api, explicit_conversion_to_integral_type)
{
static_assert(
static_cast<uint32_t>(TypeParam{0x0102030405060708, 0xffffffffffffffff}) == 0x05060708);

TypeParam x;
x[0] = 3;
x[1] = 0xffffffffffffffff;
Expand Down