From 431eeb8441869d5353d42f6a6ff99defe222f554 Mon Sep 17 00:00:00 2001 From: Ian Pike Date: Sat, 6 Apr 2024 18:21:31 -0400 Subject: [PATCH] Remove int128 experiment as it is currently cursed with msvc --- ccmath_headers.cmake | 6 +- .../ccmath/internal/predef/has_builtin.hpp | 17 + include/ccmath/internal/support/bits.hpp | 19 +- include/ccmath/internal/support/ctz.hpp | 10 +- .../internal/support/floating_point_bits.hpp | 641 -------- .../ccmath/internal/support/math_support.hpp | 33 - include/ccmath/internal/types/uint.hpp | 1312 ----------------- include/ccmath/internal/types/uint128.hpp | 17 - 8 files changed, 35 insertions(+), 2020 deletions(-) create mode 100644 include/ccmath/internal/predef/has_builtin.hpp delete mode 100644 include/ccmath/internal/support/floating_point_bits.hpp delete mode 100644 include/ccmath/internal/support/math_support.hpp delete mode 100644 include/ccmath/internal/types/uint.hpp delete mode 100644 include/ccmath/internal/types/uint128.hpp diff --git a/ccmath_headers.cmake b/ccmath_headers.cmake index 8ba9acb..efd866a 100644 --- a/ccmath_headers.cmake +++ b/ccmath_headers.cmake @@ -20,6 +20,7 @@ set(ccmath_internal_predef_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/unlikely.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/no_debug.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/suppress.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/predef/has_builtin.hpp ) set(ccmath_internal_support_headers @@ -31,15 +32,12 @@ set(ccmath_internal_support_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/unreachable.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/ctz.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/type_identity.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/floating_point_bits.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/math_support.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/support/always_false.hpp ) set(ccmath_internal_types_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/fp_types.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/uint128.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/uint.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/number_pair.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/sign.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/internal/types/float128.hpp ) diff --git a/include/ccmath/internal/predef/has_builtin.hpp b/include/ccmath/internal/predef/has_builtin.hpp new file mode 100644 index 0000000..f1dae76 --- /dev/null +++ b/include/ccmath/internal/predef/has_builtin.hpp @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2024-Present Ian Pike +* Copyright (c) 2024-Present ccmath contributors +* +* This library is provided under the MIT License. +* See LICENSE for more information. +*/ + +#pragma once + +#ifndef CCM_HAS_BUILTIN +# if defined(__has_builtin) +# define CCM_HAS_BUILTIN(x) __has_builtin(x) +# else +# define CCM_HAS_BUILTIN(x) 0 +# endif +#endif // CCM_HAS_BUILTIN diff --git a/include/ccmath/internal/support/bits.hpp b/include/ccmath/internal/support/bits.hpp index 50da9c4..fc93c08 100644 --- a/include/ccmath/internal/support/bits.hpp +++ b/include/ccmath/internal/support/bits.hpp @@ -11,6 +11,7 @@ #pragma once #include "ccmath/internal/support/ctz.hpp" +#include "ccmath/internal/predef/has_builtin.hpp" #include #include @@ -219,13 +220,13 @@ namespace ccm::support return std::numeric_limits::digits - countl_zero(value); } -#if __has_builtin(__builtin_popcountg) +#if CCM_HAS_BUILTIN(__builtin_popcountg) template [[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> popcount(T value) { return __builtin_popcountg(value); } -#else // !__has_builtin(__builtin_popcountg) +#else // !CCM_HAS_BUILTIN(__builtin_popcountg) template [[nodiscard]] constexpr std::enable_if_t, int> popcount(T value) { @@ -236,10 +237,10 @@ namespace ccm::support } return count; } -#endif // __has_builtin(__builtin_popcountg) +#endif // CCM_HAS_BUILTIN(__builtin_popcountg) // If the compiler has builtin's for popcount, the create specializations that use the builtin. -#if __has_builtin(__builtin_popcount) +#if CCM_HAS_BUILTIN(__builtin_popcount) template <> [[nodiscard]] constexpr int popcount(unsigned char value) { @@ -257,22 +258,22 @@ namespace ccm::support { return __builtin_popcount(value); } -#endif // __has_builtin(__builtin_popcount) +#endif // CCM_HAS_BUILTIN(__builtin_popcount) -#if __has_builtin(__builtin_popcountl) +#if CCM_HAS_BUILTIN(__builtin_popcountl) template <> [[nodiscard]] constexpr int popcount(unsigned long value) { return __builtin_popcountl(value); } -#endif // __has_builtin(__builtin_popcountl) +#endif // CCM_HAS_BUILTIN(__builtin_popcountl) -#if __has_builtin(__builtin_popcountll) +#if CCM_HAS_BUILTIN(__builtin_popcountll) template <> [[nodiscard]] constexpr int popcount(unsigned long long value) { return __builtin_popcountll(value); } -#endif // __has_builtin(__builtin_popcountll) +#endif // CCM_HAS_BUILTIN(__builtin_popcountll) } // namespace ccm::support diff --git a/include/ccmath/internal/support/ctz.hpp b/include/ccmath/internal/support/ctz.hpp index ccfdbd3..7941cbb 100644 --- a/include/ccmath/internal/support/ctz.hpp +++ b/include/ccmath/internal/support/ctz.hpp @@ -8,6 +8,8 @@ #pragma once +#include "ccmath/internal/predef/has_builtin.hpp" + #include #include @@ -47,7 +49,7 @@ namespace ccm::support template <> constexpr int ctz(unsigned short x) noexcept { -#if __has_builtin(__builtin_ctzs) +#if CCM_HAS_BUILTIN(__builtin_ctzs) return __builtin_ctzs(x); #else return internal::generic_ctz(x); @@ -57,7 +59,7 @@ namespace ccm::support template <> constexpr int ctz(unsigned int x) noexcept { -#if __has_builtin(__builtin_ctz) +#if CCM_HAS_BUILTIN(__builtin_ctz) return __builtin_ctz(x); #else return internal::generic_ctz(x); @@ -67,7 +69,7 @@ namespace ccm::support template <> constexpr int ctz(unsigned long x) noexcept { -#if __has_builtin(__builtin_ctzl) +#if CCM_HAS_BUILTIN(__builtin_ctzl) return __builtin_ctzl(x); #else return internal::generic_ctz(x); @@ -77,7 +79,7 @@ namespace ccm::support template <> constexpr int ctz(unsigned long long x) noexcept { -#if __has_builtin(__builtin_ctzll) +#if CCM_HAS_BUILTIN(__builtin_ctzll) return __builtin_ctzll(x); #else return internal::generic_ctz(x); diff --git a/include/ccmath/internal/support/floating_point_bits.hpp b/include/ccmath/internal/support/floating_point_bits.hpp deleted file mode 100644 index 159926d..0000000 --- a/include/ccmath/internal/support/floating_point_bits.hpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -// Large chunks of this code is inspired by or uses code from LLVM -// https://github.com/llvm/llvm-project/ - -#pragma once - -#include "ccmath/internal/predef/likely.hpp" -#include "ccmath/internal/support/always_false.hpp" -#include "ccmath/internal/support/bits.hpp" -#include "ccmath/internal/support/math_support.hpp" -#include "ccmath/internal/types/float128.hpp" -#include "ccmath/internal/types/sign.hpp" -#include "ccmath/internal/types/uint128.hpp" -#include - -namespace ccm::support -{ - // The supported floating point types. - enum class FPType : uint8_t - { - eBinary32, - eBinary64, - eBinary128, - eBinary80_x86, - }; - - namespace internal - { - - template - struct FPLayout - { - }; - - template <> - struct FPLayout - { - using StorageType = uint32_t; - static constexpr int SIGN_LEN = 1; - static constexpr int EXP_LEN = 8; - static constexpr int SIG_LEN = 23; - static constexpr int FRACTION_LEN = SIG_LEN; - }; - - template <> - struct FPLayout - { - using StorageType = uint64_t; - inline static constexpr int SIGN_LEN = 1; - inline static constexpr int EXP_LEN = 11; - inline static constexpr int SIG_LEN = 52; - inline static constexpr int FRACTION_LEN = SIG_LEN; - }; - - template <> - struct FPLayout - { - using StorageType = ccm::uint128_t; - inline static constexpr int SIGN_LEN = 1; - inline static constexpr int EXP_LEN = 15; - inline static constexpr int SIG_LEN = 112; - inline static constexpr int FRACTION_LEN = SIG_LEN; - }; - - template <> - struct FPLayout - { - using StorageType = ccm::uint128_t; - inline static constexpr int SIGN_LEN = 1; - inline static constexpr int EXP_LEN = 15; - inline static constexpr int SIG_LEN = 64; - inline static constexpr int FRACTION_LEN = SIG_LEN - 1; - }; - - // FPStorage derives useful constants from the FPLayout above. - template - struct FPStorage : public FPLayout - { - using UP = FPLayout; - - using UP::EXP_LEN; // The number of bits for the *exponent* part - using UP::SIG_LEN; // The number of bits for the *significand* part - using UP::SIGN_LEN; // The number of bits for the *sign* part - // For convenience, the sum of `SIG_LEN`, `EXP_LEN`, and `SIGN_LEN`. - inline static constexpr int TOTAL_LEN = SIGN_LEN + EXP_LEN + SIG_LEN; - - // The number of bits after the decimal dot when the number is in normal form. - using UP::FRACTION_LEN; - - // An unsigned integer that is wide enough to contain all of the floating - // point bits. - using StorageType = typename UP::StorageType; - - // The number of bits in StorageType. - inline static constexpr int STORAGE_LEN = sizeof(StorageType) * CHAR_BIT; - static_assert(STORAGE_LEN >= TOTAL_LEN); - - // The exponent bias. Always positive. - inline static constexpr int32_t EXP_BIAS = (1U << (EXP_LEN - 1U)) - 1U; - static_assert(EXP_BIAS > 0); - - // The bit pattern that keeps only the *significand* part. - inline static constexpr StorageType SIG_MASK = mask_trailing_ones(); - // The bit pattern that keeps only the *exponent* part. - inline static constexpr StorageType EXP_MASK = mask_trailing_ones() << SIG_LEN; - // The bit pattern that keeps only the *sign* part. - inline static constexpr StorageType SIGN_MASK = mask_trailing_ones() << (EXP_LEN + SIG_LEN); - // The bit pattern that keeps only the *exponent + significand* part. - inline static constexpr StorageType EXP_SIG_MASK = mask_trailing_ones(); - // The bit pattern that keeps only the *sign + exponent + significand* part. - inline static constexpr StorageType FP_MASK = mask_trailing_ones(); - // The bit pattern that keeps only the *fraction* part. - // i.e., the *significand* without the leading one. - inline static constexpr StorageType FRACTION_MASK = mask_trailing_ones(); - - static_assert((SIG_MASK & EXP_MASK & SIGN_MASK) == 0, "masks disjoint"); - static_assert((SIG_MASK | EXP_MASK | SIGN_MASK) == FP_MASK, "masks cover"); - - protected: - // Merge bits from 'a' and 'b' values according to 'mask'. - // Use 'a' bits when corresponding 'mask' bits are zeroes and 'b' bits when - // corresponding bits are ones. - inline static constexpr StorageType merge(StorageType a, StorageType b, StorageType mask) - { - // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge - return a ^ ((a ^ b) & mask); - } - - // A stongly typed integer that prevents mixing and matching integers with - // different semantics. - template - struct TypedInt // NOLINT(cppcoreguidelines-special-member-functions) - { - using value_type = T; - inline constexpr explicit TypedInt(T value) : value(value) {} - inline constexpr TypedInt(const TypedInt & value) = default; - inline constexpr TypedInt & operator=(const TypedInt & value) = default; - - inline constexpr explicit operator T() const { return value; } - - [[nodiscard]] inline constexpr StorageType to_storage_type() const { return StorageType(value); } - - inline friend constexpr bool operator==(TypedInt a, TypedInt b) { return a.value == b.value; } - inline friend constexpr bool operator!=(TypedInt a, TypedInt b) { return a.value != b.value; } - - protected: - T value; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - }; - - // An opaque type to store a floating point exponent. - // We define special values but it is valid to create arbitrary values as long - // as they are in the range [min, max]. - struct Exponent : public TypedInt - { - using UP = TypedInt; - using UP::UP; - inline static constexpr auto subnormal() { return Exponent(-EXP_BIAS); } - inline static constexpr auto min() { return Exponent(1 - EXP_BIAS); } - inline static constexpr auto zero() { return Exponent(0); } - inline static constexpr auto max() { return Exponent(EXP_BIAS); } - inline static constexpr auto inf() { return Exponent(EXP_BIAS + 1); } - }; - - // An opaque type to store a floating point biased exponent. - // We define special values but it is valid to create arbitrary values as long - // as they are in the range [zero, bits_all_ones]. - // Values greater than bits_all_ones are truncated. - struct BiasedExponent : public TypedInt - { - using UP = TypedInt; - using UP::UP; - - inline constexpr BiasedExponent(Exponent exp) : UP(static_cast(exp) + EXP_BIAS) {} - - // Cast operator to get convert from BiasedExponent to Exponent. - inline constexpr operator Exponent() const { return Exponent(UP::value - EXP_BIAS); } - - inline constexpr BiasedExponent & operator++() - { - assert(*this != BiasedExponent(Exponent::inf())); - ++UP::value; - return *this; - } - - inline constexpr BiasedExponent & operator--() - { - assert(*this != BiasedExponent(Exponent::subnormal())); - --UP::value; - return *this; - } - }; - - // An opaque type to store a floating point significand. - // We define special values but it is valid to create arbitrary values as long - // as they are in the range [zero, bits_all_ones]. - // Note that the semantics of the Significand are implementation dependent. - // Values greater than bits_all_ones are truncated. - struct Significand : public TypedInt - { - using UP = TypedInt; - using UP::UP; - - inline friend constexpr Significand operator|(const Significand a, const Significand b) - { - return Significand((a.to_storage_type() | b.to_storage_type())); - } - inline friend constexpr Significand operator^(const Significand a, const Significand b) - { - return Significand((a.to_storage_type() ^ b.to_storage_type())); - } - inline friend constexpr Significand operator>>(const Significand a, int shift) { return Significand((a.to_storage_type() >> shift)); } - - inline static constexpr auto zero() { return Significand(static_cast(0)); } - inline static constexpr auto lsb() { return Significand(static_cast(1)); } - inline static constexpr auto msb() { return Significand(static_cast(1) << (SIG_LEN - 1)); } - inline static constexpr auto bits_all_ones() { return Significand(SIG_MASK); } - }; - - inline static constexpr StorageType encode(BiasedExponent exp) { return (exp.to_storage_type() << SIG_LEN) & EXP_MASK; } - - inline static constexpr StorageType encode(Significand value) { return value.to_storage_type() & SIG_MASK; } - - inline static constexpr StorageType encode(BiasedExponent exp, Significand sig) { return encode(exp) | encode(sig); } - - inline static constexpr StorageType encode(Sign sign, BiasedExponent exp, Significand sig) - { - if (sign.is_neg()) { return SIGN_MASK | encode(exp, sig); } - return encode(exp, sig); - } - - // The floating point number representation as an unsigned integer. - StorageType bits{}; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - - inline constexpr FPStorage() : bits(0) {} - inline constexpr FPStorage(StorageType value) : bits(value) {} - - // Observers - inline constexpr StorageType exp_bits() const { return bits & EXP_MASK; } - inline constexpr StorageType sig_bits() const { return bits & SIG_MASK; } - inline constexpr StorageType exp_sig_bits() const { return bits & EXP_SIG_MASK; } - - // Parts - inline constexpr BiasedExponent biased_exponent() const { return BiasedExponent(static_cast(exp_bits() >> SIG_LEN)); } - inline constexpr void set_biased_exponent(BiasedExponent biased) { bits = merge(bits, encode(biased), EXP_MASK); } - - public: - inline constexpr Sign sign() const { return (bits & SIGN_MASK) ? Sign::negative : Sign::positive; } - inline constexpr void set_sign(Sign signVal) - { - if (sign() != signVal) { bits ^= SIGN_MASK; } - } - }; - - // This layer defines all functions that are specific to how the the floating - // point type is encoded. It enables constructions, modification and observation - // of values manipulated as 'StorageType'. - template - struct FPRepSem : public FPStorage - { - using UP = FPStorage; - using typename UP::StorageType; - using UP::FRACTION_LEN; - using UP::FRACTION_MASK; - - protected: - using typename UP::Exponent; - using typename UP::Significand; - using UP::bits; - using UP::encode; - using UP::exp_bits; - using UP::exp_sig_bits; - using UP::sig_bits; - using UP::UP; - - public: - // Builders - static constexpr RetT zero(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::subnormal(), Significand::zero())); } - static constexpr RetT one(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::zero(), Significand::zero())); } - static constexpr RetT min_subnormal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::subnormal(), Significand::lsb())); } - static constexpr RetT max_subnormal(Sign sign = Sign::positive) - { - return RetT(encode(sign, Exponent::subnormal(), Significand::bits_all_ones())); - } - inline static constexpr RetT min_normal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::min(), Significand::zero())); } - inline static constexpr RetT max_normal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::max(), Significand::bits_all_ones())); } - inline static constexpr RetT inf(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::inf(), Significand::zero())); } - inline static constexpr RetT signaling_nan(Sign sign = Sign::positive, StorageType v = 0) - { - return RetT(encode(sign, Exponent::inf(), (v ? Significand(v) : (Significand::msb() >> 1)))); - } - inline static constexpr RetT quiet_nan(Sign sign = Sign::positive, StorageType v = 0) - { - return RetT(encode(sign, Exponent::inf(), Significand::msb() | Significand(v))); - } - - // Observers - [[nodiscard]] inline constexpr bool is_zero() const { return exp_sig_bits() == 0; } - [[nodiscard]] inline constexpr bool is_nan() const { return exp_sig_bits() > encode(Exponent::inf(), Significand::zero()); } - [[nodiscard]] inline constexpr bool is_quiet_nan() const { return exp_sig_bits() >= encode(Exponent::inf(), Significand::msb()); } - [[nodiscard]] inline constexpr bool is_signaling_nan() const { return is_nan() && !is_quiet_nan(); } - [[nodiscard]] inline constexpr bool is_inf() const { return exp_sig_bits() == encode(Exponent::inf(), Significand::zero()); } - [[nodiscard]] inline constexpr bool is_finite() const { return exp_bits() != encode(Exponent::inf()); } - [[nodiscard]] inline constexpr bool is_subnormal() const { return exp_bits() == encode(Exponent::subnormal()); } - [[nodiscard]] inline constexpr bool is_normal() const { return is_finite() && !is_subnormal(); } - [[nodiscard]] inline constexpr RetT next_toward_inf() const - { - if (is_finite()) { return RetT(bits + StorageType(1)); } - return RetT(bits); - } - - // Returns the mantissa with the implicit bit set iff the current - // value is a valid normal number. - inline constexpr StorageType get_explicit_mantissa() const - { - if (is_subnormal()) { return sig_bits(); } - return (StorageType(1) << UP::SIG_LEN) | sig_bits(); - } - }; - - // Specialization for the X86 Extended Precision type. - template - struct FPRepSem : public FPStorage - { - using UP = FPStorage; - using typename UP::StorageType; - using UP::FRACTION_LEN; - using UP::FRACTION_MASK; - - // The x86 80 bit float represents the leading digit of the mantissa - // explicitly. This is the mask for that bit. - static constexpr StorageType EXPLICIT_BIT_MASK = static_cast(1) << FRACTION_LEN; - // The X80 significand is made of an explicit bit and the fractional part. - static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0, "the explicit bit and the fractional part should not overlap"); - static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK, "the explicit bit and the fractional part should cover the " - "whole significand"); - - protected: - using typename UP::Exponent; - using typename UP::Significand; - using UP::encode; - using UP::UP; - - public: - // Builders - inline static constexpr RetT zero(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::subnormal(), Significand::zero())); } - inline static constexpr RetT one(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::zero(), Significand::msb())); } - inline static constexpr RetT min_subnormal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::subnormal(), Significand::lsb())); } - inline static constexpr RetT max_subnormal(Sign sign = Sign::positive) - { - return RetT(encode(sign, Exponent::subnormal(), Significand::bits_all_ones() ^ Significand::msb())); - } - inline static constexpr RetT min_normal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::min(), Significand::msb())); } - inline static constexpr RetT max_normal(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::max(), Significand::bits_all_ones())); } - inline static constexpr RetT inf(Sign sign = Sign::positive) { return RetT(encode(sign, Exponent::inf(), Significand::msb())); } - inline static constexpr RetT signaling_nan(Sign sign = Sign::positive, StorageType v = 0) - { - return RetT(encode(sign, Exponent::inf(), Significand::msb() | (v ? Significand(v) : (Significand::msb() >> 2)))); - } - inline static constexpr RetT quiet_nan(Sign sign = Sign::positive, StorageType v = 0) - { - return RetT(encode(sign, Exponent::inf(), Significand::msb() | (Significand::msb() >> 1) | Significand(v))); - } - - // Observers - [[nodiscard]] inline constexpr bool is_zero() const { return exp_sig_bits() == 0; } - inline constexpr bool is_nan() const - { - // Most encoding forms from the table found in - // https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format - // are interpreted as NaN. - // More precisely : - // - Pseudo-Infinity - // - Pseudo Not a Number - // - Signalling Not a Number - // - Floating-point Indefinite - // - Quiet Not a Number - // - Unnormal - // This can be reduced to the following logic: - if (exp_bits() == encode(Exponent::inf())) { return !is_inf(); } - if (exp_bits() != encode(Exponent::subnormal())) { return (sig_bits() & encode(Significand::msb())) == 0; } - return false; - } - [[nodiscard]] inline constexpr bool is_quiet_nan() const - { - return exp_sig_bits() >= encode(Exponent::inf(), Significand::msb() | (Significand::msb() >> 1)); - } - [[nodiscard]] inline constexpr bool is_signaling_nan() const { return is_nan() && !is_quiet_nan(); } - [[nodiscard]] inline constexpr bool is_inf() const { return exp_sig_bits() == encode(Exponent::inf(), Significand::msb()); } - [[nodiscard]] inline constexpr bool is_finite() const { return !is_inf() && !is_nan(); } - [[nodiscard]] inline constexpr bool is_subnormal() const { return exp_bits() == encode(Exponent::subnormal()); } - [[nodiscard]] inline constexpr bool is_normal() const - { - const auto exp = exp_bits(); - if (exp == encode(Exponent::subnormal()) || exp == encode(Exponent::inf())) { return false; } - return get_implicit_bit(); - } - inline constexpr RetT next_toward_inf() const - { - if (is_finite()) - { - if (exp_sig_bits() == max_normal().uintval()) { return inf(sign()); } - if (exp_sig_bits() == max_subnormal().uintval()) { return min_normal(sign()); } - if (sig_bits() == SIG_MASK) { return RetT(encode(sign(), ++biased_exponent(), Significand::zero())); } - return RetT(bits + static_cast(1)); - } - return RetT(bits); - } - - [[nodiscard]] inline constexpr StorageType get_explicit_mantissa() const { return sig_bits(); } - - // This functions is specific to FPRepSem. - inline constexpr bool get_implicit_bit() const { return static_cast(bits & EXPLICIT_BIT_MASK); } - - // This functions is specific to FPRepSem. - inline constexpr void set_implicit_bit(bool implicitVal) - { - if (get_implicit_bit() != implicitVal) { bits ^= EXPLICIT_BIT_MASK; } - } - }; - - // 'FPRepImpl' is the bottom of the class hierarchy that only deals with - // 'FPType'. The operations dealing with specific float semantics are - // implemented by 'FPRepSem' above and specialized when needed. - // - // The 'RetT' type is being propagated up to 'FPRepSem' so that the functions - // creating new values (Builders) can return the appropriate type. That is, when - // creating a value through 'FPBits' below the builder will return an 'FPBits' - // value. - // FPBits::zero(); // returns an FPBits<> - // - // When we don't care about specific C++ floating point type we can use - // 'FPRep' and specify the 'FPType' directly. - // FPRep::zero() // returns an FPRep<> - template - struct FPRepImpl : public FPRepSem - { - using UP = FPRepSem; - using StorageType = typename UP::StorageType; - - protected: - using UP::bits; - using UP::encode; - using UP::exp_bits; - using UP::exp_sig_bits; - - using typename UP::BiasedExponent; - using typename UP::Exponent; - using typename UP::Significand; - - using UP::FP_MASK; - - public: - // Constants. - using UP::EXP_BIAS; - using UP::EXP_MASK; - using UP::FRACTION_MASK; - using UP::SIG_LEN; - using UP::SIG_MASK; - using UP::SIGN_MASK; - inline static constexpr int MAX_BIASED_EXPONENT = (1 << UP::EXP_LEN) - 1; - - // CTors - inline constexpr FPRepImpl() = default; - inline constexpr explicit FPRepImpl(StorageType x) : UP(x) {} - - // Comparison - inline constexpr friend bool operator==(FPRepImpl a, FPRepImpl b) { return a.uintval() == b.uintval(); } - inline constexpr friend bool operator!=(FPRepImpl a, FPRepImpl b) { return a.uintval() != b.uintval(); } - - // Representation - inline constexpr StorageType uintval() const { return bits & FP_MASK; } - inline constexpr void set_uintval(StorageType value) { bits = (value & FP_MASK); } - - // Builders - using UP::inf; - using UP::max_normal; - using UP::max_subnormal; - using UP::min_normal; - using UP::min_subnormal; - using UP::one; - using UP::quiet_nan; - using UP::signaling_nan; - using UP::zero; - - // Modifiers - inline constexpr RetT abs() const { return RetT(bits & UP::EXP_SIG_MASK); } - - // Observers - using UP::get_explicit_mantissa; - using UP::is_finite; - using UP::is_inf; - using UP::is_nan; - using UP::is_normal; - using UP::is_quiet_nan; - using UP::is_signaling_nan; - using UP::is_subnormal; - using UP::is_zero; - using UP::next_toward_inf; - using UP::sign; - inline constexpr bool is_inf_or_nan() const { return !is_finite(); } - inline constexpr bool is_neg() const { return sign().is_neg(); } - inline constexpr bool is_pos() const { return sign().is_pos(); } - - inline constexpr uint16_t get_biased_exponent() const { return static_cast(static_cast(UP::biased_exponent())); } - - inline constexpr void set_biased_exponent(StorageType biased) { UP::set_biased_exponent(BiasedExponent((int32_t)biased)); } - - inline constexpr int get_exponent() const { return static_cast(Exponent(UP::biased_exponent())); } - - // If the number is subnormal, the exponent is treated as if it were the - // minimum exponent for a normal number. This is to keep continuity between - // the normal and subnormal ranges, but it causes problems for functions where - // values are calculated from the exponent, since just subtracting the bias - // will give a slightly incorrect result. Additionally, zero has an exponent - // of zero, and that should actually be treated as zero. - inline constexpr int get_explicit_exponent() const - { - Exponent exponent(UP::biased_exponent()); - if (is_zero()) exponent = Exponent::zero(); - if (exponent == Exponent::subnormal()) exponent = Exponent::min(); - return static_cast(exponent); - } - - inline constexpr StorageType get_mantissa() const { return bits & FRACTION_MASK; } - - inline constexpr void set_mantissa(StorageType mantVal) { bits = UP::merge(bits, mantVal, FRACTION_MASK); } - - inline constexpr void set_significand(StorageType sigVal) { bits = UP::merge(bits, sigVal, SIG_MASK); } - // Unsafe function to create a floating point representation. - // It simply packs the sign, biased exponent and mantissa values without - // checking bound nor normalization. - // - // WARNING: For X86 Extended Precision, implicit bit needs to be set correctly - // in the 'mantissa' by the caller. This function will not check for its - // validity. - // - // FIXME: Use an uint32_t for 'biased_exp'. - inline static constexpr RetT create_value(Sign sign, StorageType biased_exp, StorageType mantissa) - { - return RetT(encode(sign, BiasedExponent(static_cast(biased_exp)), Significand(mantissa))); - } - - // The function converts integer number and unbiased exponent to proper - // float T type: - // Result = number * 2^(ep+1 - exponent_bias) - // Be careful! - // 1) "ep" is the raw exponent value. - // 2) The function adds +1 to ep for seamless normalized to denormalized - // transition. - // 3) The function does not check exponent high limit. - // 4) "number" zero value is not processed correctly. - // 5) Number is unsigned, so the result can be only positive. - inline static constexpr RetT make_value(StorageType number, int ep) - { - FPRepImpl result(0); - int lz = UP::FRACTION_LEN + 1 - (UP::STORAGE_LEN - ccm::support::countl_zero(number)); - - number <<= lz; - ep -= lz; - - if (CCM_LIKELY(ep >= 0)) - { - // Implicit number bit will be removed by mask - result.set_significand(number); - result.set_biased_exponent(ep + 1); - } - else { result.set_significand(number >> -ep); } - return RetT(result.uintval()); - } - }; - - // A generic class to manipulate floating point formats. - // It derives its functionality to FPRepImpl above. - template - struct FPRep : public FPRepImpl> - { - using UP = FPRepImpl>; - using StorageType = typename UP::StorageType; - using UP::UP; - - inline constexpr explicit operator StorageType() const { return UP::uintval(); } - }; - - } // namespace internal - - // Returns the FPType corresponding to C++ type T on the host. - template - inline static constexpr FPType get_fp_type() - { - using UnqualT = std::remove_cv_t; - if constexpr (std::is_same_v && __FLT_MANT_DIG__ == 24) { return FPType::eBinary32; } - else if constexpr (std::is_same_v && __DBL_MANT_DIG__ == 53) { return FPType::eBinary64; } - else if constexpr (std::is_same_v) - { - if constexpr (__LDBL_MANT_DIG__ == 53) { return FPType::eBinary64; } - else if constexpr (__LDBL_MANT_DIG__ == 64) { return FPType::eBinary80_x86; } - else if constexpr (__LDBL_MANT_DIG__ == 113) { return FPType::eBinary128; } - } -#if defined(CCM_HAS_FLOAT128) - else if constexpr (std::is_same_v) { return FPType::eBinary128; } -#endif - else { static_assert(ccm::support::always_false, "Unsupported type"); } - } - - // A generic class to manipulate C++ floating point formats. - // It derives its functionality to FPRepImpl above. - template - struct FPBits final : public internal::FPRepImpl(), FPBits> - { - static_assert(std::is_floating_point_v, "FPBits instantiated with invalid type."); - using UP = internal::FPRepImpl(), FPBits>; - using StorageType = typename UP::StorageType; - - // Constructors. - inline constexpr FPBits() = default; - - template - inline constexpr explicit FPBits(XType x) - { - using Unqual = typename std::remove_cv_t; - if constexpr (std::is_same_v) { UP::bits = ccm::support::bit_cast(x); } - else if constexpr (std::is_same_v) { UP::bits = x; } - else - { - // We don't want accidental type promotions/conversions, so we require - // exact type match. - static_assert(ccm::support::always_false); - } - } - - // Floating-point conversions. - inline constexpr T get_val() const { return ccm::support::bit_cast(UP::bits); } - }; -} // namespace ccm::support diff --git a/include/ccmath/internal/support/math_support.hpp b/include/ccmath/internal/support/math_support.hpp deleted file mode 100644 index e9839a1..0000000 --- a/include/ccmath/internal/support/math_support.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include -#include - -namespace ccm::support -{ - // Create a bitmask with the count right-most bits set to 1, and all other bits - // set to 0. Only unsigned types are allowed. - template - constexpr std::enable_if_t, T> - mask_trailing_ones() { - constexpr unsigned T_BITS = CHAR_BIT * sizeof(T); - static_assert(count <= T_BITS && "Invalid bit index"); - return count == 0 ? 0 : (T(-1) >> (T_BITS - count)); - } - - // Create a bitmask with the count left-most bits set to 1, and all other bits - // set to 0. Only unsigned types are allowed. - template - constexpr std::enable_if_t, T> - mask_leading_ones() { - return T(~mask_trailing_ones()); - } -} diff --git a/include/ccmath/internal/types/uint.hpp b/include/ccmath/internal/types/uint.hpp deleted file mode 100644 index c18695d..0000000 --- a/include/ccmath/internal/types/uint.hpp +++ /dev/null @@ -1,1312 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -// Large chunks of this code is inspired by or uses code from LLVM -// https://github.com/llvm/llvm-project/ - -#pragma once - -#include "ccmath/internal/predef/unlikely.hpp" -#include "ccmath/internal/predef/compiler_warnings_and_errors.hpp" // Required to disable -fpermissive -#include "ccmath/internal/support/bits.hpp" -#include "ccmath/internal/support/limits.hpp" -#include "ccmath/internal/support/math_support.hpp" -#include "ccmath/internal/support/type_identity.hpp" - -#include -#include // For std::size_t -#include -#include -#include -#include - -#if defined(UINT64_MAX) - #define CCM_HAS_INT64 -#endif // UINT64_MAX - -#if defined(__SIZEOF_INT128__) - #define CCM_HAS_INT128 -namespace ccm::internal -{ - // This is a workaround to get -Wpedantic to not complain about the use of - // unsigned __int128, a non-iso type, inside of an iso function. - __extension__ using uint128_t = unsigned __int128; -} // namespace ccm::internal -#endif // defined(__SIZEOF_INT128__) - -// Since unsigned __int128 is not a standard type, we need to add a specialization for it. -#ifdef CCM_HAS_INT128 -namespace std -{ - template <> - struct [[maybe_unused]] is_unsigned : std::true_type // NOLINT - { - }; -} // namespace std -#endif - -namespace ccm -{ - namespace multiword - { - // A type trait mapping unsigned integers to their half-width unsigned - // counterparts. - template - struct half_width; - template <> - struct half_width : ccm::type_identity - { - }; - template <> - struct half_width : ccm::type_identity - { - }; -#ifdef CCM_HAS_INT64 - template <> - struct half_width : ccm::type_identity - { - }; -#endif // CCM_HAS_INT64 -#ifdef CCM_HAS_INT128 - template <> - struct half_width<__uint128_t> : ccm::type_identity - { - }; -#endif // CCM_HAS_INT128 - template - using half_width_t = typename half_width::type; - - // An array of two elements that can be used in multiword operations. - template - struct DoubleWide final : std::array - { - using UP = std::array; - using UP::UP; - inline constexpr DoubleWide(T lo, T hi) : UP({lo, hi}) {} - }; - - // Converts an unsigned value into a DoubleWide>. - template - inline constexpr auto split(T value) - { - static_assert(std::is_unsigned_v); - using half_type = half_width_t; - return DoubleWide(static_cast(value), static_cast(value >> ccm::support::numeric_limits::digits)); - } - - // The low part of a DoubleWide value. - template - inline constexpr T lo(const DoubleWide & value) - { - return value[0]; - } - // The high part of a DoubleWide value. - template - inline constexpr T hi(const DoubleWide & value) - { - return value[1]; - } - // The low part of an unsigned value. - template - inline constexpr half_width_t lo(T value) - { - return lo(split(value)); - } - // The high part of an unsigned value. - template - inline constexpr half_width_t hi(T value) - { - return hi(split(value)); - } - - // In-place 'dst op= rhs' with operation with carry propagation. Returns carry. - template - inline constexpr word inplace_binop(Function op_with_carry, std::array & dst, const std::array & rhs) - { - static_assert(N >= M); - word carry_out = 0; - for (size_t i = 0; i < N; ++i) - { - const bool has_rhs_value = i < M; - const word rhs_value = has_rhs_value ? rhs[i] : 0; - const word carry_in = carry_out; - dst[i] = op_with_carry(dst[i], rhs_value, carry_in, carry_out); - // stop early when rhs is over and no carry is to be propagated. - if (!has_rhs_value && carry_out == 0) { break; } - } - return carry_out; - } - - // In-place addition. Returns carry. - template - inline constexpr word add_with_carry(std::array & dst, const std::array & rhs) - { - return inplace_binop(ccm::multiword::add_with_carry, dst, rhs); - } - - // In-place subtraction. Returns borrow. - template - inline constexpr word sub_with_borrow(std::array & dst, const std::array & rhs) - { - return inplace_binop(ccm::multiword::sub_with_borrow, dst, rhs); - } - - // In-place multiply-add. Returns carry. - // i.e., 'dst += b * c' - template - inline constexpr word mul_add_with_carry(std::array & dst, word b, word c) - { - return add_with_carry(dst, mul2(b, c)); - } - - // Returns 'a' times 'b' in a DoubleWide. Cannot overflow by construction. - template - inline constexpr DoubleWide mul2(word a, word b) - { - if constexpr (std::is_same_v) { return split(uint16_t(a) * uint16_t(b)); } - else if constexpr (std::is_same_v) { return split(uint32_t(a) * uint32_t(b)); } -#ifdef CCM_HAS_INT64 - else if constexpr (std::is_same_v) { return split(uint64_t(a) * uint64_t(b)); } -#endif -#ifdef CCM_HAS_INT128 - else if constexpr (std::is_same_v) { return split<__uint128_t>(__uint128_t(a) * __uint128_t(b)); } -#endif - else - { - using half_word = half_width_t; - const auto shiftl = [](word value) -> word { return value << ccm::support::numeric_limits::digits; }; - const auto shiftr = [](word value) -> word { return value >> ccm::support::numeric_limits::digits; }; - // Here we do a one digit multiplication where 'a' and 'b' are of type - // word. We split 'a' and 'b' into half words and perform the classic long - // multiplication with 'a' and 'b' being two-digit numbers. - - // a a_hi a_lo - // x b => x b_hi b_lo - // ---- ----------- - // c result - // We convert 'lo' and 'hi' from 'half_word' to 'word' so multiplication - // doesn't overflow. - const word a_lo = lo(a); - const word b_lo = lo(b); - const word a_hi = hi(a); - const word b_hi = hi(b); - const word step1 = b_lo * a_lo; // no overflow; - const word step2 = b_lo * a_hi; // no overflow; - const word step3 = b_hi * a_lo; // no overflow; - const word step4 = b_hi * a_hi; // no overflow; - word lo_digit = step1; - word hi_digit = step4; - const word no_carry = 0; - word carry; - word _; // unused carry variable. - lo_digit = add_with_carry(lo_digit, shiftl(step2), no_carry, carry); - hi_digit = add_with_carry(hi_digit, shiftr(step2), carry, _); - lo_digit = add_with_carry(lo_digit, shiftl(step3), no_carry, carry); - hi_digit = add_with_carry(hi_digit, shiftr(step3), carry, _); - return DoubleWide(lo_digit, hi_digit); - } - } - - // An array of two elements serving as an accumulator during multiword - // computations. - template - struct Accumulator final : std::array - { - using UP = std::array; - inline constexpr Accumulator() : UP({0, 0}) {} - inline constexpr T advance(T carry_in) - { - auto result = UP::front(); - UP::front() = UP::back(); - UP::back() = carry_in; - return result; - } - inline constexpr T sum() const { return UP::front(); } - inline constexpr T carry() const { return UP::back(); } - }; - - // In-place multiplication by a single word. Returns carry. - template - inline constexpr word scalar_multiply_with_carry(std::array & dst, word x) - { - Accumulator acc; - for (auto & val : dst) - { - const word carry = mul_add_with_carry(acc, val, x); - val = acc.advance(carry); - } - return acc.carry(); - } - - // Multiplication of 'lhs' by 'rhs' into 'dst'. Returns carry. - // This function is safe to use for signed numbers. - // https://stackoverflow.com/a/20793834 - // https://pages.cs.wisc.edu/%7Emarkhill/cs354/Fall2008/beyond354/int.mult.html - template - inline constexpr word multiply_with_carry(std::array & dst, const std::array & lhs, const std::array & rhs) - { - static_assert(O >= M + N); - Accumulator acc; - for (size_t i = 0; i < O; ++i) - { - const size_t lower_idx = i < N ? 0 : i - N + 1; - const size_t upper_idx = i < M ? i : M - 1; - word carry = 0; - for (size_t j = lower_idx; j <= upper_idx; ++j) { carry += mul_add_with_carry(acc, lhs[j], rhs[i - j]); } - dst[i] = acc.advance(carry); - } - return acc.carry(); - } - - template - inline constexpr void quick_mul_hi(std::array & dst, const std::array & lhs, const std::array & rhs) - { - Accumulator acc; - word carry = 0; - // First round of accumulation for those at N - 1 in the full product. - for (size_t i = 0; i < N; ++i) { carry += mul_add_with_carry(acc, lhs[i], rhs[N - 1 - i]); } - for (size_t i = N; i < 2 * N - 1; ++i) - { - acc.advance(carry); - carry = 0; - for (size_t j = i - N + 1; j < N; ++j) { carry += mul_add_with_carry(acc, lhs[j], rhs[i - j]); } - dst[i - N] = acc.sum(); - } - dst.back() = acc.carry(); - } - - template - inline constexpr bool is_negative(std::array & array) - { - using signed_word = std::make_signed_t; - return ccm::support::bit_cast(array.back()) < 0; - } - - // An enum for the shift function below. - enum Direction : std::uint8_t - { - LEFT, - RIGHT - }; - - // A bitwise shift on an array of elements. - // TODO: Make the result UB when 'offset' is greater or equal to the number of - // bits in 'array'. This will allow for better code performance. - template - inline constexpr std::array shift(std::array array, size_t offset) - { - static_assert(direction == LEFT || direction == RIGHT); - constexpr size_t WORD_BITS = ccm::support::numeric_limits::digits; - constexpr size_t TOTAL_BITS = N * WORD_BITS; - if (CCM_UNLIKELY(offset == 0)) { return array; } - if (CCM_UNLIKELY(offset >= TOTAL_BITS)) { return {}; } -#ifdef CCM_HAS_INT128 - if constexpr (TOTAL_BITS == 128) - { - using type = std::conditional_t; - auto tmp = ccm::support::bit_cast(array); - if constexpr (direction == LEFT) { tmp <<= offset; } - else { tmp >>= offset; } - return ccm::support::bit_cast>(tmp); - } -#endif - const bool is_neg = is_signed && is_negative(array); - constexpr auto at = [](size_t index) -> int - { - // reverse iteration when direction == LEFT. - if constexpr (direction == LEFT) { return static_cast(N) - static_cast(index) - 1; } - return static_cast(index); - }; - const auto safe_get_at = [&](size_t index) -> word - { - // return appropriate value when accessing out of bound elements. - const int i = at(index); - if (i < 0) { return 0; } - if (i >= static_cast(N)) { return is_neg ? -1 : 0; } - return array[i]; - }; - const size_t index_offset = offset / WORD_BITS; - const size_t bit_offset = offset % WORD_BITS; -#ifdef __clang__ - __builtin_assume(index_offset < N); -#endif - std::array out = {}; - for (size_t index = 0; index < N; ++index) - { - const word part1 = safe_get_at(index + index_offset); - const word part2 = safe_get_at(index + index_offset + 1); - word & dst = out[at(index)]; - if (bit_offset == 0) - { - dst = part1; // no crosstalk between parts. - } - else if constexpr (direction == LEFT) { dst = (part1 << bit_offset) | (part2 >> (WORD_BITS - bit_offset)); } - else { dst = (part1 >> bit_offset) | (part2 << (WORD_BITS - bit_offset)); } - } - return out; - } - -#define DECLARE_COUNTBIT(NAME, INDEX_EXPR) \ - template \ - inline constexpr int NAME(const std::array & val) \ - { \ - int bit_count = 0; \ - for (size_t i = 0; i < N; ++i) \ - { \ - const int word_count = ccm::support::NAME(val[INDEX_EXPR]); \ - bit_count += word_count; \ - if (word_count != ccm::support::numeric_limits::digits) break; \ - } \ - return bit_count; \ - } - - DECLARE_COUNTBIT(countr_zero, i) // iterating forward - DECLARE_COUNTBIT(countr_one, i) // iterating forward - DECLARE_COUNTBIT(countl_zero, N - i - 1) // iterating backward - DECLARE_COUNTBIT(countl_one, N - i - 1) // iterating backward - - } // namespace multiword - - template - struct BigInt // NOLINT(cppcoreguidelines-special-member-functions) - { - private: - static_assert(std::is_integral_v && std::is_unsigned_v, "WordType must be unsigned integer."); - - struct Division - { - BigInt quotient; - BigInt remainder; - }; - - public: - using word_type = WordType; - using unsigned_type = BigInt; - using signed_type = BigInt; - - inline static constexpr bool SIGNED = Signed; - inline static constexpr size_t BITS = Bits; - inline static constexpr size_t WORD_SIZE = sizeof(WordType) * ccm::support::numeric_limits::digits; - - static_assert(Bits > 0 && Bits % WORD_SIZE == 0, "Number of bits in BigInt should be a multiple of WORD_SIZE."); - - inline static constexpr size_t WORD_COUNT = Bits / WORD_SIZE; - - std::array val{}; // zero initialized. - - inline constexpr BigInt() = default; - - inline constexpr BigInt(const BigInt & other) = default; - - template - inline constexpr explicit BigInt(const BigInt & other) - { - if (OtherBits >= Bits) - { // truncate - for (size_t i = 0; i < WORD_COUNT; ++i) { val[i] = other[i]; } - } - else - { // zero or sign extend - size_t i = 0; - for (; i < OtherBits / WORD_SIZE; ++i) { val[i] = other[i]; } - extend(i, Signed && other.is_neg()); - } - } - - // Construct a BigInt from a C array. - template - inline constexpr explicit BigInt(const std::array & nums) - { - static_assert(N == WORD_COUNT); - for (size_t i = 0; i < WORD_COUNT; ++i) { val[i] = nums[i]; } - } - - inline constexpr explicit BigInt(const std::array & words) : val(words) {} - - // Initialize the first word to |v| and the rest to 0. - template >> - inline constexpr explicit BigInt(T v) - { - constexpr size_t T_SIZE = sizeof(T) * ccm::support::numeric_limits::digits; - const bool is_neg = Signed && (v < 0); - for (size_t i = 0; i < WORD_COUNT; ++i) - { - if (v == 0) - { - extend(i, is_neg); - return; - } - val[i] = static_cast(v); - if constexpr (T_SIZE > WORD_SIZE) { v >>= WORD_SIZE; } - else { v = 0; } - } - } - inline constexpr BigInt & operator=(const BigInt & other) = default; - - // constants - inline static constexpr BigInt zero() { return BigInt(); } - inline static constexpr BigInt one() { return BigInt(1); } - inline static constexpr BigInt all_ones() { return ~zero(); } - inline static constexpr BigInt min() - { - BigInt out; - if constexpr (SIGNED) { out.set_msb(); } - return out; - } - inline static constexpr BigInt max() - { - BigInt out = all_ones(); - if constexpr (SIGNED) { out.clear_msb(); } - return out; - } - - [[nodiscard]] inline constexpr bool is_neg() const { return SIGNED && get_msb(); } - - template - inline constexpr explicit operator T() const - { - return to(); - } - - template - inline constexpr std::enable_if_t && !std::is_same_v, T> to() const - { - constexpr size_t T_SIZE = sizeof(T) * ccm::support::numeric_limits::digits; - T lo = static_cast(val[0]); - if constexpr (T_SIZE <= WORD_SIZE) { return lo; } - constexpr size_t MAX_COUNT = T_SIZE > Bits ? WORD_COUNT : T_SIZE / WORD_SIZE; - for (size_t i = 1; i < MAX_COUNT; ++i) { lo += static_cast(val[i]) << (WORD_SIZE * i); } - if constexpr (Signed && (T_SIZE > Bits)) - { - // Extend sign for negative numbers. - constexpr T MASK = (~T(0) << Bits); - if (is_neg()) { lo |= MASK; } - } - return lo; - } - - inline constexpr explicit operator bool() const { return !is_zero(); } - - [[nodiscard]] inline constexpr bool is_zero() const - { - for (auto part : val) - { - if (part != 0) { return false; } - } - return true; - } - - // Add 'rhs' to this number and store the result in this number. - // Returns the carry value produced by the addition operation. - inline constexpr WordType add_overflow(const BigInt & rhs) { return multiword::add_with_carry(val, rhs.val); } - - inline constexpr BigInt operator+(const BigInt & other) const - { - BigInt result = *this; - result.add_overflow(other); - return result; - } - - // This will only apply when initializing a variable from constant values, so - // it will always use the constexpr version of add_with_carry. - inline constexpr BigInt operator+(BigInt && other) const // NOLINT - { - // We use addition commutativity to reuse 'other' and prevent allocation. - other.add_overflow(*this); // Returned carry value is ignored. - return other; - } - - inline constexpr BigInt & operator+=(const BigInt & other) - { - add_overflow(other); // Returned carry value is ignored. - return *this; - } - - // Subtract 'rhs' to this number and store the result in this number. - // Returns the carry value produced by the subtraction operation. - inline constexpr WordType sub_overflow(const BigInt & rhs) { return multiword::sub_with_borrow(val, rhs.val); } - - inline constexpr BigInt operator-(const BigInt & other) const - { - BigInt result = *this; - result.sub_overflow(other); // Returned carry value is ignored. - return result; - } - - inline constexpr BigInt operator-(BigInt && other) const // NOLINT - { - BigInt result = *this; - result.sub_overflow(other); // Returned carry value is ignored. - return result; - } - - inline constexpr BigInt & operator-=(const BigInt & other) - { - // TODO(lntue): Set overflow flag / errno when carry is true. - sub_overflow(other); // Returned carry value is ignored. - return *this; - } - - // Multiply this number with x and store the result in this number. - inline constexpr WordType mul(WordType x) { return multiword::scalar_multiply_with_carry(val, x); } - - // Return the full product. - template - inline constexpr auto ful_mul(const BigInt & other) const - { - BigInt result; - multiword::multiply_with_carry(result.val, val, other.val); - return result; - } - - inline constexpr BigInt operator*(const BigInt & other) const - { - // Perform full mul and truncate. - return BigInt(ful_mul(other)); - } - - // Fast hi part of the full product. The normal product `operator*` returns - // `Bits` least significant bits of the full product, while this function will - // approximate `Bits` most significant bits of the full product with errors - // bounded by: - // 0 <= (a.full_mul(b) >> Bits) - a.quick_mul_hi(b)) <= WORD_COUNT - 1. - // - // An example usage of this is to quickly (but less accurately) compute the - // product of (normalized) mantissas of floating point numbers: - // (mant_1, mant_2) -> quick_mul_hi -> normalize leading bit - // is much more efficient than: - // (mant_1, mant_2) -> ful_mul -> normalize leading bit - // -> convert back to same Bits width by shifting/rounding, - // especially for higher precisions. - // - // Performance summary: - // Number of 64-bit x 64-bit -> 128-bit multiplications performed. - // Bits WORD_COUNT ful_mul quick_mul_hi Error bound - // 128 2 4 3 1 - // 196 3 9 6 2 - // 256 4 16 10 3 - // 512 8 64 36 7 - [[nodiscard]] inline constexpr BigInt quick_mul_hi(const BigInt & other) const - { - BigInt result; - multiword::quick_mul_hi(result.val, val, other.val); - return result; - } - - // BigInt(x).pow_n(n) computes x ^ n. - // Note 0 ^ 0 == 1. - inline constexpr void pow_n(uint64_t power) - { - static_assert(!Signed); - BigInt result = one(); - BigInt cur_power = *this; - while (power > 0) - { - if ((power % 2) > 0) { result *= cur_power; } - power >>= 1; - cur_power *= cur_power; - } - *this = result; - } - - // Performs inplace signed / unsigned division. Returns remainder if not - // dividing by zero. - // For signed numbers it behaves like C++ signed integer division. - // That is by truncating the fractionnal part - // https://stackoverflow.com/a/3602857 - inline constexpr std::optional div(const BigInt & divider) - { - if (CCM_UNLIKELY(divider.is_zero())) { return std::nullopt; } - if (CCM_UNLIKELY(divider == BigInt::one())) { return BigInt::zero(); } - Division result; - if constexpr (SIGNED) { result = divide_signed(*this, divider); } - else { result = divide_unsigned(*this, divider); } - *this = result.quotient; - return result.remainder; - } - - // Efficiently perform BigInt / (x * 2^e), where x is a half-word-size - // unsigned integer, and return the remainder. The main idea is as follow: - // Let q = y / (x * 2^e) be the quotient, and - // r = y % (x * 2^e) be the remainder. - // First, notice that: - // r % (2^e) = y % (2^e), - // so we just need to focus on all the bits of y that is >= 2^e. - // To speed up the shift-and-add steps, we only use x as the divisor, and - // performing 32-bit shiftings instead of bit-by-bit shiftings. - // Since the remainder of each division step < x < 2^(WORD_SIZE / 2), the - // computation of each step is now properly contained within WordType. - // And finally we perform some extra alignment steps for the remaining bits. - inline constexpr std::optional div_uint_half_times_pow_2(multiword::half_width_t x, size_t e) - { - BigInt remainder; - if (x == 0) { return std::nullopt; } - if (e >= Bits) - { - remainder = *this; - *this = BigInt(); - return remainder; - } - BigInt quotient; - auto x_word = static_cast(x); - constexpr size_t LOG2_WORD_SIZE = ccm::support::bit_width(WORD_SIZE) - 1; - constexpr size_t HALF_WORD_SIZE = WORD_SIZE >> 1; - constexpr WordType HALF_MASK = ((WordType(1) << HALF_WORD_SIZE) - 1); - // lower = smallest multiple of WORD_SIZE that is >= e. - size_t lower = ((e >> LOG2_WORD_SIZE) + static_cast((e & (WORD_SIZE - 1)) != 0)) << LOG2_WORD_SIZE; - // lower_pos is the index of the closest WORD_SIZE-bit chunk >= 2^e. - size_t lower_pos = lower / WORD_SIZE; - // Keep track of current remainder mod x * 2^(32*i) - WordType rem = 0; - // pos is the index of the current 64-bit chunk that we are processing. - size_t pos = WORD_COUNT; - - for (size_t q_pos = WORD_COUNT - lower_pos; q_pos > 0; --q_pos) - { - // q_pos is 1 + the index of the current WORD_SIZE-bit chunk of the - // quotient being processed. Performing the division / modulus with - // divisor: - // x * 2^(WORD_SIZE*q_pos - WORD_SIZE/2), - // i.e. using the upper (WORD_SIZE/2)-bit of the current WORD_SIZE-bit - // chunk. - rem <<= HALF_WORD_SIZE; - rem += val[--pos] >> HALF_WORD_SIZE; - WordType q_tmp = rem / x_word; - rem %= x_word; - - // Performing the division / modulus with divisor: - // x * 2^(WORD_SIZE*(q_pos - 1)), - // i.e. using the lower (WORD_SIZE/2)-bit of the current WORD_SIZE-bit - // chunk. - rem <<= HALF_WORD_SIZE; - rem += val[pos] & HALF_MASK; - quotient.val[q_pos - 1] = (q_tmp << HALF_WORD_SIZE) + rem / x_word; - rem %= x_word; - } - - // So far, what we have is: - // quotient = y / (x * 2^lower), and - // rem = (y % (x * 2^lower)) / 2^lower. - // If (lower > e), we will need to perform an extra adjustment of the - // quotient and remainder, namely: - // y / (x * 2^e) = [ y / (x * 2^lower) ] * 2^(lower - e) + - // + (rem * 2^(lower - e)) / x - // (y % (x * 2^e)) / 2^e = (rem * 2^(lower - e)) % x - size_t last_shift = lower - e; - - if (last_shift > 0) - { - // quotient * 2^(lower - e) - quotient <<= last_shift; - WordType q_tmp = 0; - WordType d = val[--pos]; - if (last_shift >= HALF_WORD_SIZE) - { - // The shifting (rem * 2^(lower - e)) might overflow WordTyoe, so we - // perform a HALF_WORD_SIZE-bit shift first. - rem <<= HALF_WORD_SIZE; - rem += d >> HALF_WORD_SIZE; - d &= HALF_MASK; - q_tmp = rem / x_word; - rem %= x_word; - last_shift -= HALF_WORD_SIZE; - } - else - { - // Only use the upper HALF_WORD_SIZE-bit of the current WORD_SIZE-bit - // chunk. - d >>= HALF_WORD_SIZE; - } - - if (last_shift > 0) - { - rem <<= HALF_WORD_SIZE; - rem += d; - q_tmp <<= last_shift; - x_word <<= HALF_WORD_SIZE - last_shift; - q_tmp += rem / x_word; - rem %= x_word; - } - - quotient.val[0] += q_tmp; - - if (lower - e <= HALF_WORD_SIZE) - { - // The remainder rem * 2^(lower - e) might overflow to the higher - // WORD_SIZE-bit chunk. - if (pos < WORD_COUNT - 1) { remainder[pos + 1] = rem >> HALF_WORD_SIZE; } - remainder[pos] = (rem << HALF_WORD_SIZE) + (val[pos] & HALF_MASK); - } - else { remainder[pos] = rem; } - } - else { remainder[pos] = rem; } - - // Set the remaining lower bits of the remainder. - for (; pos > 0; --pos) { remainder[pos - 1] = val[pos - 1]; } - - *this = quotient; - return remainder; - } - - inline constexpr BigInt operator/(const BigInt & other) const - { - BigInt result(*this); - result.div(other); - return result; - } - - inline constexpr BigInt & operator/=(const BigInt & other) - { - div(other); - return *this; - } - - inline constexpr BigInt operator%(const BigInt & other) const - { - BigInt result(*this); - return *result.div(other); - } - - inline constexpr BigInt & operator*=(const BigInt & other) - { - *this = *this * other; - return *this; - } - - inline constexpr BigInt & operator<<=(size_t s) - { - val = multiword::shift(val, s); - return *this; - } - - inline constexpr BigInt operator<<(size_t s) const { return BigInt(multiword::shift(val, s)); } - - inline constexpr BigInt & operator>>=(size_t s) - { - val = multiword::shift(val, s); - return *this; - } - - inline constexpr BigInt operator>>(size_t s) const { return BigInt(multiword::shift(val, s)); } - -#define DEFINE_BINOP(OP) \ - inline friend constexpr BigInt operator OP(const BigInt & lhs, const BigInt & rhs) \ - { \ - BigInt result; \ - for (size_t i = 0; i < WORD_COUNT; ++i) result[i] = lhs[i] OP rhs[i]; \ - return result; \ - } \ - inline friend constexpr BigInt operator OP##=(BigInt & lhs, const BigInt & rhs) \ - { \ - for (size_t i = 0; i < WORD_COUNT; ++i) lhs[i] OP## = rhs[i]; \ - return lhs; \ - } - - DEFINE_BINOP(&) // & and &= - DEFINE_BINOP(|) // | and |= - DEFINE_BINOP(^) // ^ and ^= -#undef DEFINE_BINOP - - inline constexpr BigInt operator~() const - { - BigInt result; - for (size_t i = 0; i < WORD_COUNT; ++i) - { - result[i] = ~val[i]; // NOLINT - } - return result; - } - - inline constexpr BigInt operator-() const - { - BigInt result(*this); - result.negate(); - return result; - } - - inline friend constexpr bool operator==(const BigInt & lhs, const BigInt & rhs) - { - for (size_t i = 0; i < WORD_COUNT; ++i) - { - if (lhs.val[i] != rhs.val[i]) { return false; } - } - return true; - } - - inline friend constexpr bool operator!=(const BigInt & lhs, const BigInt & rhs) { return lhs != rhs; } - - inline friend constexpr bool operator>(const BigInt & lhs, const BigInt & rhs) { return cmp(lhs, rhs) > 0; } - inline friend constexpr bool operator>=(const BigInt & lhs, const BigInt & rhs) { return cmp(lhs, rhs) >= 0; } - inline friend constexpr bool operator<(const BigInt & lhs, const BigInt & rhs) { return cmp(lhs, rhs) < 0; } - inline friend constexpr bool operator<=(const BigInt & lhs, const BigInt & rhs) { return cmp(lhs, rhs) <= 0; } - - inline constexpr BigInt & operator++() - { - increment(); - return *this; - } - - inline constexpr BigInt operator++(int) // NOLINT - { - BigInt oldval(*this); - increment(); - return oldval; - } - - inline constexpr BigInt & operator--() - { - decrement(); - return *this; - } - - inline constexpr BigInt operator--(int) // NOLINT - { - BigInt oldval(*this); - decrement(); - return oldval; - } - - // Return the i-th word of the number. - inline constexpr const WordType & operator[](size_t i) const { return val[i]; } - - // Return the i-th word of the number. - inline constexpr WordType & operator[](size_t i) { return val[i]; } // NOLINT - - private: - inline friend constexpr int cmp(const BigInt & lhs, const BigInt & rhs) - { - constexpr auto compare = [](WordType a, WordType b) { return a == b ? 0 : a > b ? 1 : -1; }; - if constexpr (Signed) - { - const bool lhs_is_neg = lhs.is_neg(); - const bool rhs_is_neg = rhs.is_neg(); - if (lhs_is_neg != rhs_is_neg) { return rhs_is_neg ? 1 : -1; } - } - for (size_t i = WORD_COUNT; i-- > 0;) - { - if (auto cmp = compare(lhs[i], rhs[i]); cmp != 0) { return cmp; } - } - return 0; - } - - inline constexpr void bitwise_not() - { - for (auto & part : val) { part = ~part; } - } - - inline constexpr void negate() - { - bitwise_not(); - increment(); - } - - inline constexpr void increment() { multiword::add_with_carry(val, std::array{1}); } - - inline constexpr void decrement() { multiword::add_with_carry(val, std::array{1}); } - - inline constexpr void extend(size_t index, bool is_neg) - { - const WordType value = is_neg ? ccm::support::numeric_limits::max() : ccm::support::numeric_limits::min(); - for (size_t i = index; i < WORD_COUNT; ++i) { val[i] = value; } - } - - [[nodiscard]] inline constexpr bool get_msb() const { return val.back() >> (WORD_SIZE - 1); } - - inline constexpr void set_msb() { val.back() |= ccm::support::mask_leading_ones(); } - - inline constexpr void clear_msb() { val.back() &= ccm::support::mask_trailing_ones(); } - - inline constexpr void set_bit(size_t i) - { - const size_t word_index = i / WORD_SIZE; - val[word_index] |= WordType(1) << (i % WORD_SIZE); - } - - inline constexpr static Division divide_unsigned(const BigInt & dividend, const BigInt & divider) - { - BigInt remainder = dividend; - BigInt quotient; - if (remainder >= divider) - { - BigInt subtractor = divider; - int cur_bit = multiword::countl_zero(subtractor.val) - multiword::countl_zero(remainder.val); - subtractor <<= cur_bit; - for (; cur_bit >= 0 && remainder > 0; --cur_bit, subtractor >>= 1) - { - if (remainder < subtractor) { continue; } - remainder -= subtractor; - quotient.set_bit(cur_bit); - } - } - return Division{quotient, remainder}; - } - - inline constexpr static Division divide_signed(const BigInt & dividend, const BigInt & divider) - { - // Special case because it is not possible to negate the min value of a - // signed integer. - if (dividend == min() && divider == min()) { return Division{one(), zero()}; } - // 1. Convert the dividend and divisor to unsigned representation. - unsigned_type udividend(dividend); - unsigned_type udivider(divider); - // 2. Negate the dividend if it's negative, and similarly for the divisor. - const bool dividend_is_neg = dividend.is_neg(); - const bool divider_is_neg = divider.is_neg(); - if (dividend_is_neg) { udividend.negate(); } - if (divider_is_neg) { udivider.negate(); } - // 3. Use unsigned multiword division algorithm. - const auto unsigned_result = divide_unsigned(udividend, udivider); - // 4. Convert the quotient and remainder to signed representation. - Division result; - result.quotient = signed_type(unsigned_result.quotient); - result.remainder = signed_type(unsigned_result.remainder); - // 5. Negate the quotient if the dividend and divisor had opposite signs. - if (dividend_is_neg != divider_is_neg) { result.quotient.negate(); } - // 6. Negate the remainder if the dividend was negative. - if (dividend_is_neg) { result.remainder.negate(); } - return result; - } - - friend signed_type; - friend unsigned_type; - }; - - namespace internal - { - // We default BigInt's WordType to 'uint64_t' or 'uint32_t' depending on type - // availability. - template - struct WordTypeSelector : ccm::type_identity< -#ifdef CCM_HAS_INT64 - uint64_t -#else - uint32_t -#endif // CCM_HAS_INT64 - > - { - }; - // Except if we request 32 bits explicitly. - template <> - struct WordTypeSelector<32> : ccm::type_identity - { - }; - template - using WordTypeSelectorT = typename WordTypeSelector::type; - } // namespace internal - - template - using UInt = BigInt>; - - template - using Int = BigInt>; - -} // namespace ccm - -namespace ccm::support -{ - // Provides limits of U/Int<128>. - template <> - class numeric_limits> - { - public: - static constexpr ccm::UInt<128> max() { return ccm::UInt<128>::max(); } - static constexpr ccm::UInt<128> min() { return ccm::UInt<128>::min(); } - // Meant to match std::numeric_limits interface. - // NOLINTNEXTLINE(readability-identifier-naming) - static constexpr int digits = 128; - }; - - template <> - class numeric_limits> - { - public: - static constexpr ccm::Int<128> max() { return ccm::Int<128>::max(); } - static constexpr ccm::Int<128> min() { return ccm::Int<128>::min(); } - // Meant to match std::numeric_limits interface. - // NOLINTNEXTLINE(readability-identifier-naming) - static constexpr int digits = 128; - }; -} // namespace ccm::support - -//CCM_RESTORE_GCC_WARNING() - -namespace ccm -{ - // type traits to determine whether a T is a BigInt. - template - struct is_big_int : std::false_type - { - }; - - template - struct is_big_int> : std::true_type - { - }; - - template - inline constexpr bool is_big_int_v = is_big_int::value; - - // extensions of type traits to include BigInt - - // is_integral_or_big_int - template - struct is_integral_or_big_int : std::bool_constant<(std::is_integral_v || is_big_int_v)> - { - }; - - template - inline constexpr bool is_integral_or_big_int_v = is_integral_or_big_int::value; - - // make_big_int_unsigned - template - struct make_big_int_unsigned; - - template - struct make_big_int_unsigned> : ccm::type_identity> - { - }; - - template - using make_big_int_unsigned_t = typename make_big_int_unsigned::type; - - // make_big_int_signed - template - struct make_big_int_signed; - - template - struct make_big_int_signed> : ccm::type_identity> - { - }; - - template - using make_big_int_signed_t = typename make_big_int_signed::type; - - // make_integral_or_big_int_unsigned - template - struct make_integral_or_big_int_unsigned; - - template - struct make_integral_or_big_int_unsigned>> : std::make_unsigned - { - }; - - template - struct make_integral_or_big_int_unsigned>> : make_big_int_unsigned - { - }; - - template - using make_integral_or_big_int_unsigned_t = typename make_integral_or_big_int_unsigned::type; - - // make_integral_or_big_int_signed - template - struct make_integral_or_big_int_signed; - - template - struct make_integral_or_big_int_signed>> : std::make_signed - { - }; - - template - struct make_integral_or_big_int_signed>> : make_big_int_signed - { - }; - - template - using make_integral_or_big_int_signed_t = typename make_integral_or_big_int_signed::type; - - namespace cpp - { - - // Specialization of ccm::support::bit_cast ('') from T to BigInt. - template - inline constexpr std::enable_if_t< - (sizeof(To) == sizeof(From)) && std::is_trivially_copyable_v && std::is_trivially_copyable_v && is_big_int::value, To> - bit_cast(const From & from) - { - To out; - using Storage = decltype(out.val); - out.val = ccm::support::bit_cast(from); - return out; - } - - // Specialization of ccm::support::bit_cast ('') from BigInt to T. - template - inline constexpr std::enable_if_t) && std::is_trivially_constructible_v && std::is_trivially_copyable_v && - std::is_trivially_copyable_v>, - To> - bit_cast(const UInt & from) - { - return ccm::support::bit_cast(from.val); - } - - // Specialization of std::popcount ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> popcount(T value) - { - int bits = 0; - for (auto word : value.val) - { - if (word) { bits += popcount(word); } - } - return bits; - } - - // Specialization of std::has_single_bit ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, bool> has_single_bit(T value) - { - int bits = 0; - for (auto word : value.val) - { - if (word == 0) { continue; } - bits += popcount(word); - if (bits > 1) { return false; } - } - return bits == 1; - } - - // Specialization of std::countr_zero ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> countr_zero(const T & value) - { - return multiword::countr_zero(value.val); - } - - // Specialization of std::countl_zero ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> countl_zero(const T & value) - { - return multiword::countl_zero(value.val); - } - - // Specialization of std::countl_one ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> countl_one(T value) - { - return multiword::countl_one(value.val); - } - - // Specialization of std::countr_one ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> countr_one(T value) - { - return multiword::countr_one(value.val); - } - - // Specialization of ccm::support::bit_width ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> bit_width(T value) - { - return ccm::support::numeric_limits::digits - ccm::support::countl_zero(value); - } - - // Forward-declare rotr so that rotl can use it. - template - [[nodiscard]] inline constexpr std::enable_if_t, T> rotr(T value, int rotate); - - // Specialization of std::rotl ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, T> rotl(T value, int rotate) - { - constexpr unsigned N = ccm::support::numeric_limits::digits; - rotate = rotate % N; - if (!rotate) { return value; } - if (rotate < 0) { return ccm::support::rotr(value, -rotate); } - return (value << rotate) | (value >> (N - rotate)); - } - - // Specialization of std::rotr ('') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, T> rotr(T value, int rotate) - { - constexpr unsigned N = ccm::support::numeric_limits::digits; - rotate = rotate % N; - if (!rotate) { return value; } - if (rotate < 0) { return ccm::support::rotl(value, -rotate); } - return (value >> rotate) | (value << (N - rotate)); - } - - } // namespace cpp - - // Specialization of mask_trailing_ones ('math_support.hpp') for BigInt. - template - inline constexpr std::enable_if_t, T> mask_trailing_ones() - { - static_assert(!T::SIGNED && count <= T::BITS); - if (count == T::BITS) { return T::all_ones(); } - constexpr size_t QUOTIENT = count / T::WORD_SIZE; - constexpr size_t REMAINDER = count % T::WORD_SIZE; - T out; // zero initialized - for (size_t i = 0; i <= QUOTIENT; ++i) { out[i] = i < QUOTIENT ? -1 : mask_trailing_ones(); } - return out; - } - - // Specialization of mask_leading_ones ('math_support.hpp') for BigInt. - template - inline constexpr std::enable_if_t, T> mask_leading_ones() - { - static_assert(!T::SIGNED && count <= T::BITS); - if (count == T::BITS) { return T::all_ones(); } - constexpr size_t QUOTIENT = (T::BITS - count - 1U) / T::WORD_SIZE; - constexpr size_t REMAINDER = count % T::WORD_SIZE; - T out; // zero initialized - for (size_t i = QUOTIENT; i < T::WORD_COUNT; ++i) { out[i] = i > QUOTIENT ? -1 : mask_leading_ones(); } - return out; - } - - // Specialization of mask_trailing_zeros ('math_support.hpp') for BigInt. - template - inline constexpr std::enable_if_t, T> mask_trailing_zeros() - { - return mask_leading_ones(); - } - - // Specialization of mask_leading_zeros ('math_support.hpp') for BigInt. - template - inline constexpr std::enable_if_t, T> mask_leading_zeros() - { - return mask_trailing_ones(); - } - - // Specialization of count_zeros ('math_support.hpp) for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> count_zeros(T value) - { - return ccm::support::popcount(~value); - } - - // Specialization of first_leading_zero ('math_support.hpp') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> first_leading_zero(T value) - { - return value == ccm::support::numeric_limits::max() ? 0 : ccm::support::countl_one(value) + 1; - } - - // Specialization of first_leading_one ('math_support.hpp') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> first_leading_one(T value) - { - return first_leading_zero(~value); - } - - // Specialization of first_trailing_zero ('math_support.hpp') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> first_trailing_zero(T value) - { - return value == ccm::support::numeric_limits::max() ? 0 : ccm::support::countr_zero(~value) + 1; - } - - // Specialization of first_trailing_one ('math_support.hpp') for BigInt. - template - [[nodiscard]] inline constexpr std::enable_if_t, int> first_trailing_one(T value) - { - return value == ccm::support::numeric_limits::max() ? 0 : ccm::support::countr_zero(value) + 1; - } - -} // namespace ccm diff --git a/include/ccmath/internal/types/uint128.hpp b/include/ccmath/internal/types/uint128.hpp deleted file mode 100644 index 7fc1420..0000000 --- a/include/ccmath/internal/types/uint128.hpp +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include "ccmath/internal/types/uint.hpp" - -namespace ccm -{ -#ifdef CCM_HAS_INT128 - using uint128_t = __uint128_t; - using int128_t = __int128_t; -#else - using uint128_t = ccm::UInt<128>; - using int128_t = ccm::Int<128>; -#endif // CCM_HAS_INT128 - -} // namespace ccm