From 60f8ecbfa80f179a417cd3af8966f18ff381ebeb Mon Sep 17 00:00:00 2001 From: Rinzii Date: Mon, 1 Apr 2024 23:24:26 -0400 Subject: [PATCH] Bring all of my work into the dev branch for further work --- ccmath_headers.cmake | 9 + include/ccmath/internal/types/int128.hpp | 2301 ++++++++++++++++- include/ccmath/math/basic/fma.hpp | 10 + .../math/basic/impl/fma_double_impl.hpp | 31 + .../ccmath/math/basic/impl/fma_float_impl.hpp | 28 + .../math/basic/impl/fma_ldouble_impl.hpp | 31 + include/ccmath/math/power/pow.hpp | 41 +- test/CMakeLists.txt | 13 +- test/misc/lerp_test_no_std.cpp | 2 + test/power/pow_test.cpp | 29 + 10 files changed, 2456 insertions(+), 39 deletions(-) create mode 100644 include/ccmath/math/basic/impl/fma_double_impl.hpp create mode 100644 include/ccmath/math/basic/impl/fma_float_impl.hpp create mode 100644 include/ccmath/math/basic/impl/fma_ldouble_impl.hpp create mode 100644 test/power/pow_test.cpp diff --git a/ccmath_headers.cmake b/ccmath_headers.cmake index a0ef9e8..392ab73 100644 --- a/ccmath_headers.cmake +++ b/ccmath_headers.cmake @@ -55,6 +55,8 @@ set(ccmath_internal_headers ########################################## set(ccmath_math_basic_impl_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/fma_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/fma_double_impl.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/nan_float_impl.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/nan_double_impl.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/basic/impl/remquo_float_impl.hpp @@ -111,7 +113,14 @@ set(ccmath_math_exponential_headers ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/exponential/log10.hpp ) +set(ccmath_math_fmanip_impl_headers + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_float_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_double_impl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/impl/scalbn_ldouble_impl.hpp +) + set(ccmath_math_fmanip_headers + ${ccmath_math_fmanip_impl_headers} ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/copysign.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/frexp.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/ccmath/math/fmanip/ilogb.hpp diff --git a/include/ccmath/internal/types/int128.hpp b/include/ccmath/internal/types/int128.hpp index 2ff77cb..ff528ba 100644 --- a/include/ccmath/internal/types/int128.hpp +++ b/include/ccmath/internal/types/int128.hpp @@ -1,3 +1,20 @@ +// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code modified for use by ccmath + /* * Copyright (c) 2024-Present Ian Pike * Copyright (c) 2024-Present ccmath contributors @@ -8,12 +25,2286 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" + +#if defined(_MSC_VER) + // In very old versions of MSVC and when the /Zc:wchar_t flag is off, wchar_t is + // a typedef for unsigned short. Otherwise wchar_t is mapped to the __wchar_t + // builtin type. We need to make sure not to define operator wchar_t() + // alongside operator unsigned short() in these instances. + #define CCM_INTERNAL_WCHAR_T __wchar_t + #if defined(_M_X64) && !defined(_M_ARM64EC) + #include + #pragma intrinsic(_umul128) + #endif // defined(_M_X64) +#else // defined(_MSC_VER) + #define CCM_INTERNAL_WCHAR_T wchar_t +#endif // defined(_MSC_VER) + namespace ccm { - // TODO: Implement int128_t at some point. - // REASONING: Having access to int128_t would be extremely helpful in many cases when implementing specific - // algorithms such as remquo and many trigonometric functions. + + class int128; + + // uint128 + // + // An unsigned 128-bit integer type. The API is meant to mimic an intrinsic type + // as closely as is practical, including exhibiting undefined behavior in + // analogous cases (e.g. division by zero). This type is intended to be a + // drop-in replacement once C++ supports an intrinsic `uint128_t` type; when + // that occurs, existing well-behaved uses of `uint128` will continue to work + // using that new type. + // + // Note: code written with this type will continue to compile once `uint128_t` + // is introduced, provided the replacement helper functions + // `Uint128(Low|High)64()` and `MakeUint128()` are made. + // + // A `uint128` supports the following: + // + // * Implicit construction from integral types + // * Explicit conversion to integral types + // + // Additionally, if your compiler supports `__int128`, `uint128` is + // interoperable with that type. (Abseil checks for this compatibility through + // the `CCM_HAVE_INTRINSIC_INT128` macro.) + // + // However, a `uint128` differs from intrinsic integral types in the following + // ways: + // + // * Errors on implicit conversions that do not preserve value (such as + // loss of precision when converting to float values). + // * Requires explicit construction from and conversion to floating point + // types. + // * Conversion to integral types requires an explicit static_cast() to + // mimic use of the `-Wnarrowing` compiler flag. + // * The alignment requirement of `uint128` may differ from that of an + // intrinsic 128-bit integer type depending on platform and build + // configuration. // - // For the time being this is mega on hold, but I may return to this at some point. - // This file is really just here to keep a reminder of the idea. + // Example: + // + // float y = absl::Uint128Max(); // Error. uint128 cannot be implicitly + // // converted to float. + // + // absl::uint128 v; + // uint64_t i = v; // Error + // uint64_t i = static_cast(v); // OK + // + class +#if defined(CCM_HAVE_INTRINSIC_INT128) + alignas(unsigned __int128) +#endif // CCM_HAVE_INTRINSIC_INT128 + uint128 { + public: + uint128() = default; + + // Constructors from arithmetic types + constexpr uint128(int v); // NOLINT(runtime/explicit) + constexpr uint128(unsigned int v); // NOLINT(runtime/explicit) + constexpr uint128(long v); // NOLINT(runtime/int) + constexpr uint128(unsigned long v); // NOLINT(runtime/int) + constexpr uint128(long long v); // NOLINT(runtime/int) + constexpr uint128(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr uint128(__int128 v); // NOLINT(runtime/explicit) + constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit) +#endif // CCM_HAVE_INTRINSIC_INT128 + constexpr uint128(int128 v); // NOLINT(runtime/explicit) + explicit uint128(float v); + explicit uint128(double v); + explicit uint128(long double v); + + // Assignment operators from arithmetic types + uint128& operator=(int v); + uint128& operator=(unsigned int v); + uint128& operator=(long v); // NOLINT(runtime/int) + uint128& operator=(unsigned long v); // NOLINT(runtime/int) + uint128& operator=(long long v); // NOLINT(runtime/int) + uint128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + uint128& operator=(__int128 v); + uint128& operator=(unsigned __int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + uint128& operator=(int128 v); + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const;// +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: int128.h +// ----------------------------------------------------------------------------- +// +// This header file defines 128-bit integer types, `uint128` and `int128`. +// +// TODO(absl-team): This module is inconsistent as many inline `uint128` methods +// are defined in this file, while many inline `int128` methods are defined in +// the `int128_*_intrinsic.inc` files. + +#ifndef CCM_NUMERIC_INT128_H_ +#define CCM_NUMERIC_INT128_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" + +#if defined(_MSC_VER) +// In very old versions of MSVC and when the /Zc:wchar_t flag is off, wchar_t is +// a typedef for unsigned short. Otherwise wchar_t is mapped to the __wchar_t +// builtin type. We need to make sure not to define operator wchar_t() +// alongside operator unsigned short() in these instances. +#define CCM_INTERNAL_WCHAR_T __wchar_t +#if defined(_M_X64) && !defined(_M_ARM64EC) +#include +#pragma intrinsic(_umul128) +#endif // defined(_M_X64) +#else // defined(_MSC_VER) +#define CCM_INTERNAL_WCHAR_T wchar_t +#endif // defined(_MSC_VER) + +namespace absl { +CCM_NAMESPACE_BEGIN + +class int128; + +// uint128 +// +// An unsigned 128-bit integer type. The API is meant to mimic an intrinsic type +// as closely as is practical, including exhibiting undefined behavior in +// analogous cases (e.g. division by zero). This type is intended to be a +// drop-in replacement once C++ supports an intrinsic `uint128_t` type; when +// that occurs, existing well-behaved uses of `uint128` will continue to work +// using that new type. +// +// Note: code written with this type will continue to compile once `uint128_t` +// is introduced, provided the replacement helper functions +// `Uint128(Low|High)64()` and `MakeUint128()` are made. +// +// A `uint128` supports the following: +// +// * Implicit construction from integral types +// * Explicit conversion to integral types +// +// Additionally, if your compiler supports `__int128`, `uint128` is +// interoperable with that type. (Abseil checks for this compatibility through +// the `CCM_HAVE_INTRINSIC_INT128` macro.) +// +// However, a `uint128` differs from intrinsic integral types in the following +// ways: +// +// * Errors on implicit conversions that do not preserve value (such as +// loss of precision when converting to float values). +// * Requires explicit construction from and conversion to floating point +// types. +// * Conversion to integral types requires an explicit static_cast() to +// mimic use of the `-Wnarrowing` compiler flag. +// * The alignment requirement of `uint128` may differ from that of an +// intrinsic 128-bit integer type depending on platform and build +// configuration. +// +// Example: +// +// float y = absl::Uint128Max(); // Error. uint128 cannot be implicitly +// // converted to float. +// +// absl::uint128 v; +// uint64_t i = v; // Error +// uint64_t i = static_cast(v); // OK +// +class +#if defined(CCM_HAVE_INTRINSIC_INT128) + alignas(unsigned __int128) +#endif // CCM_HAVE_INTRINSIC_INT128 + uint128 { + public: + uint128() = default; + + // Constructors from arithmetic types + constexpr uint128(int v); // NOLINT(runtime/explicit) + constexpr uint128(unsigned int v); // NOLINT(runtime/explicit) + constexpr uint128(long v); // NOLINT(runtime/int) + constexpr uint128(unsigned long v); // NOLINT(runtime/int) + constexpr uint128(long long v); // NOLINT(runtime/int) + constexpr uint128(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr uint128(__int128 v); // NOLINT(runtime/explicit) + constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit) +#endif // CCM_HAVE_INTRINSIC_INT128 + constexpr uint128(int128 v); // NOLINT(runtime/explicit) + explicit uint128(float v); + explicit uint128(double v); + explicit uint128(long double v); + + // Assignment operators from arithmetic types + uint128& operator=(int v); + uint128& operator=(unsigned int v); + uint128& operator=(long v); // NOLINT(runtime/int) + uint128& operator=(unsigned long v); // NOLINT(runtime/int) + uint128& operator=(long long v); // NOLINT(runtime/int) + uint128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + uint128& operator=(__int128 v); + uint128& operator=(unsigned __int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + uint128& operator=(int128 v); + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const; + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator CCM_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // CCM_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators. + uint128& operator+=(uint128 other); + uint128& operator-=(uint128 other); + uint128& operator*=(uint128 other); + // Long division/modulo for uint128. + uint128& operator/=(uint128 other); + uint128& operator%=(uint128 other); + uint128 operator++(int); + uint128 operator--(int); + uint128& operator<<=(int); + uint128& operator>>=(int); + uint128& operator&=(uint128 other); + uint128& operator|=(uint128 other); + uint128& operator^=(uint128 other); + uint128& operator++(); + uint128& operator--(); + + // Uint128Low64() + // + // Returns the lower 64-bit value of a `uint128` value. + friend constexpr uint64_t Uint128Low64(uint128 v); + + // Uint128High64() + // + // Returns the higher 64-bit value of a `uint128` value. + friend constexpr uint64_t Uint128High64(uint128 v); + + // MakeUInt128() + // + // Constructs a `uint128` numeric value from two 64-bit unsigned integers. + // Note that this factory function is the only way to construct a `uint128` + // from integer values greater than 2^64. + // + // Example: + // + // absl::uint128 big = absl::MakeUint128(1, 0); + friend constexpr uint128 MakeUint128(uint64_t high, uint64_t low); + + // Uint128Max() + // + // Returns the highest value for a 128-bit unsigned integer. + friend constexpr uint128 Uint128Max(); + + // Support for absl::Hash. + template + friend H AbslHashValue(H h, uint128 v) { + return H::combine(std::move(h), Uint128High64(v), Uint128Low64(v)); + } + + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, uint128 v) { + sink.Append(v.ToString()); + } + + private: + constexpr uint128(uint64_t high, uint64_t low); + + std::string ToString() const; + + // TODO(strel) Update implementation to use __int128 once all users of + // uint128 are fixed to not depend on alignof(uint128) == 8. Also add + // alignas(16) to class definition to keep alignment consistent across + // platforms. +#if defined(CCM_IS_LITTLE_ENDIAN) + uint64_t lo_; + uint64_t hi_; +#elif defined(CCM_IS_BIG_ENDIAN) + uint64_t hi_; + uint64_t lo_; +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order +}; + +// allow uint128 to be logged +std::ostream& operator<<(std::ostream& os, uint128 v); + +// TODO(strel) add operator>>(std::istream&, uint128) + +constexpr uint128 Uint128Max() { + return uint128((std::numeric_limits::max)(), + (std::numeric_limits::max)()); +} + +CCM_NAMESPACE_END +} // namespace absl + +// Specialized numeric_limits for uint128. +namespace std { +template <> +class numeric_limits { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr int digits = 128; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#else // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#endif // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::uint128(min)() { return 0; } + static constexpr absl::uint128 lowest() { return 0; } + static constexpr absl::uint128(max)() { return absl::Uint128Max(); } + static constexpr absl::uint128 epsilon() { return 0; } + static constexpr absl::uint128 round_error() { return 0; } + static constexpr absl::uint128 infinity() { return 0; } + static constexpr absl::uint128 quiet_NaN() { return 0; } + static constexpr absl::uint128 signaling_NaN() { return 0; } + static constexpr absl::uint128 denorm_min() { return 0; } +}; +} // namespace std + +namespace absl { +CCM_NAMESPACE_BEGIN + +// int128 +// +// A signed 128-bit integer type. The API is meant to mimic an intrinsic +// integral type as closely as is practical, including exhibiting undefined +// behavior in analogous cases (e.g. division by zero). +// +// An `int128` supports the following: +// +// * Implicit construction from integral types +// * Explicit conversion to integral types +// +// However, an `int128` differs from intrinsic integral types in the following +// ways: +// +// * It is not implicitly convertible to other integral types. +// * Requires explicit construction from and conversion to floating point +// types. + +// Additionally, if your compiler supports `__int128`, `int128` is +// interoperable with that type. (Abseil checks for this compatibility through +// the `CCM_HAVE_INTRINSIC_INT128` macro.) +// +// The design goal for `int128` is that it will be compatible with a future +// `int128_t`, if that type becomes a part of the standard. +// +// Example: +// +// float y = absl::int128(17); // Error. int128 cannot be implicitly +// // converted to float. +// +// absl::int128 v; +// int64_t i = v; // Error +// int64_t i = static_cast(v); // OK +// +class int128 { + public: + int128() = default; + + // Constructors from arithmetic types + constexpr int128(int v); // NOLINT(runtime/explicit) + constexpr int128(unsigned int v); // NOLINT(runtime/explicit) + constexpr int128(long v); // NOLINT(runtime/int) + constexpr int128(unsigned long v); // NOLINT(runtime/int) + constexpr int128(long long v); // NOLINT(runtime/int) + constexpr int128(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr int128(__int128 v); // NOLINT(runtime/explicit) + constexpr explicit int128(unsigned __int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + constexpr explicit int128(uint128 v); + explicit int128(float v); + explicit int128(double v); + explicit int128(long double v); + + // Assignment operators from arithmetic types + int128& operator=(int v); + int128& operator=(unsigned int v); + int128& operator=(long v); // NOLINT(runtime/int) + int128& operator=(unsigned long v); // NOLINT(runtime/int) + int128& operator=(long long v); // NOLINT(runtime/int) + int128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + int128& operator=(__int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const; + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator CCM_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // CCM_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators + int128& operator+=(int128 other); + int128& operator-=(int128 other); + int128& operator*=(int128 other); + int128& operator/=(int128 other); + int128& operator%=(int128 other); + int128 operator++(int); // postfix increment: i++ + int128 operator--(int); // postfix decrement: i-- + int128& operator++(); // prefix increment: ++i + int128& operator--(); // prefix decrement: --i + int128& operator&=(int128 other); + int128& operator|=(int128 other); + int128& operator^=(int128 other); + int128& operator<<=(int amount); + int128& operator>>=(int amount); + + // Int128Low64() + // + // Returns the lower 64-bit value of a `int128` value. + friend constexpr uint64_t Int128Low64(int128 v); + + // Int128High64() + // + // Returns the higher 64-bit value of a `int128` value. + friend constexpr int64_t Int128High64(int128 v); + + // MakeInt128() + // + // Constructs a `int128` numeric value from two 64-bit integers. Note that + // signedness is conveyed in the upper `high` value. + // + // (absl::int128(1) << 64) * high + low + // + // Note that this factory function is the only way to construct a `int128` + // from integer values greater than 2^64 or less than -2^64. + // + // Example: + // + // absl::int128 big = absl::MakeInt128(1, 0); + // absl::int128 big_n = absl::MakeInt128(-1, 0); + friend constexpr int128 MakeInt128(int64_t high, uint64_t low); + + // Int128Max() + // + // Returns the maximum value for a 128-bit signed integer. + friend constexpr int128 Int128Max(); + + // Int128Min() + // + // Returns the minimum value for a 128-bit signed integer. + friend constexpr int128 Int128Min(); + + // Support for absl::Hash. + template + friend H AbslHashValue(H h, int128 v) { + return H::combine(std::move(h), Int128High64(v), Int128Low64(v)); + } + + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, int128 v) { + sink.Append(v.ToString()); + } + + private: + constexpr int128(int64_t high, uint64_t low); + + std::string ToString() const; + +#if defined(CCM_HAVE_INTRINSIC_INT128) + __int128 v_; +#else // CCM_HAVE_INTRINSIC_INT128 +#if defined(CCM_IS_LITTLE_ENDIAN) + uint64_t lo_; + int64_t hi_; +#elif defined(CCM_IS_BIG_ENDIAN) + int64_t hi_; + uint64_t lo_; +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order +#endif // CCM_HAVE_INTRINSIC_INT128 +}; + +std::ostream& operator<<(std::ostream& os, int128 v); + +// TODO(absl-team) add operator>>(std::istream&, int128) + +constexpr int128 Int128Max() { + return int128((std::numeric_limits::max)(), + (std::numeric_limits::max)()); +} + +constexpr int128 Int128Min() { + return int128((std::numeric_limits::min)(), 0); +} + +CCM_NAMESPACE_END +} // namespace absl + +// Specialized numeric_limits for int128. +namespace std { +template <> +class numeric_limits { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 127; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits<__int128>::traps; +#else // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#endif // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::int128(min)() { return absl::Int128Min(); } + static constexpr absl::int128 lowest() { return absl::Int128Min(); } + static constexpr absl::int128(max)() { return absl::Int128Max(); } + static constexpr absl::int128 epsilon() { return 0; } + static constexpr absl::int128 round_error() { return 0; } + static constexpr absl::int128 infinity() { return 0; } + static constexpr absl::int128 quiet_NaN() { return 0; } + static constexpr absl::int128 signaling_NaN() { return 0; } + static constexpr absl::int128 denorm_min() { return 0; } +}; +} // namespace std + +// -------------------------------------------------------------------------- +// Implementation details follow +// -------------------------------------------------------------------------- +namespace absl { +CCM_NAMESPACE_BEGIN + +constexpr uint128 MakeUint128(uint64_t high, uint64_t low) { + return uint128(high, low); +} + +// Assignment from integer types. + +inline uint128& uint128::operator=(int v) { return *this = uint128(v); } + +inline uint128& uint128::operator=(unsigned int v) { + return *this = uint128(v); +} + +inline uint128& uint128::operator=(long v) { // NOLINT(runtime/int) + return *this = uint128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline uint128& uint128::operator=(unsigned long v) { + return *this = uint128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline uint128& uint128::operator=(long long v) { return *this = uint128(v); } + +// NOLINTNEXTLINE(runtime/int) +inline uint128& uint128::operator=(unsigned long long v) { + return *this = uint128(v); +} + +#ifdef CCM_HAVE_INTRINSIC_INT128 +inline uint128& uint128::operator=(__int128 v) { return *this = uint128(v); } + +inline uint128& uint128::operator=(unsigned __int128 v) { + return *this = uint128(v); +} +#endif // CCM_HAVE_INTRINSIC_INT128 + +inline uint128& uint128::operator=(int128 v) { return *this = uint128(v); } + +// Arithmetic operators. + +constexpr uint128 operator<<(uint128 lhs, int amount); +constexpr uint128 operator>>(uint128 lhs, int amount); +constexpr uint128 operator+(uint128 lhs, uint128 rhs); +constexpr uint128 operator-(uint128 lhs, uint128 rhs); +uint128 operator*(uint128 lhs, uint128 rhs); +uint128 operator/(uint128 lhs, uint128 rhs); +uint128 operator%(uint128 lhs, uint128 rhs); + +inline uint128& uint128::operator<<=(int amount) { + *this = *this << amount; + return *this; +} + +inline uint128& uint128::operator>>=(int amount) { + *this = *this >> amount; + return *this; +} + +inline uint128& uint128::operator+=(uint128 other) { + *this = *this + other; + return *this; +} + +inline uint128& uint128::operator-=(uint128 other) { + *this = *this - other; + return *this; +} + +inline uint128& uint128::operator*=(uint128 other) { + *this = *this * other; + return *this; +} + +inline uint128& uint128::operator/=(uint128 other) { + *this = *this / other; + return *this; +} + +inline uint128& uint128::operator%=(uint128 other) { + *this = *this % other; + return *this; +} + +constexpr uint64_t Uint128Low64(uint128 v) { return v.lo_; } + +constexpr uint64_t Uint128High64(uint128 v) { return v.hi_; } + +// Constructors from integer types. + +#if defined(CCM_IS_LITTLE_ENDIAN) + +constexpr uint128::uint128(uint64_t high, uint64_t low) : lo_{low}, hi_{high} {} + +constexpr uint128::uint128(int v) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} +constexpr uint128::uint128(long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} +constexpr uint128::uint128(long long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} + +constexpr uint128::uint128(unsigned int v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::uint128(unsigned long v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::uint128(unsigned long long v) : lo_{v}, hi_{0} {} + +#ifdef CCM_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(__int128 v) + : lo_{static_cast(v & ~uint64_t{0})}, + hi_{static_cast(static_cast(v) >> 64)} {} +constexpr uint128::uint128(unsigned __int128 v) + : lo_{static_cast(v & ~uint64_t{0})}, + hi_{static_cast(v >> 64)} {} +#endif // CCM_HAVE_INTRINSIC_INT128 + +constexpr uint128::uint128(int128 v) + : lo_{Int128Low64(v)}, hi_{static_cast(Int128High64(v))} {} + +#elif defined(CCM_IS_BIG_ENDIAN) + +constexpr uint128::uint128(uint64_t high, uint64_t low) : hi_{high}, lo_{low} {} + +constexpr uint128::uint128(int v) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} +constexpr uint128::uint128(long v) // NOLINT(runtime/int) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} +constexpr uint128::uint128(long long v) // NOLINT(runtime/int) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} + +constexpr uint128::uint128(unsigned int v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::uint128(unsigned long v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::uint128(unsigned long long v) : hi_{0}, lo_{v} {} + +#ifdef CCM_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(__int128 v) + : hi_{static_cast(static_cast(v) >> 64)}, + lo_{static_cast(v & ~uint64_t{0})} {} +constexpr uint128::uint128(unsigned __int128 v) + : hi_{static_cast(v >> 64)}, + lo_{static_cast(v & ~uint64_t{0})} {} +#endif // CCM_HAVE_INTRINSIC_INT128 + +constexpr uint128::uint128(int128 v) + : hi_{static_cast(Int128High64(v))}, lo_{Int128Low64(v)} {} + +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order + +// Conversion operators to integer types. + +constexpr uint128::operator bool() const { return lo_ || hi_; } + +constexpr uint128::operator char() const { return static_cast(lo_); } + +constexpr uint128::operator signed char() const { + return static_cast(lo_); +} + +constexpr uint128::operator unsigned char() const { + return static_cast(lo_); +} + +constexpr uint128::operator char16_t() const { + return static_cast(lo_); +} + +constexpr uint128::operator char32_t() const { + return static_cast(lo_); +} + +constexpr uint128::operator CCM_INTERNAL_WCHAR_T() const { + return static_cast(lo_); +} + +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::operator short() const { return static_cast(lo_); } + +constexpr uint128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +constexpr uint128::operator int() const { return static_cast(lo_); } + +constexpr uint128::operator unsigned int() const { + return static_cast(lo_); +} + +// NOLINTNEXTLINE(runtime/int) +constexpr uint128::operator long() const { return static_cast(lo_); } + +constexpr uint128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +constexpr uint128::operator long long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +constexpr uint128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +#ifdef CCM_HAVE_INTRINSIC_INT128 +constexpr uint128::operator __int128() const { + return (static_cast<__int128>(hi_) << 64) + lo_; +} + +constexpr uint128::operator unsigned __int128() const { + return (static_cast(hi_) << 64) + lo_; +} +#endif // CCM_HAVE_INTRINSIC_INT128 + +// Conversion operators to floating point types. + +inline uint128::operator float() const { + return static_cast(lo_) + std::ldexp(static_cast(hi_), 64); +} + +inline uint128::operator double() const { + return static_cast(lo_) + std::ldexp(static_cast(hi_), 64); +} + +inline uint128::operator long double() const { + return static_cast(lo_) + + std::ldexp(static_cast(hi_), 64); +} + +// Comparison operators. + +constexpr bool operator==(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) == + static_cast(rhs); +#else + return (Uint128Low64(lhs) == Uint128Low64(rhs) && + Uint128High64(lhs) == Uint128High64(rhs)); +#endif +} + +constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); } + +constexpr bool operator<(uint128 lhs, uint128 rhs) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) < + static_cast(rhs); +#else + return (Uint128High64(lhs) == Uint128High64(rhs)) + ? (Uint128Low64(lhs) < Uint128Low64(rhs)) + : (Uint128High64(lhs) < Uint128High64(rhs)); +#endif +} + +constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } + +constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } + +constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } + +// Unary operators. + +constexpr inline uint128 operator+(uint128 val) { return val; } + +constexpr inline int128 operator+(int128 val) { return val; } + +constexpr uint128 operator-(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return -static_cast(val); +#else + return MakeUint128( + ~Uint128High64(val) + static_cast(Uint128Low64(val) == 0), + ~Uint128Low64(val) + 1); +#endif +} + +constexpr inline bool operator!(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return !static_cast(val); +#else + return !Uint128High64(val) && !Uint128Low64(val); +#endif +} + +// Logical operators. + +constexpr inline uint128 operator~(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return ~static_cast(val); +#else + return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif +} + +constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) | + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif +} + +constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) & + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif +} + +constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) ^ + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif +} + +inline uint128& uint128::operator|=(uint128 other) { + *this = *this | other; + return *this; +} + +inline uint128& uint128::operator&=(uint128 other) { + *this = *this & other; + return *this; +} + +inline uint128& uint128::operator^=(uint128 other) { + *this = *this ^ other; + return *this; +} + +// Arithmetic operators. + +constexpr uint128 operator<<(uint128 lhs, int amount) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) << amount; +#else + // uint64_t shifts of >= 64 are undefined, so we will need some + // special-casing. + return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0) + : amount == 0 ? lhs + : MakeUint128((Uint128High64(lhs) << amount) | + (Uint128Low64(lhs) >> (64 - amount)), + Uint128Low64(lhs) << amount); +#endif +} + +constexpr uint128 operator>>(uint128 lhs, int amount) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) >> amount; +#else + // uint64_t shifts of >= 64 are undefined, so we will need some + // special-casing. + return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64)) + : amount == 0 ? lhs + : MakeUint128(Uint128High64(lhs) >> amount, + (Uint128Low64(lhs) >> amount) | + (Uint128High64(lhs) << (64 - amount))); +#endif +} + +#if !defined(CCM_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 AddResult(uint128 result, uint128 lhs) { + // check for carry + return (Uint128Low64(result) < Uint128Low64(lhs)) + ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) + + static_cast(rhs); +#else + return int128_internal::AddResult( + MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), + Uint128Low64(lhs) + Uint128Low64(rhs)), + lhs); +#endif +} + +#if !defined(CCM_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) { + // check for carry + return (Uint128Low64(lhs) < Uint128Low64(rhs)) + ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) - + static_cast(rhs); +#else + return int128_internal::SubstructResult( + MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), + Uint128Low64(lhs) - Uint128Low64(rhs)), + lhs, rhs); +#endif +} + +inline uint128 operator*(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + // TODO(strel) Remove once alignment issues are resolved and unsigned __int128 + // can be used for uint128 storage. + return static_cast(lhs) * + static_cast(rhs); +#elif defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) + uint64_t carry; + uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); + return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + + Uint128High64(lhs) * Uint128Low64(rhs) + carry, + low); +#else // CCM_HAVE_INTRINSIC128 + uint64_t a32 = Uint128Low64(lhs) >> 32; + uint64_t a00 = Uint128Low64(lhs) & 0xffffffff; + uint64_t b32 = Uint128Low64(rhs) >> 32; + uint64_t b00 = Uint128Low64(rhs) & 0xffffffff; + uint128 result = + MakeUint128(Uint128High64(lhs) * Uint128Low64(rhs) + + Uint128Low64(lhs) * Uint128High64(rhs) + a32 * b32, + a00 * b00); + result += uint128(a32 * b00) << 32; + result += uint128(a00 * b32) << 32; + return result; +#endif // CCM_HAVE_INTRINSIC128 +} + +#if defined(CCM_HAVE_INTRINSIC_INT128) +inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast(lhs) / + static_cast(rhs); +} + +inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast(lhs) % + static_cast(rhs); +} +#endif + +// Increment/decrement operators. + +inline uint128 uint128::operator++(int) { + uint128 tmp(*this); + *this += 1; + return tmp; +} + +inline uint128 uint128::operator--(int) { + uint128 tmp(*this); + *this -= 1; + return tmp; } + +inline uint128& uint128::operator++() { + *this += 1; + return *this; +} + +inline uint128& uint128::operator--() { + *this -= 1; + return *this; +} + +constexpr int128 MakeInt128(int64_t high, uint64_t low) { + return int128(high, low); +} + +// Assignment from integer types. +inline int128& int128::operator=(int v) { return *this = int128(v); } + +inline int128& int128::operator=(unsigned int v) { return *this = int128(v); } + +inline int128& int128::operator=(long v) { // NOLINT(runtime/int) + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long v) { return *this = int128(v); } + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(long long v) { return *this = int128(v); } + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long long v) { + return *this = int128(v); +} + +// Arithmetic operators. +constexpr int128 operator-(int128 v); +constexpr int128 operator+(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 lhs, int128 rhs); +int128 operator*(int128 lhs, int128 rhs); +int128 operator/(int128 lhs, int128 rhs); +int128 operator%(int128 lhs, int128 rhs); +constexpr int128 operator|(int128 lhs, int128 rhs); +constexpr int128 operator&(int128 lhs, int128 rhs); +constexpr int128 operator^(int128 lhs, int128 rhs); +constexpr int128 operator<<(int128 lhs, int amount); +constexpr int128 operator>>(int128 lhs, int amount); + +inline int128& int128::operator+=(int128 other) { + *this = *this + other; + return *this; +} + +inline int128& int128::operator-=(int128 other) { + *this = *this - other; + return *this; +} + +inline int128& int128::operator*=(int128 other) { + *this = *this * other; + return *this; +} + +inline int128& int128::operator/=(int128 other) { + *this = *this / other; + return *this; +} + +inline int128& int128::operator%=(int128 other) { + *this = *this % other; + return *this; +} + +inline int128& int128::operator|=(int128 other) { + *this = *this | other; + return *this; +} + +inline int128& int128::operator&=(int128 other) { + *this = *this & other; + return *this; +} + +inline int128& int128::operator^=(int128 other) { + *this = *this ^ other; + return *this; +} + +inline int128& int128::operator<<=(int amount) { + *this = *this << amount; + return *this; +} + +inline int128& int128::operator>>=(int amount) { + *this = *this >> amount; + return *this; +} + +// Forward declaration for comparison operators. +constexpr bool operator!=(int128 lhs, int128 rhs); + +namespace int128_internal { + +// Casts from unsigned to signed while preserving the underlying binary +// representation. +constexpr int64_t BitCastToSigned(uint64_t v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang, GCC, and + // MSVC optimize this to a no-op on x86-64. + return v & (uint64_t{1} << 63) ? ~static_cast(~v) + : static_cast(v); +} + +} // namespace int128_internal + +#if defined(CCM_HAVE_INTRINSIC_INT128) +#include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export +#else // CCM_HAVE_INTRINSIC_INT128 +#include "absl/numeric/int128_no_intrinsic.inc" // IWYU pragma: export +#endif // CCM_HAVE_INTRINSIC_INT128 + +CCM_NAMESPACE_END +} // namespace absl + +#undef CCM_INTERNAL_WCHAR_T + +#endif // CCM_NUMERIC_INT128_H_ + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator CCM_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // CCM_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators. + uint128& operator+=(uint128 other); + uint128& operator-=(uint128 other); + uint128& operator*=(uint128 other); + // Long division/modulo for uint128. + uint128& operator/=(uint128 other); + uint128& operator%=(uint128 other); + uint128 operator++(int); + uint128 operator--(int); + uint128& operator<<=(int); + uint128& operator>>=(int); + uint128& operator&=(uint128 other); + uint128& operator|=(uint128 other); + uint128& operator^=(uint128 other); + uint128& operator++(); + uint128& operator--(); + + // Uint128Low64() + // + // Returns the lower 64-bit value of a `uint128` value. + friend constexpr uint64_t Uint128Low64(uint128 v); + + // Uint128High64() + // + // Returns the higher 64-bit value of a `uint128` value. + friend constexpr uint64_t Uint128High64(uint128 v); + + // MakeUInt128() + // + // Constructs a `uint128` numeric value from two 64-bit unsigned integers. + // Note that this factory function is the only way to construct a `uint128` + // from integer values greater than 2^64. + // + // Example: + // + // absl::uint128 big = absl::MakeUint128(1, 0); + friend constexpr uint128 MakeUint128(uint64_t high, uint64_t low); + + // Uint128Max() + // + // Returns the highest value for a 128-bit unsigned integer. + friend constexpr uint128 Uint128Max(); + + // Support for absl::Hash. + template + friend H AbslHashValue(H h, uint128 v) { + return H::combine(std::move(h), Uint128High64(v), Uint128Low64(v)); + } + + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, uint128 v) { + sink.Append(v.ToString()); + } + + private: + constexpr uint128(uint64_t high, uint64_t low); + + std::string ToString() const; + + // TODO(strel) Update implementation to use __int128 once all users of + // uint128 are fixed to not depend on alignof(uint128) == 8. Also add + // alignas(16) to class definition to keep alignment consistent across + // platforms. +#if defined(CCM_IS_LITTLE_ENDIAN) + uint64_t lo_; + uint64_t hi_; +#elif defined(CCM_IS_BIG_ENDIAN) + uint64_t hi_; + uint64_t lo_; +#else // byte order + #error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order + }; + + // allow uint128 to be logged + std::ostream& operator<<(std::ostream& os, uint128 v); + + // TODO(strel) add operator>>(std::istream&, uint128) + + constexpr uint128 Uint128Max() { + return uint128((std::numeric_limits::max)(), + (std::numeric_limits::max)()); + } + + CCM_NAMESPACE_END +} // namespace absl + +// Specialized numeric_limits for uint128. +namespace std { + template <> + class numeric_limits { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr int digits = 128; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#else // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#endif // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::uint128(min)() { return 0; } + static constexpr absl::uint128 lowest() { return 0; } + static constexpr absl::uint128(max)() { return absl::Uint128Max(); } + static constexpr absl::uint128 epsilon() { return 0; } + static constexpr absl::uint128 round_error() { return 0; } + static constexpr absl::uint128 infinity() { return 0; } + static constexpr absl::uint128 quiet_NaN() { return 0; } + static constexpr absl::uint128 signaling_NaN() { return 0; } + static constexpr absl::uint128 denorm_min() { return 0; } + }; +} // namespace std + +namespace absl { + CCM_NAMESPACE_BEGIN + + // int128 + // + // A signed 128-bit integer type. The API is meant to mimic an intrinsic + // integral type as closely as is practical, including exhibiting undefined + // behavior in analogous cases (e.g. division by zero). + // + // An `int128` supports the following: + // + // * Implicit construction from integral types + // * Explicit conversion to integral types + // + // However, an `int128` differs from intrinsic integral types in the following + // ways: + // + // * It is not implicitly convertible to other integral types. + // * Requires explicit construction from and conversion to floating point + // types. + + // Additionally, if your compiler supports `__int128`, `int128` is + // interoperable with that type. (Abseil checks for this compatibility through + // the `CCM_HAVE_INTRINSIC_INT128` macro.) + // + // The design goal for `int128` is that it will be compatible with a future + // `int128_t`, if that type becomes a part of the standard. + // + // Example: + // + // float y = absl::int128(17); // Error. int128 cannot be implicitly + // // converted to float. + // + // absl::int128 v; + // int64_t i = v; // Error + // int64_t i = static_cast(v); // OK + // + class int128 { + public: + int128() = default; + + // Constructors from arithmetic types + constexpr int128(int v); // NOLINT(runtime/explicit) + constexpr int128(unsigned int v); // NOLINT(runtime/explicit) + constexpr int128(long v); // NOLINT(runtime/int) + constexpr int128(unsigned long v); // NOLINT(runtime/int) + constexpr int128(long long v); // NOLINT(runtime/int) + constexpr int128(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr int128(__int128 v); // NOLINT(runtime/explicit) + constexpr explicit int128(unsigned __int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + constexpr explicit int128(uint128 v); + explicit int128(float v); + explicit int128(double v); + explicit int128(long double v); + + // Assignment operators from arithmetic types + int128& operator=(int v); + int128& operator=(unsigned int v); + int128& operator=(long v); // NOLINT(runtime/int) + int128& operator=(unsigned long v); // NOLINT(runtime/int) + int128& operator=(long long v); // NOLINT(runtime/int) + int128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef CCM_HAVE_INTRINSIC_INT128 + int128& operator=(__int128 v); +#endif // CCM_HAVE_INTRINSIC_INT128 + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const; + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator CCM_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // CCM_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators + int128& operator+=(int128 other); + int128& operator-=(int128 other); + int128& operator*=(int128 other); + int128& operator/=(int128 other); + int128& operator%=(int128 other); + int128 operator++(int); // postfix increment: i++ + int128 operator--(int); // postfix decrement: i-- + int128& operator++(); // prefix increment: ++i + int128& operator--(); // prefix decrement: --i + int128& operator&=(int128 other); + int128& operator|=(int128 other); + int128& operator^=(int128 other); + int128& operator<<=(int amount); + int128& operator>>=(int amount); + + // Int128Low64() + // + // Returns the lower 64-bit value of a `int128` value. + friend constexpr uint64_t Int128Low64(int128 v); + + // Int128High64() + // + // Returns the higher 64-bit value of a `int128` value. + friend constexpr int64_t Int128High64(int128 v); + + // MakeInt128() + // + // Constructs a `int128` numeric value from two 64-bit integers. Note that + // signedness is conveyed in the upper `high` value. + // + // (absl::int128(1) << 64) * high + low + // + // Note that this factory function is the only way to construct a `int128` + // from integer values greater than 2^64 or less than -2^64. + // + // Example: + // + // absl::int128 big = absl::MakeInt128(1, 0); + // absl::int128 big_n = absl::MakeInt128(-1, 0); + friend constexpr int128 MakeInt128(int64_t high, uint64_t low); + + // Int128Max() + // + // Returns the maximum value for a 128-bit signed integer. + friend constexpr int128 Int128Max(); + + // Int128Min() + // + // Returns the minimum value for a 128-bit signed integer. + friend constexpr int128 Int128Min(); + + // Support for absl::Hash. + template + friend H AbslHashValue(H h, int128 v) { + return H::combine(std::move(h), Int128High64(v), Int128Low64(v)); + } + + // Support for absl::StrCat() etc. + template + friend void AbslStringify(Sink& sink, int128 v) { + sink.Append(v.ToString()); + } + + private: + constexpr int128(int64_t high, uint64_t low); + + std::string ToString() const; + +#if defined(CCM_HAVE_INTRINSIC_INT128) + __int128 v_; +#else // CCM_HAVE_INTRINSIC_INT128 + #if defined(CCM_IS_LITTLE_ENDIAN) + uint64_t lo_; + int64_t hi_; + #elif defined(CCM_IS_BIG_ENDIAN) + int64_t hi_; + uint64_t lo_; + #else // byte order + #error "Unsupported byte order: must be little-endian or big-endian." + #endif // byte order +#endif // CCM_HAVE_INTRINSIC_INT128 + }; + + std::ostream& operator<<(std::ostream& os, int128 v); + + // TODO(absl-team) add operator>>(std::istream&, int128) + + constexpr int128 Int128Max() { + return int128((std::numeric_limits::max)(), + (std::numeric_limits::max)()); + } + + constexpr int128 Int128Min() { + return int128((std::numeric_limits::min)(), 0); + } + + CCM_NAMESPACE_END +} // namespace absl + +// Specialized numeric_limits for int128. +namespace std { + template <> + class numeric_limits { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 127; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits<__int128>::traps; +#else // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#endif // CCM_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::int128(min)() { return absl::Int128Min(); } + static constexpr absl::int128 lowest() { return absl::Int128Min(); } + static constexpr absl::int128(max)() { return absl::Int128Max(); } + static constexpr absl::int128 epsilon() { return 0; } + static constexpr absl::int128 round_error() { return 0; } + static constexpr absl::int128 infinity() { return 0; } + static constexpr absl::int128 quiet_NaN() { return 0; } + static constexpr absl::int128 signaling_NaN() { return 0; } + static constexpr absl::int128 denorm_min() { return 0; } + }; +} // namespace std + +// -------------------------------------------------------------------------- +// Implementation details follow +// -------------------------------------------------------------------------- +namespace absl { + CCM_NAMESPACE_BEGIN + + constexpr uint128 MakeUint128(uint64_t high, uint64_t low) { + return uint128(high, low); + } + + // Assignment from integer types. + + inline uint128& uint128::operator=(int v) { return *this = uint128(v); } + + inline uint128& uint128::operator=(unsigned int v) { + return *this = uint128(v); + } + + inline uint128& uint128::operator=(long v) { // NOLINT(runtime/int) + return *this = uint128(v); + } + + // NOLINTNEXTLINE(runtime/int) + inline uint128& uint128::operator=(unsigned long v) { + return *this = uint128(v); + } + + // NOLINTNEXTLINE(runtime/int) + inline uint128& uint128::operator=(long long v) { return *this = uint128(v); } + + // NOLINTNEXTLINE(runtime/int) + inline uint128& uint128::operator=(unsigned long long v) { + return *this = uint128(v); + } + +#ifdef CCM_HAVE_INTRINSIC_INT128 + inline uint128& uint128::operator=(__int128 v) { return *this = uint128(v); } + + inline uint128& uint128::operator=(unsigned __int128 v) { + return *this = uint128(v); + } +#endif // CCM_HAVE_INTRINSIC_INT128 + + inline uint128& uint128::operator=(int128 v) { return *this = uint128(v); } + + // Arithmetic operators. + + constexpr uint128 operator<<(uint128 lhs, int amount); + constexpr uint128 operator>>(uint128 lhs, int amount); + constexpr uint128 operator+(uint128 lhs, uint128 rhs); + constexpr uint128 operator-(uint128 lhs, uint128 rhs); + uint128 operator*(uint128 lhs, uint128 rhs); + uint128 operator/(uint128 lhs, uint128 rhs); + uint128 operator%(uint128 lhs, uint128 rhs); + + inline uint128& uint128::operator<<=(int amount) { + *this = *this << amount; + return *this; + } + + inline uint128& uint128::operator>>=(int amount) { + *this = *this >> amount; + return *this; + } + + inline uint128& uint128::operator+=(uint128 other) { + *this = *this + other; + return *this; + } + + inline uint128& uint128::operator-=(uint128 other) { + *this = *this - other; + return *this; + } + + inline uint128& uint128::operator*=(uint128 other) { + *this = *this * other; + return *this; + } + + inline uint128& uint128::operator/=(uint128 other) { + *this = *this / other; + return *this; + } + + inline uint128& uint128::operator%=(uint128 other) { + *this = *this % other; + return *this; + } + + constexpr uint64_t Uint128Low64(uint128 v) { return v.lo_; } + + constexpr uint64_t Uint128High64(uint128 v) { return v.hi_; } + + // Constructors from integer types. + +#if defined(CCM_IS_LITTLE_ENDIAN) + + constexpr uint128::uint128(uint64_t high, uint64_t low) : lo_{low}, hi_{high} {} + + constexpr uint128::uint128(int v) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} + constexpr uint128::uint128(long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} + constexpr uint128::uint128(long long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, + hi_{v < 0 ? (std::numeric_limits::max)() : 0} {} + + constexpr uint128::uint128(unsigned int v) : lo_{v}, hi_{0} {} + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::uint128(unsigned long v) : lo_{v}, hi_{0} {} + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::uint128(unsigned long long v) : lo_{v}, hi_{0} {} + + #ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr uint128::uint128(__int128 v) + : lo_{static_cast(v & ~uint64_t{0})}, + hi_{static_cast(static_cast(v) >> 64)} {} + constexpr uint128::uint128(unsigned __int128 v) + : lo_{static_cast(v & ~uint64_t{0})}, + hi_{static_cast(v >> 64)} {} + #endif // CCM_HAVE_INTRINSIC_INT128 + + constexpr uint128::uint128(int128 v) + : lo_{Int128Low64(v)}, hi_{static_cast(Int128High64(v))} {} + +#elif defined(CCM_IS_BIG_ENDIAN) + + constexpr uint128::uint128(uint64_t high, uint64_t low) : hi_{high}, lo_{low} {} + + constexpr uint128::uint128(int v) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} + constexpr uint128::uint128(long v) // NOLINT(runtime/int) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} + constexpr uint128::uint128(long long v) // NOLINT(runtime/int) + : hi_{v < 0 ? (std::numeric_limits::max)() : 0}, + lo_{static_cast(v)} {} + + constexpr uint128::uint128(unsigned int v) : hi_{0}, lo_{v} {} + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::uint128(unsigned long v) : hi_{0}, lo_{v} {} + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::uint128(unsigned long long v) : hi_{0}, lo_{v} {} + + #ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr uint128::uint128(__int128 v) + : hi_{static_cast(static_cast(v) >> 64)}, + lo_{static_cast(v & ~uint64_t{0})} {} + constexpr uint128::uint128(unsigned __int128 v) + : hi_{static_cast(v >> 64)}, + lo_{static_cast(v & ~uint64_t{0})} {} + #endif // CCM_HAVE_INTRINSIC_INT128 + + constexpr uint128::uint128(int128 v) + : hi_{static_cast(Int128High64(v))}, lo_{Int128Low64(v)} {} + +#else // byte order + #error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order + + // Conversion operators to integer types. + + constexpr uint128::operator bool() const { return lo_ || hi_; } + + constexpr uint128::operator char() const { return static_cast(lo_); } + + constexpr uint128::operator signed char() const { + return static_cast(lo_); + } + + constexpr uint128::operator unsigned char() const { + return static_cast(lo_); + } + + constexpr uint128::operator char16_t() const { + return static_cast(lo_); + } + + constexpr uint128::operator char32_t() const { + return static_cast(lo_); + } + + constexpr uint128::operator CCM_INTERNAL_WCHAR_T() const { + return static_cast(lo_); + } + + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::operator short() const { return static_cast(lo_); } + + constexpr uint128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) + } + + constexpr uint128::operator int() const { return static_cast(lo_); } + + constexpr uint128::operator unsigned int() const { + return static_cast(lo_); + } + + // NOLINTNEXTLINE(runtime/int) + constexpr uint128::operator long() const { return static_cast(lo_); } + + constexpr uint128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) + } + + constexpr uint128::operator long long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) + } + + constexpr uint128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) + } + +#ifdef CCM_HAVE_INTRINSIC_INT128 + constexpr uint128::operator __int128() const { + return (static_cast<__int128>(hi_) << 64) + lo_; + } + + constexpr uint128::operator unsigned __int128() const { + return (static_cast(hi_) << 64) + lo_; + } +#endif // CCM_HAVE_INTRINSIC_INT128 + + // Conversion operators to floating point types. + + inline uint128::operator float() const { + return static_cast(lo_) + std::ldexp(static_cast(hi_), 64); + } + + inline uint128::operator double() const { + return static_cast(lo_) + std::ldexp(static_cast(hi_), 64); + } + + inline uint128::operator long double() const { + return static_cast(lo_) + + std::ldexp(static_cast(hi_), 64); + } + + // Comparison operators. + + constexpr bool operator==(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) == + static_cast(rhs); +#else + return (Uint128Low64(lhs) == Uint128Low64(rhs) && + Uint128High64(lhs) == Uint128High64(rhs)); +#endif + } + + constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); } + + constexpr bool operator<(uint128 lhs, uint128 rhs) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) < + static_cast(rhs); +#else + return (Uint128High64(lhs) == Uint128High64(rhs)) + ? (Uint128Low64(lhs) < Uint128Low64(rhs)) + : (Uint128High64(lhs) < Uint128High64(rhs)); +#endif + } + + constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } + + constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } + + constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } + + // Unary operators. + + constexpr inline uint128 operator+(uint128 val) { return val; } + + constexpr inline int128 operator+(int128 val) { return val; } + + constexpr uint128 operator-(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return -static_cast(val); +#else + return MakeUint128( + ~Uint128High64(val) + static_cast(Uint128Low64(val) == 0), + ~Uint128Low64(val) + 1); +#endif + } + + constexpr inline bool operator!(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return !static_cast(val); +#else + return !Uint128High64(val) && !Uint128Low64(val); +#endif + } + + // Logical operators. + + constexpr inline uint128 operator~(uint128 val) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return ~static_cast(val); +#else + return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif + } + + constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) | + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif + } + + constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) & + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif + } + + constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) ^ + static_cast(rhs); +#else + return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif + } + + inline uint128& uint128::operator|=(uint128 other) { + *this = *this | other; + return *this; + } + + inline uint128& uint128::operator&=(uint128 other) { + *this = *this & other; + return *this; + } + + inline uint128& uint128::operator^=(uint128 other) { + *this = *this ^ other; + return *this; + } + + // Arithmetic operators. + + constexpr uint128 operator<<(uint128 lhs, int amount) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) << amount; +#else + // uint64_t shifts of >= 64 are undefined, so we will need some + // special-casing. + return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0) + : amount == 0 ? lhs + : MakeUint128((Uint128High64(lhs) << amount) | + (Uint128Low64(lhs) >> (64 - amount)), + Uint128Low64(lhs) << amount); +#endif + } + + constexpr uint128 operator>>(uint128 lhs, int amount) { +#ifdef CCM_HAVE_INTRINSIC_INT128 + return static_cast(lhs) >> amount; +#else + // uint64_t shifts of >= 64 are undefined, so we will need some + // special-casing. + return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64)) + : amount == 0 ? lhs + : MakeUint128(Uint128High64(lhs) >> amount, + (Uint128Low64(lhs) >> amount) | + (Uint128High64(lhs) << (64 - amount))); +#endif + } + +#if !defined(CCM_HAVE_INTRINSIC_INT128) + namespace int128_internal { + constexpr uint128 AddResult(uint128 result, uint128 lhs) { + // check for carry + return (Uint128Low64(result) < Uint128Low64(lhs)) + ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)) + : result; + } + } // namespace int128_internal +#endif + + constexpr uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) + + static_cast(rhs); +#else + return int128_internal::AddResult( + MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), + Uint128Low64(lhs) + Uint128Low64(rhs)), + lhs); +#endif + } + +#if !defined(CCM_HAVE_INTRINSIC_INT128) + namespace int128_internal { + constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) { + // check for carry + return (Uint128Low64(lhs) < Uint128Low64(rhs)) + ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)) + : result; + } + } // namespace int128_internal +#endif + + constexpr uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + return static_cast(lhs) - + static_cast(rhs); +#else + return int128_internal::SubstructResult( + MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), + Uint128Low64(lhs) - Uint128Low64(rhs)), + lhs, rhs); +#endif + } + + inline uint128 operator*(uint128 lhs, uint128 rhs) { +#if defined(CCM_HAVE_INTRINSIC_INT128) + // TODO(strel) Remove once alignment issues are resolved and unsigned __int128 + // can be used for uint128 storage. + return static_cast(lhs) * + static_cast(rhs); +#elif defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) + uint64_t carry; + uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); + return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + + Uint128High64(lhs) * Uint128Low64(rhs) + carry, + low); +#else // CCM_HAVE_INTRINSIC128 + uint64_t a32 = Uint128Low64(lhs) >> 32; + uint64_t a00 = Uint128Low64(lhs) & 0xffffffff; + uint64_t b32 = Uint128Low64(rhs) >> 32; + uint64_t b00 = Uint128Low64(rhs) & 0xffffffff; + uint128 result = + MakeUint128(Uint128High64(lhs) * Uint128Low64(rhs) + + Uint128Low64(lhs) * Uint128High64(rhs) + a32 * b32, + a00 * b00); + result += uint128(a32 * b00) << 32; + result += uint128(a00 * b32) << 32; + return result; +#endif // CCM_HAVE_INTRINSIC128 + } + +#if defined(CCM_HAVE_INTRINSIC_INT128) + inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast(lhs) / + static_cast(rhs); + } + + inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast(lhs) % + static_cast(rhs); + } +#endif + + // Increment/decrement operators. + + inline uint128 uint128::operator++(int) { + uint128 tmp(*this); + *this += 1; + return tmp; + } + + inline uint128 uint128::operator--(int) { + uint128 tmp(*this); + *this -= 1; + return tmp; + } + + inline uint128& uint128::operator++() { + *this += 1; + return *this; + } + + inline uint128& uint128::operator--() { + *this -= 1; + return *this; + } + + constexpr int128 MakeInt128(int64_t high, uint64_t low) { + return int128(high, low); + } + + // Assignment from integer types. + inline int128& int128::operator=(int v) { return *this = int128(v); } + + inline int128& int128::operator=(unsigned int v) { return *this = int128(v); } + + inline int128& int128::operator=(long v) { // NOLINT(runtime/int) + return *this = int128(v); + } + + // NOLINTNEXTLINE(runtime/int) + inline int128& int128::operator=(unsigned long v) { return *this = int128(v); } + + // NOLINTNEXTLINE(runtime/int) + inline int128& int128::operator=(long long v) { return *this = int128(v); } + + // NOLINTNEXTLINE(runtime/int) + inline int128& int128::operator=(unsigned long long v) { + return *this = int128(v); + } + + // Arithmetic operators. + constexpr int128 operator-(int128 v); + constexpr int128 operator+(int128 lhs, int128 rhs); + constexpr int128 operator-(int128 lhs, int128 rhs); + int128 operator*(int128 lhs, int128 rhs); + int128 operator/(int128 lhs, int128 rhs); + int128 operator%(int128 lhs, int128 rhs); + constexpr int128 operator|(int128 lhs, int128 rhs); + constexpr int128 operator&(int128 lhs, int128 rhs); + constexpr int128 operator^(int128 lhs, int128 rhs); + constexpr int128 operator<<(int128 lhs, int amount); + constexpr int128 operator>>(int128 lhs, int amount); + + inline int128& int128::operator+=(int128 other) { + *this = *this + other; + return *this; + } + + inline int128& int128::operator-=(int128 other) { + *this = *this - other; + return *this; + } + + inline int128& int128::operator*=(int128 other) { + *this = *this * other; + return *this; + } + + inline int128& int128::operator/=(int128 other) { + *this = *this / other; + return *this; + } + + inline int128& int128::operator%=(int128 other) { + *this = *this % other; + return *this; + } + + inline int128& int128::operator|=(int128 other) { + *this = *this | other; + return *this; + } + + inline int128& int128::operator&=(int128 other) { + *this = *this & other; + return *this; + } + + inline int128& int128::operator^=(int128 other) { + *this = *this ^ other; + return *this; + } + + inline int128& int128::operator<<=(int amount) { + *this = *this << amount; + return *this; + } + + inline int128& int128::operator>>=(int amount) { + *this = *this >> amount; + return *this; + } + + // Forward declaration for comparison operators. + constexpr bool operator!=(int128 lhs, int128 rhs); + + namespace int128_internal { + + // Casts from unsigned to signed while preserving the underlying binary + // representation. + constexpr int64_t BitCastToSigned(uint64_t v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang, GCC, and + // MSVC optimize this to a no-op on x86-64. + return v & (uint64_t{1} << 63) ? ~static_cast(~v) + : static_cast(v); + } + + } // namespace int128_internal + +#if defined(CCM_HAVE_INTRINSIC_INT128) + #include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export +#else // CCM_HAVE_INTRINSIC_INT128 + #include "absl/numeric/int128_no_intrinsic.inc" // IWYU pragma: export +#endif // CCM_HAVE_INTRINSIC_INT128 + +} // namespace absl + +#undef CCM_INTERNAL_WCHAR_T + diff --git a/include/ccmath/math/basic/fma.hpp b/include/ccmath/math/basic/fma.hpp index dc41d62..d35e632 100644 --- a/include/ccmath/math/basic/fma.hpp +++ b/include/ccmath/math/basic/fma.hpp @@ -8,6 +8,9 @@ #pragma once +#include "ccmath/math/basic/impl/fma_double_impl.hpp" +#include "ccmath/math/basic/impl/fma_float_impl.hpp" + #include "ccmath/math/compare/isinf.hpp" #include "ccmath/internal/predef/unlikely.hpp" #include "ccmath/math/compare/isnan.hpp" @@ -16,6 +19,13 @@ namespace ccm { + template , bool> = true> + inline constexpr T new_fma(T x, T y, T z) noexcept + { + if constexpr (std::is_same_v) { return ccm::internal::impl::fma_float_impl(x, y, z); } + else { return ccm::internal::impl::fma_double_impl(x, y, z); } + } + /** * @brief Fused multiply-add operation. * @tparam T Numeric type. diff --git a/include/ccmath/math/basic/impl/fma_double_impl.hpp b/include/ccmath/math/basic/impl/fma_double_impl.hpp new file mode 100644 index 0000000..f5fce92 --- /dev/null +++ b/include/ccmath/math/basic/impl/fma_double_impl.hpp @@ -0,0 +1,31 @@ +/* + * 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 "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include +#include + +namespace ccm::internal +{ + namespace + { + namespace impl + { + inline constexpr double fma_double_impl(double x, double y, double z) noexcept + { + +#pragma STDC FENV_ACCESS ON + + return 0; + } + } // namespace impl + } // namespace +} // namespace ccm::internal diff --git a/include/ccmath/math/basic/impl/fma_float_impl.hpp b/include/ccmath/math/basic/impl/fma_float_impl.hpp new file mode 100644 index 0000000..0214428 --- /dev/null +++ b/include/ccmath/math/basic/impl/fma_float_impl.hpp @@ -0,0 +1,28 @@ +/* +* 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 + +namespace ccm::internal +{ + namespace + { + namespace impl + { + inline constexpr double fma_float_impl(double x, double y, double z) noexcept + { + + + + + + return 0; + } + } // namespace impl + } // namespace +} // namespace ccm::internal diff --git a/include/ccmath/math/basic/impl/fma_ldouble_impl.hpp b/include/ccmath/math/basic/impl/fma_ldouble_impl.hpp new file mode 100644 index 0000000..f5fce92 --- /dev/null +++ b/include/ccmath/math/basic/impl/fma_ldouble_impl.hpp @@ -0,0 +1,31 @@ +/* + * 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 "ccmath/internal/predef/unlikely.hpp" +#include "ccmath/internal/support/bits.hpp" +#include +#include + +namespace ccm::internal +{ + namespace + { + namespace impl + { + inline constexpr double fma_double_impl(double x, double y, double z) noexcept + { + +#pragma STDC FENV_ACCESS ON + + return 0; + } + } // namespace impl + } // namespace +} // namespace ccm::internal diff --git a/include/ccmath/math/power/pow.hpp b/include/ccmath/math/power/pow.hpp index 469c11a..c7d9cf7 100644 --- a/include/ccmath/math/power/pow.hpp +++ b/include/ccmath/math/power/pow.hpp @@ -8,44 +8,19 @@ #pragma once +#include "ccmath/ccmath.hpp" +#include "ccmath/math/exponential/exp.hpp" +#include "ccmath/math/exponential/log.hpp" #include #include -#include - namespace ccm { - namespace - { - namespace detail - { - // check that exponent does not have a fractional part - template , bool> = true> - [[nodiscard]] - inline constexpr bool is_integral(T exponent) noexcept - { - return exponent == static_cast(static_cast(exponent)); - } - - //lookup table for 2^i - template - [[nodiscard]] - inline constexpr T two_to_pow_lookup_table(const T exponent) noexcept - { - constexpr T lookup[] = { - static_cast(1), static_cast(2), static_cast(4), static_cast(8), static_cast(16), static_cast(32), static_cast(64), static_cast(128), - static_cast(256), static_cast(512), static_cast(1024), static_cast(2048), static_cast(4096), static_cast(8192), static_cast(16384), static_cast(32768), - static_cast(65536), static_cast(131072), static_cast(262144), static_cast(524288), static_cast(1048576), static_cast(2097152), static_cast(4194304), static_cast(8388608), - static_cast(16777216), static_cast(33554432), static_cast(67108864), static_cast(134217728), static_cast(268435456), static_cast(536870912), static_cast(1073741824), static_cast(2147483648) - }; - - return lookup[exponent]; - } - - - - } // namespace detail - } // namespace + template + inline constexpr T pow(T x, T y) noexcept + { + return 0; + } } // namespace ccm diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fb307d7..4a1736b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -104,6 +104,16 @@ target_link_libraries(${PROJECT_NAME}-nearest PRIVATE gtest::gtest ) +add_executable(${PROJECT_NAME}-power) +target_sources(${PROJECT_NAME}-power PRIVATE + power/pow_test.cpp + +) +target_link_libraries(${PROJECT_NAME}-power PRIVATE + ccmath::test + gtest::gtest +) + add_executable(${PROJECT_NAME}-misc) # This is required as std::lerp is only available in versions of C++20 or greater. @@ -143,9 +153,10 @@ endif() add_test(NAME ${PROJECT_NAME}-basic COMMAND ${PROJECT_NAME}-basic) add_test(NAME ${PROJECT_NAME}-compare COMMAND ${PROJECT_NAME}-compare) -add_test(NAME ${PROJECT_NAME}-nearest COMMAND ${PROJECT_NAME}-nearest) add_test(NAME ${PROJECT_NAME}-exponential COMMAND ${PROJECT_NAME}-exponential) add_test(NAME ${PROJECT_NAME}-fmanip COMMAND ${PROJECT_NAME}-fmanip) +add_test(NAME ${PROJECT_NAME}-nearest COMMAND ${PROJECT_NAME}-nearest) +add_test(NAME ${PROJECT_NAME}-power COMMAND ${PROJECT_NAME}-power) add_test(NAME ${PROJECT_NAME}-misc COMMAND ${PROJECT_NAME}-misc) diff --git a/test/misc/lerp_test_no_std.cpp b/test/misc/lerp_test_no_std.cpp index f7b6a99..9c661be 100644 --- a/test/misc/lerp_test_no_std.cpp +++ b/test/misc/lerp_test_no_std.cpp @@ -13,6 +13,7 @@ #include +/* TEST(CcmathBasicTests, LerpStaticAssert) { static_assert(ccm::lerp(1, 2, 0.5) == 1.5, "lerp has failed testing that it is static_assert-able!"); @@ -39,3 +40,4 @@ TEST(CcmathBasicTests, LerpFloat) } // NOLINTEND } + */ diff --git a/test/power/pow_test.cpp b/test/power/pow_test.cpp new file mode 100644 index 0000000..cc7640d --- /dev/null +++ b/test/power/pow_test.cpp @@ -0,0 +1,29 @@ +/* +* 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. +*/ + +#include + +#include "ccmath/ccmath.hpp" +#include "ccmath/numbers.hpp" +#include +#include + +TEST(CcmathExponentialTests, Pow) +{ + /* + // ccm::exp(1.0) is equivalent to the mathematical constant e + static_assert(ccm::pow(2.0, 2.0) == 4, "ccm::pow is not working with static_assert!"); + + EXPECT_EQ(ccm::pow(2.0, 2.0), std::pow(2.0, 2.0)); + EXPECT_EQ(ccm::pow(2.0, 3.0), std::pow(2.0, 3.0)); + EXPECT_EQ(ccm::pow(2.0, 4.0), std::pow(2.0, 4.0)); + EXPECT_EQ(ccm::pow(2.0, 5.0), std::pow(2.0, 5.0)); + EXPECT_EQ(ccm::pow(2.0, 6.0), std::pow(2.0, 6.0)); +*/ + +}