Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 94 additions & 45 deletions include/CppCore/Math/Util.h
Original file line number Diff line number Diff line change
Expand Up @@ -3496,19 +3496,29 @@ namespace CppCore
/// Modulo for any sized unsigned integers.
/// Based on Knuth's Algorithm in Hacker's Delight. (r=u%v)
/// </summary>
template<typename UINT1, typename UINT2, typename MEM>
INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v, MEM& mem)
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
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<UINT3> tr;
CppCore::umod(tr, u, v, mem);
CppCore::clone(r, tr.v);
}
else if constexpr (sizeof(UINT1) % sizeof(size_t) != 0)
{
Expand All @@ -3518,31 +3528,35 @@ namespace CppCore
else if constexpr (sizeof(UINT2) % sizeof(size_t) != 0)
{
Padded<UINT2> tv(v);
Padded<UINT2> 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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

/// <summary>
/// Like other variant but using temporary stack memory
/// </summary>
template<typename UINT1, typename UINT2>
INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v)
template<typename UINT1, typename UINT2, typename UINT3>
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<UINT1> x;
size_t p;
};
Expand Down Expand Up @@ -3774,6 +3815,14 @@ namespace CppCore
r = u % v;
}

/// <summary>
/// 64%32=64
/// </summary>
template<> INLINE void umod(uint64_t& r, const uint64_t& u, const uint32_t& v)
{
r = u % v;
}

/// <summary>
/// TODO: Special Version for High Bit on Divisor set.
/// </summary>
Expand Down Expand Up @@ -3930,8 +3979,8 @@ namespace CppCore
/// <summary>
/// a*b mod m. For any sized integers that are multiples of 32-bit.
/// </summary>
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r, MEM& mem)
template<typename UINT1, typename UINT2, typename UINT3, typename UINT4, typename MEM>
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT4& r, MEM& mem)
{
static_assert(sizeof(MEM) >= sizeof(Padded<UINT1>) + sizeof(Padded<UINT2>) + sizeof(size_t));
struct UINTX2 { UINT1 a; UINT2 b; };
Expand All @@ -3942,10 +3991,10 @@ namespace CppCore
/// <summary>
/// a*b mod m. For any sized integers that are multiples of 32-bit.
/// </summary>
template<typename UINT1, typename UINT2, typename UINT3>
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r)
template<typename UINT1, typename UINT2, typename UINT3, typename UINT4>
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<UINT1> a;
Padded<UINT2> b;
size_t p;
Expand Down Expand Up @@ -4020,8 +4069,8 @@ namespace CppCore
/// <summary>
/// a*a mod m. For any sized integers that are multiples of 32-bit.
/// </summary>
template<typename UINT1, typename UINT2, typename MEM>
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r, MEM& mem)
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT3& r, MEM& mem)
{
static_assert(sizeof(MEM) >= sizeof(Padded<UINT1>) + sizeof(Padded<UINT1>) + sizeof(size_t));
struct UINTX2 { UINT1 a1; UINT1 a2; };
Expand All @@ -4032,10 +4081,10 @@ namespace CppCore
/// <summary>
/// a*a mod m. For any sized integers that are multiples of 32-bit.
/// </summary>
template<typename UINT1, typename UINT2>
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r)
template<typename UINT1, typename UINT2, typename UINT3>
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<UINT1> a1;
Padded<UINT1> a2;
size_t p;
Expand Down
Loading