30hi_warning_ignore_msvc(26472)
41template<std::
unsigned_
integral T>
42[[nodiscard]] hi_force_inline
constexpr T get_bit(T
const *lhs,
std::size_t index)
noexcept
44 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
46 hilet digit_count = index / bits_per_digit;
47 hilet bit_count = index % bits_per_digit;
49 return (lhs[digit_count] >> bit_count) & 1;
59template<std::
unsigned_
integral T>
60constexpr void set_bit(T *r,
std::size_t index, T value = T{1})
noexcept
64 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
66 hilet digit_count = index / bits_per_digit;
67 hilet bit_count = index % bits_per_digit;
70 hilet mask = ~(T{1} << bit_count);
71 r[digit_count] = (r[digit_count] & mask) | value;
80template<std::
unsigned_
integral T>
83 constexpr auto num_bits =
sizeof(T) * CHAR_BIT;
84 hilet reverse_count = num_bits - rhs;
86 return {(lhs << rhs) | carry, lhs >> reverse_count};
95template<std::
unsigned_
integral T>
98 constexpr auto num_bits =
sizeof(T) * CHAR_BIT;
99 hilet reverse_count = num_bits - rhs;
101 return {(lhs >> rhs) | carry, lhs << reverse_count};
109template<std::
unsigned_
integral T>
112 using S = std::make_signed_t<T>;
114 constexpr auto num_bits =
sizeof(T) * CHAR_BIT;
115 hilet reverse_count = num_bits - rhs;
117 return {(
static_cast<S
>(lhs) >> rhs), lhs << reverse_count};
126template<std::
unsigned_
integral T>
127hi_force_inline
constexpr std::pair<T, T> add_carry(T lhs, T rhs, T carry = T{0})
noexcept
129 hi_axiom(carry <= 1);
131 constexpr std::size_t num_bits =
sizeof(T) * CHAR_BIT;
133 if constexpr (has_native_uintxx_v<num_bits * 2>) {
135 using U = make_uintxx_t<num_bits * 2>;
137 hilet r =
static_cast<U
>(lhs) +
static_cast<U
>(rhs) +
static_cast<U
>(carry);
138 return {
static_cast<T
>(r),
static_cast<T
>(r >> num_bits)};
140 }
else if (not std::is_constant_evaluated()) {
141#if HI_COMPILER == HI_CC_MSVC
143 hilet c = _addcarry_u64(
static_cast<unsigned char>(carry), lhs, rhs, &r);
144 return {r,
static_cast<T
>(c)};
149 hilet r =
static_cast<T
>(lhs + rhs + carry);
150 hilet c =
static_cast<T
>(r < lhs or r < rhs);
165template<std::
unsigned_
integral T>
166hi_force_inline
constexpr std::pair<T, T> mul_carry(T lhs, T rhs, T carry = T{0}, T accumulator = T{0})
noexcept
168 constexpr std::size_t num_bits =
sizeof(T) * CHAR_BIT;
170 if constexpr (has_native_uintxx_v<num_bits * 2>) {
172 using U = make_uintxx_t<num_bits * 2>;
174 hilet r =
static_cast<U
>(lhs) *
static_cast<U
>(rhs) +
static_cast<U
>(carry) +
static_cast<U
>(accumulator);
175 return {
static_cast<T
>(r),
static_cast<T
>(r >> num_bits)};
177 }
else if (not std::is_constant_evaluated()) {
178#if HI_COMPILER == HI_CC_MSVC
179 if constexpr (
sizeof(T) == 8) {
181 uint64_t lo = _umul128(lhs, rhs, &
hi);
183 std::tie(lo, c) = add_carry(lo, carry, uint64_t{0});
185 std::tie(lo, c) = add_carry(lo, accumulator, uint64_t{0});
192 constexpr std::size_t num_half_bits = num_bits / 2;
193 constexpr T half_mask = (T{1} << num_half_bits) - T{1};
195 hilet A = lhs >> num_half_bits;
196 hilet B = lhs & half_mask;
197 hilet C = rhs >> num_half_bits;
198 hilet D = rhs & half_mask;
210 hilet AD_lo = AD << num_half_bits;
211 hilet AD_hi = AD >> num_half_bits;
212 hilet BC_lo = BC << num_half_bits;
213 hilet BC_hi = BC >> num_half_bits;
215 std::tie(lo, c) = add_carry(lo, AD_lo, T{0});
217 std::tie(lo, c) = add_carry(lo, BC_lo, T{0});
221 std::tie(lo, c) = add_carry(lo, carry, T{0});
223 std::tie(lo, c) = add_carry(lo, accumulator, T{0});
237template<std::
unsigned_
integral T>
238hi_force_inline
constexpr T wide_div(T lhs_lo, T lhs_hi, T rhs)
noexcept
240 if constexpr (
sizeof(T) == 1) {
241 hilet lhs =
static_cast<uint16_t
>(lhs_hi) << 8 |
static_cast<uint16_t
>(lhs_lo);
242 return narrow_cast<uint8_t>(lhs / rhs);
244 }
else if constexpr (
sizeof(T) == 2) {
245 hilet lhs =
static_cast<uint32_t
>(lhs_hi) << 16 |
static_cast<uint32_t
>(lhs_lo);
246 return narrow_cast<uint16_t>(lhs / rhs);
248 }
else if constexpr (
sizeof(T) == 4) {
249 hilet lhs =
static_cast<uint64_t
>(lhs_hi) << 32 |
static_cast<uint64_t
>(lhs_lo);
250 return narrow_cast<uint32_t>(lhs / rhs);
252 }
else if constexpr (
sizeof(T) == 8) {
253#if HI_COMPILER == HI_CC_MSVC
255 return _udiv128(lhs_hi, lhs_lo, rhs, &remainder);
257#elif HI_COMPILER == HI_CC_CLANG || HI_COMPILER == HI_CC_GCC
258 hilet lhs =
static_cast<__uint128_t
>(lhs_hi) << 64 |
static_cast<__uint128_t
>(lhs_lo);
259 return narrow_cast<uint64_t>(lhs / rhs);
261#error "Not implemented"
272template<std::
unsigned_
integral T>
273[[nodiscard]] hi_force_inline
constexpr ssize_t bsr_carry_chain(T
const *lhs,
std::size_t n)
noexcept
275 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
277 for (ssize_t i =
static_cast<ssize_t
>(n) - 1; i >= 0; i--) {
278 auto tmp = std::countl_zero(lhs[i]);
279 if (tmp < bits_per_digit) {
280 return i * bits_per_digit + bits_per_digit - tmp - 1;
293template<std::
unsigned_
integral T>
294hi_force_inline
constexpr void invert_carry_chain(T *r, T
const *rhs,
std::size_t n)
noexcept
308template<std::
unsigned_
integral T>
309hi_force_inline
constexpr void sll_carry_chain(T *r, T
const *lhs,
std::size_t rhs,
std::size_t n)
noexcept
311 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
313 hilet digit_count =
static_cast<ssize_t
>(rhs / bits_per_digit);
314 hilet bit_count = rhs % bits_per_digit;
316 if (r != lhs or digit_count > 0) {
318 for (i =
static_cast<ssize_t
>(n) - 1; i >= digit_count; --i) {
319 r[i] = lhs[i - digit_count];
321 for (; i >= 0; --i) {
329 std::tie(r[i], carry) = sll_carry(r[i], bit_count, carry);
341template<std::
unsigned_
integral T>
342hi_force_inline
constexpr void srl_carry_chain(T *r, T
const *lhs,
std::size_t rhs,
std::size_t n)
noexcept
344 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
346 hilet digit_count = rhs / bits_per_digit;
347 hilet bit_count = rhs % bits_per_digit;
349 if (r != lhs or digit_count > 0) {
351 for (; i != (n - digit_count); ++i) {
352 r[i] = lhs[i + digit_count];
354 for (; i != n; ++i) {
362 for (ssize_t i =
static_cast<ssize_t
>(n) - digit_count - 1; i >= 0; --i) {
363 std::tie(r[i], carry) = srl_carry(r[i], bit_count, carry);
376template<std::
unsigned_
integral T>
377hi_force_inline
constexpr void sra_carry_chain(T *r, T
const *lhs,
std::size_t rhs,
std::size_t n)
noexcept
379 using S = std::make_signed_t<T>;
380 constexpr std::size_t bits_per_digit =
sizeof(T) * CHAR_BIT;
382 hilet digit_count = rhs / bits_per_digit;
383 hilet bit_count = rhs % bits_per_digit;
385 if (r != lhs or digit_count > 0) {
386 hi_axiom(digit_count < n);
389 for (; i != (n - digit_count); ++i) {
390 r[i] = lhs[i + digit_count];
394 hilet sign = lhs[n - 1] < 0 ? S{-1} : S{0};
395 for (; i != n; ++i) {
405 ssize_t i =
static_cast<ssize_t
>(n) - digit_count - 1;
406 std::tie(r[i], carry) = sra_carry(r[i], bit_count);
410 for (; i >= 0; --i) {
411 std::tie(r[i], carry) = srl_carry(r[i], bit_count, carry);
423template<std::
unsigned_
integral T>
424hi_force_inline
constexpr void and_carry_chain(T *r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
427 r[i] = lhs[i] & rhs[i];
438template<std::
unsigned_
integral T>
439hi_force_inline
constexpr void or_carry_chain(T *r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
442 r[i] = lhs[i] | rhs[i];
453template<std::
unsigned_
integral T>
454hi_force_inline
constexpr void xor_carry_chain(T *r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
457 r[i] = lhs[i] ^ rhs[i];
461template<std::
unsigned_
integral T>
462[[nodiscard]] hi_force_inline
constexpr bool eq_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
465 if (lhs[i] != rhs[i]) {
472template<std::
unsigned_
integral T>
473[[nodiscard]] hi_force_inline
constexpr bool ne_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
475 return not eq_carry_chain(lhs, rhs, n);
478template<std::
unsigned_
integral T>
479[[nodiscard]] hi_force_inline
constexpr std::strong_ordering
480cmp_unsigned_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
482 for (ssize_t i =
static_cast<ssize_t
>(n) - 1; i >= 0; --i) {
483 hilet r = lhs[i] <=> rhs[i];
484 if (r != std::strong_ordering::equal) {
488 return std::strong_ordering::equal;
491template<std::
unsigned_
integral T>
492[[nodiscard]] hi_force_inline
constexpr std::strong_ordering cmp_signed_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
494 using S = std::make_signed_t<T>;
498 hilet r =
static_cast<S
>(lhs[n - 1]) <=>
static_cast<S
>(rhs[n - 1]);
499 if (r != std::strong_ordering::equal) {
506 for (ssize_t i =
static_cast<ssize_t
>(n) - 2; i >= 0; --i) {
507 hilet r = lhs[i] <=> rhs[i];
508 if (r != std::strong_ordering::equal) {
512 return std::strong_ordering::equal;
515template<std::
unsigned_
integral T>
516[[nodiscard]] hi_force_inline
constexpr bool lt_unsigned_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
518 return cmp_unsigned_carry_chain(lhs, rhs, n) == std::strong_ordering::less;
521template<std::
unsigned_
integral T>
522[[nodiscard]] hi_force_inline
constexpr bool gt_unsigned_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
524 return cmp_unsigned_carry_chain(lhs, rhs, n) == std::strong_ordering::greater;
527template<std::
unsigned_
integral T>
528[[nodiscard]] hi_force_inline
constexpr bool ge_unsigned_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
530 return lt_unsigned_carry_chain(rhs, lhs, n);
533template<std::
unsigned_
integral T>
534[[nodiscard]] hi_force_inline
constexpr bool le_unsigned_carry_chain(T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
536 return gt_unsigned_carry_chain(rhs, lhs, n);
546template<std::
unsigned_
integral T>
547hi_force_inline
constexpr void neg_carry_chain(T *r, T
const *rhs,
std::size_t n)
noexcept
551 std::tie(r[i], carry) = add_carry(~rhs[i], T{0}, carry);
562template<std::
unsigned_
integral T>
563hi_force_inline
constexpr void add_carry_chain(T *r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
567 std::tie(r[i], carry) = add_carry(lhs[i], rhs[i], carry);
578template<std::
unsigned_
integral T>
579hi_force_inline
constexpr void sub_carry_chain(T *r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
583 std::tie(r[i], carry) = add_carry(lhs[i], ~rhs[i], carry);
595template<std::
unsigned_
integral T>
596hi_force_inline
constexpr void mul_carry_chain(T *hi_restrict r, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
598 hi_axiom(r != lhs and r != rhs);
600 for (
auto rhs_index = 0; rhs_index < n; rhs_index++) {
601 hilet rhs_digit = rhs[rhs_index];
604 for (
auto lhs_index = 0; (lhs_index + rhs_index) < n; lhs_index++) {
605 hilet lhs_digit = lhs[lhs_index];
608 T accumulator = r[rhs_index + lhs_index];
609 std::tie(result, carry) = mul_carry(lhs_digit, rhs_digit, carry, accumulator);
610 r[rhs_index + lhs_index] = result;
625template<std::
unsigned_
integral T>
626constexpr void div_carry_chain(T *hi_restrict quotient, T *hi_restrict remainder, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
628 hi_axiom(quotient != lhs and quotient != rhs and quotient != remainder);
629 hi_axiom(remainder != lhs and remainder != rhs);
631 hilet nr_bits =
static_cast<ssize_t
>(n *
sizeof(T) * CHAR_BIT);
633 for (ssize_t i = nr_bits - 1; i >= 0; i--) {
634 sll_carry_chain(remainder, remainder, 1, n);
635 remainder[0] |= get_bit(lhs, i);
636 if (ge_unsigned_carry_chain(remainder, rhs, n)) {
637 sub_carry_chain(remainder, remainder, rhs, n);
638 set_bit(quotient, i);
654template<std::
unsigned_
integral T>
656signed_div_carry_chain(T *hi_restrict quotient, T *hi_restrict remainder, T
const *lhs, T
const *rhs,
std::size_t n)
noexcept
659 hi_axiom(quotient != lhs and quotient != rhs and quotient != remainder);
660 hi_axiom(remainder != lhs and remainder != rhs);
662 using signed_type = std::make_signed_t<T>;
664 hilet lhs_is_negative =
static_cast<signed_type
>(lhs[n - 1]) < 0;
665 hilet rhs_is_negative =
static_cast<signed_type
>(rhs[n - 1]) < 0;
668 if (lhs_is_negative or rhs_is_negative) {
672 if (lhs_is_negative) {
675 neg_carry_chain(p, lhs, n);
679 if (rhs_is_negative) {
681 T *p = tmp.data() * n;
682 neg_carry_chain(p, rhs, n);
687 div_carry_chain(quotient, remainder, lhs, rhs, n);
689 if (lhs_is_negative != rhs_is_negative) {
691 neg_carry_chain(quotient, quotient, n);
693 if (lhs_is_negative) {
695 neg_carry_chain(remainder, remainder, n);