// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2015 Benoit Steiner // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef EIGEN_CXX11_TENSOR_TENSOR_UINT128_H #define EIGEN_CXX11_TENSOR_TENSOR_UINT128_H namespace Eigen { namespace internal { template struct static_val { static const uint64_t value = n; EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE operator uint64_t() const { return n; } EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static_val() { } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static_val(const T& v) { eigen_assert(v == n); } }; template struct TensorUInt128 { HIGH high; LOW low; template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE TensorUInt128(const TensorUInt128& other) : high(other.high), low(other.low) { EIGEN_STATIC_ASSERT(sizeof(OTHER_HIGH) <= sizeof(HIGH), YOU_MADE_A_PROGRAMMING_MISTAKE); EIGEN_STATIC_ASSERT(sizeof(OTHER_LOW) <= sizeof(LOW), YOU_MADE_A_PROGRAMMING_MISTAKE); } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE TensorUInt128& operator = (const TensorUInt128& other) { EIGEN_STATIC_ASSERT(sizeof(OTHER_HIGH) <= sizeof(HIGH), YOU_MADE_A_PROGRAMMING_MISTAKE); EIGEN_STATIC_ASSERT(sizeof(OTHER_LOW) <= sizeof(LOW), YOU_MADE_A_PROGRAMMING_MISTAKE); high = other.high; low = other.low; return *this; } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE explicit TensorUInt128(const T& x) : high(0), low(x) { eigen_assert((static_cast::type>(x) <= NumTraits::highest())); eigen_assert(x >= 0); } EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE TensorUInt128(HIGH y, LOW x) : high(y), low(x) { } EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE operator LOW() const { return low; } EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE LOW lower() const { return low; } EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE HIGH upper() const { return high; } }; template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool operator == (const TensorUInt128& lhs, const TensorUInt128& rhs) { return (lhs.high == rhs.high) & (lhs.low == rhs.low); } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool operator != (const TensorUInt128& lhs, const TensorUInt128& rhs) { return (lhs.high != rhs.high) | (lhs.low != rhs.low); } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool operator >= (const TensorUInt128& lhs, const TensorUInt128& rhs) { if (lhs.high != rhs.high) { return lhs.high > rhs.high; } return lhs.low >= rhs.low; } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool operator < (const TensorUInt128& lhs, const TensorUInt128& rhs) { if (lhs.high != rhs.high) { return lhs.high < rhs.high; } return lhs.low < rhs.low; } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE TensorUInt128 operator + (const TensorUInt128& lhs, const TensorUInt128& rhs) { TensorUInt128 result(lhs.high + rhs.high, lhs.low + rhs.low); if (result.low < rhs.low) { result.high += 1; } return result; } template EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE TensorUInt128 operator - (const TensorUInt128& lhs, const TensorUInt128& rhs) { TensorUInt128 result(lhs.high - rhs.high, lhs.low - rhs.low); if (result.low > lhs.low) { result.high -= 1; } return result; } template static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorUInt128 operator * (const TensorUInt128& lhs, const TensorUInt128& rhs) { // Split each 128-bit integer into 4 32-bit integers, and then do the // multiplications by hand as follow: // lhs a b c d // rhs e f g h // ----------- // ah bh ch dh // bg cg dg // cf df // de // The result is stored in 2 64bit integers, high and low. const uint64_t LOW = 0x00000000FFFFFFFFLL; const uint64_t HIGH = 0xFFFFFFFF00000000LL; uint64_t d = lhs.low & LOW; uint64_t c = (lhs.low & HIGH) >> 32LL; uint64_t b = lhs.high & LOW; uint64_t a = (lhs.high & HIGH) >> 32LL; uint64_t h = rhs.low & LOW; uint64_t g = (rhs.low & HIGH) >> 32LL; uint64_t f = rhs.high & LOW; uint64_t e = (rhs.high & HIGH) >> 32LL; // Compute the low 32 bits of low uint64_t acc = d * h; uint64_t low = acc & LOW; // Compute the high 32 bits of low. Add a carry every time we wrap around acc >>= 32LL; uint64_t carry = 0; uint64_t acc2 = acc + c * h; if (acc2 < acc) { carry++; } acc = acc2 + d * g; if (acc < acc2) { carry++; } low |= (acc << 32LL); // Carry forward the high bits of acc to initiate the computation of the // low 32 bits of high acc2 = (acc >> 32LL) | (carry << 32LL); carry = 0; acc = acc2 + b * h; if (acc < acc2) { carry++; } acc2 = acc + c * g; if (acc2 < acc) { carry++; } acc = acc2 + d * f; if (acc < acc2) { carry++; } uint64_t high = acc & LOW; // Start to compute the high 32 bits of high. acc2 = (acc >> 32LL) | (carry << 32LL); acc = acc2 + a * h; acc2 = acc + b * g; acc = acc2 + c * f; acc2 = acc + d * e; high |= (acc2 << 32LL); return TensorUInt128(high, low); } template static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorUInt128 operator / (const TensorUInt128& lhs, const TensorUInt128& rhs) { if (rhs == TensorUInt128, static_val<1> >(1)) { return TensorUInt128(lhs.high, lhs.low); } else if (lhs < rhs) { return TensorUInt128(0); } else { // calculate the biggest power of 2 times rhs that's less than or equal to lhs TensorUInt128 power2(1); TensorUInt128 d(rhs); TensorUInt128 tmp(lhs - d); while (lhs >= d) { tmp = tmp - d; d = d + d; power2 = power2 + power2; } tmp = TensorUInt128(lhs.high, lhs.low); TensorUInt128 result(0); while (power2 != TensorUInt128, static_val<0> >(0)) { if (tmp >= d) { tmp = tmp - d; result = result + power2; } // Shift right power2 = TensorUInt128(power2.high >> 1, (power2.low >> 1) | (power2.high << 63)); d = TensorUInt128(d.high >> 1, (d.low >> 1) | (d.high << 63)); } return result; } } } // namespace internal } // namespace Eigen #endif // EIGEN_CXX11_TENSOR_TENSOR_UINT128_H