diff --git a/include/CppCore/Math/Util.h b/include/CppCore/Math/Util.h index 2294354a..74d3784f 100644 --- a/include/CppCore/Math/Util.h +++ b/include/CppCore/Math/Util.h @@ -3496,19 +3496,29 @@ namespace CppCore /// Modulo for any sized unsigned integers. /// Based on Knuth's Algorithm in Hacker's Delight. (r=u%v) /// - template - INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v, MEM& mem) + template + INLINE static void umod(UINT3& r, const UINT1& u, const UINT2& v, MEM& mem) { - static_assert(sizeof(UINT1) != 0 && sizeof(UINT2) != 0 && sizeof(MEM) != 0); - if constexpr (sizeof(UINT1) < sizeof(size_t)) + static_assert(sizeof(UINT1) != 0 && sizeof(UINT2) != 0 && sizeof(UINT3) != 0 && sizeof(MEM) != 0); + if constexpr (sizeof(UINT3) < sizeof(size_t)) + { + size_t tr; + CppCore::umod(tr, u, v, mem); + CppCore::clone(r, *(UINT3*)&tr); + } + else if constexpr (sizeof(UINT1) < sizeof(size_t)) { CppCore::umod(r, (size_t)u, v, mem); } else if constexpr (sizeof(UINT2) < sizeof(size_t)) { - size_t tr; - CppCore::umod(tr, u, (size_t)v, mem); - CppCore::clone(r, *(UINT2*)&tr); + CppCore::umod(r, u, (size_t)v, mem); + } + else if constexpr (sizeof(UINT3) % sizeof(size_t) != 0) + { + Padded tr; + CppCore::umod(tr, u, v, mem); + CppCore::clone(r, tr.v); } else if constexpr (sizeof(UINT1) % sizeof(size_t) != 0) { @@ -3518,31 +3528,35 @@ namespace CppCore else if constexpr (sizeof(UINT2) % sizeof(size_t) != 0) { Padded tv(v); - Padded tr; - CppCore::umod(tr, u, tv, mem); - CppCore::clone(r, tr.v); + CppCore::umod(r, u, tv, mem); } #if defined(CPPCORE_CPU_X64) - else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) == 8) + else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) == 8 && sizeof(UINT3) % 8 == 0) { + if constexpr (sizeof(UINT3) > 8) + CppCore::clear(r); constexpr uint32_t N64 = sizeof(UINT1) / 8; *(uint64_t*)&r = CppCore::umod128_64x((uint64_t*)&u, v, N64); } #endif - else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) == 4) + else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) == 4 && sizeof(UINT3) % 4 == 0) { + if constexpr (sizeof(UINT3) > 4) + CppCore::clear(r); constexpr uint32_t N32 = sizeof(UINT1) / 4; *(uint32_t*)&r = CppCore::umod64_32x((uint32_t*)&u, v, N32); } #if defined(CPPCORE_CPU_X64) - else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) % 8 == 0) + else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) % 8 == 0 && sizeof(UINT3) % 8 == 0) { // using 64-bit chunks - assert(&r != &v); + assert((void*)&r != (void*)&v); + assert((void*)&r != (void*)&u); + static_assert(sizeof(UINT3) >= MIN(sizeof(UINT1), sizeof(UINT2))); static_assert(sizeof(MEM) >= sizeof(UINT1) + 8U); - static_assert(sizeof(UINT1) >= sizeof(UINT2)); static_assert(alignof(MEM) >= alignof(UINT1)); static_assert(alignof(MEM) >= alignof(UINT2)); + static_assert(alignof(MEM) >= alignof(UINT3)); constexpr uint32_t M = sizeof(UINT1) / 8; constexpr uint32_t N = sizeof(UINT2) / 8; uint64_t* rp = (uint64_t*)&r; @@ -3552,23 +3566,31 @@ namespace CppCore uint64_t* vno; uint64_t* vne; uint32_t n = N; + uint32_t m = M; + CppCore::clear(r); while (n != 0U && vp[n-1] == 0U) n--; if (n == 0U) return; + while (m != 0U && up[m-1] == 0U) + m--; + if (n > m) { + if constexpr (sizeof(UINT3) <= sizeof(UINT1)) + CppCore::clone(r, *(UINT3*)&u); + else CppCore::clone(*(UINT1*)&r, u); + return; + } if (n == 1U) { - CppCore::clear(r); - *rp = CppCore::umod128_64x(up, *vp, M); + *rp = CppCore::umod128_64x(up, *vp, m); return; } const auto S((uint8_t)CppCore::lzcnt(vp[n-1])); if (S) { - CppCore::clear(r); CppCore::shl64x(vp, rp, n, S); vno = rp; vne = &rp[n]; - unp[M] = up[M-1] >> (64U-S); - CppCore::shl64x(up, unp, M, S); + unp[m] = up[m-1] >> (64U-S); + CppCore::shl64x(up, unp, m, S); } else { vno = vp; @@ -3578,7 +3600,7 @@ namespace CppCore } const auto VNN1 = vno[n-1]; const auto VNN2 = vno[n-2]; - for (uint32_t j = M-n; j != UINT32_MAX; j--) + for (uint32_t j = m-n; j != UINT32_MAX; j--) { auto* unpjo = &unp[j]; auto* unpj = unpjo; @@ -3632,17 +3654,23 @@ namespace CppCore *unpj = kl; } if (S) { CppCore::shr64x(unp, rp, n, S); } - else { CppCore::clone(r, *(UINT2*)&mem); } + else { + if constexpr (sizeof(UINT3) <= sizeof(UINT1)) + CppCore::clone(r, *(UINT3*)&mem); + else CppCore::clone(*(UINT1*)&r, *(UINT1*)&mem); + } } - else #endif + else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) % 4 == 0 && sizeof(UINT3) % 4 == 0) { // using 32-bit chunks - assert(&r != &v); + assert((void*)&r != (void*)&v); + assert((void*)&r != (void*)&u); + static_assert(sizeof(UINT3) >= MIN(sizeof(UINT1), sizeof(UINT2))); static_assert(sizeof(MEM) >= sizeof(UINT1) + 4U); - static_assert(sizeof(UINT1) >= sizeof(UINT2)); static_assert(alignof(MEM) >= alignof(UINT1)); static_assert(alignof(MEM) >= alignof(UINT2)); + static_assert(alignof(MEM) >= alignof(UINT3)); constexpr uint32_t M = sizeof(UINT1) / 4; constexpr uint32_t N = sizeof(UINT2) / 4; uint32_t* rp = (uint32_t*)&r; @@ -3652,23 +3680,31 @@ namespace CppCore uint32_t* vno; uint32_t* vne; uint32_t n = N; + uint32_t m = M; + CppCore::clear(r); while (n != 0U && vp[n-1] == 0U) n--; if (n == 0U) return; + while (m != 0U && up[m-1] == 0U) + m--; + if (n > m) { + if constexpr (sizeof(UINT3) <= sizeof(UINT1)) + CppCore::clone(r, *(UINT3*)&u); + else CppCore::clone(*(UINT1*)&r, u); + return; + } if (n == 1U) { - CppCore::clear(r); - *rp = CppCore::umod64_32x(up, *vp, M); + *rp = CppCore::umod64_32x(up, *vp, m); return; } const auto S((uint8_t)CppCore::lzcnt(vp[n-1])); if (S) { - CppCore::clear(r); CppCore::shl32x(vp, rp, n, S); vno = rp; vne = &rp[n]; - unp[M] = up[M-1] >> (32U-S); - CppCore::shl32x(up, unp, M, S); + unp[m] = up[m-1] >> (32U-S); + CppCore::shl32x(up, unp, m, S); } else { vno = vp; @@ -3678,7 +3714,7 @@ namespace CppCore } const auto VNN1 = vno[n-1]; const auto VNN2 = vno[n-2]; - for (uint32_t j = M-n; j != UINT32_MAX; j--) + for (uint32_t j = m-n; j != UINT32_MAX; j--) { auto* unpjo = &unp[j]; auto* unpj = unpjo; @@ -3732,17 +3768,22 @@ namespace CppCore *unpj = kl; } if (S) { CppCore::shr32x(unp, rp, n, S); } - else { CppCore::clone(r, *(UINT2*)&mem); } + else { + if constexpr (sizeof(UINT3) <= sizeof(UINT1)) + CppCore::clone(r, *(UINT3*)&mem); + else CppCore::clone(*(UINT1*)&r, *(UINT1*)&mem); + } } + else assert(false); } /// /// Like other variant but using temporary stack memory /// - template - INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v) + template + INLINE static void umod(UINT3& r, const UINT1& u, const UINT2& v) { - struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), alignof(UINT2)))) MEM { + struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM { Padded x; size_t p; }; @@ -3774,6 +3815,14 @@ namespace CppCore r = u % v; } + /// + /// 64%32=64 + /// + template<> INLINE void umod(uint64_t& r, const uint64_t& u, const uint32_t& v) + { + r = u % v; + } + /// /// TODO: Special Version for High Bit on Divisor set. /// @@ -3930,8 +3979,8 @@ namespace CppCore /// /// a*b mod m. For any sized integers that are multiples of 32-bit. /// - template - INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r, MEM& mem) + template + INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT4& r, MEM& mem) { static_assert(sizeof(MEM) >= sizeof(Padded) + sizeof(Padded) + sizeof(size_t)); struct UINTX2 { UINT1 a; UINT2 b; }; @@ -3942,10 +3991,10 @@ namespace CppCore /// /// a*b mod m. For any sized integers that are multiples of 32-bit. /// - template - INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r) + template + INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT4& r) { - struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM { + struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), MAX(alignof(UINT3), alignof(UINT4)))))) MEM { Padded a; Padded b; size_t p; @@ -4020,8 +4069,8 @@ namespace CppCore /// /// a*a mod m. For any sized integers that are multiples of 32-bit. /// - template - INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r, MEM& mem) + template + INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT3& r, MEM& mem) { static_assert(sizeof(MEM) >= sizeof(Padded) + sizeof(Padded) + sizeof(size_t)); struct UINTX2 { UINT1 a1; UINT1 a2; }; @@ -4032,10 +4081,10 @@ namespace CppCore /// /// a*a mod m. For any sized integers that are multiples of 32-bit. /// - template - INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r) + template + INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT3& r) { - struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), alignof(UINT2)))) MEM { + struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM { Padded a1; Padded a2; size_t p;