Skip to content

Commit 2380fbe

Browse files
authored
umod improvements (#116)
1 parent 7bc1091 commit 2380fbe

1 file changed

Lines changed: 94 additions & 45 deletions

File tree

include/CppCore/Math/Util.h

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3496,19 +3496,29 @@ namespace CppCore
34963496
/// Modulo for any sized unsigned integers.
34973497
/// Based on Knuth's Algorithm in Hacker's Delight. (r=u%v)
34983498
/// </summary>
3499-
template<typename UINT1, typename UINT2, typename MEM>
3500-
INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v, MEM& mem)
3499+
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
3500+
INLINE static void umod(UINT3& r, const UINT1& u, const UINT2& v, MEM& mem)
35013501
{
3502-
static_assert(sizeof(UINT1) != 0 && sizeof(UINT2) != 0 && sizeof(MEM) != 0);
3503-
if constexpr (sizeof(UINT1) < sizeof(size_t))
3502+
static_assert(sizeof(UINT1) != 0 && sizeof(UINT2) != 0 && sizeof(UINT3) != 0 && sizeof(MEM) != 0);
3503+
if constexpr (sizeof(UINT3) < sizeof(size_t))
3504+
{
3505+
size_t tr;
3506+
CppCore::umod(tr, u, v, mem);
3507+
CppCore::clone(r, *(UINT3*)&tr);
3508+
}
3509+
else if constexpr (sizeof(UINT1) < sizeof(size_t))
35043510
{
35053511
CppCore::umod(r, (size_t)u, v, mem);
35063512
}
35073513
else if constexpr (sizeof(UINT2) < sizeof(size_t))
35083514
{
3509-
size_t tr;
3510-
CppCore::umod(tr, u, (size_t)v, mem);
3511-
CppCore::clone(r, *(UINT2*)&tr);
3515+
CppCore::umod(r, u, (size_t)v, mem);
3516+
}
3517+
else if constexpr (sizeof(UINT3) % sizeof(size_t) != 0)
3518+
{
3519+
Padded<UINT3> tr;
3520+
CppCore::umod(tr, u, v, mem);
3521+
CppCore::clone(r, tr.v);
35123522
}
35133523
else if constexpr (sizeof(UINT1) % sizeof(size_t) != 0)
35143524
{
@@ -3518,31 +3528,35 @@ namespace CppCore
35183528
else if constexpr (sizeof(UINT2) % sizeof(size_t) != 0)
35193529
{
35203530
Padded<UINT2> tv(v);
3521-
Padded<UINT2> tr;
3522-
CppCore::umod(tr, u, tv, mem);
3523-
CppCore::clone(r, tr.v);
3531+
CppCore::umod(r, u, tv, mem);
35243532
}
35253533
#if defined(CPPCORE_CPU_X64)
3526-
else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) == 8)
3534+
else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) == 8 && sizeof(UINT3) % 8 == 0)
35273535
{
3536+
if constexpr (sizeof(UINT3) > 8)
3537+
CppCore::clear(r);
35283538
constexpr uint32_t N64 = sizeof(UINT1) / 8;
35293539
*(uint64_t*)&r = CppCore::umod128_64x((uint64_t*)&u, v, N64);
35303540
}
35313541
#endif
3532-
else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) == 4)
3542+
else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) == 4 && sizeof(UINT3) % 4 == 0)
35333543
{
3544+
if constexpr (sizeof(UINT3) > 4)
3545+
CppCore::clear(r);
35343546
constexpr uint32_t N32 = sizeof(UINT1) / 4;
35353547
*(uint32_t*)&r = CppCore::umod64_32x((uint32_t*)&u, v, N32);
35363548
}
35373549
#if defined(CPPCORE_CPU_X64)
3538-
else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) % 8 == 0)
3550+
else if constexpr (sizeof(UINT1) % 8 == 0 && sizeof(UINT2) % 8 == 0 && sizeof(UINT3) % 8 == 0)
35393551
{
35403552
// using 64-bit chunks
3541-
assert(&r != &v);
3553+
assert((void*)&r != (void*)&v);
3554+
assert((void*)&r != (void*)&u);
3555+
static_assert(sizeof(UINT3) >= MIN(sizeof(UINT1), sizeof(UINT2)));
35423556
static_assert(sizeof(MEM) >= sizeof(UINT1) + 8U);
3543-
static_assert(sizeof(UINT1) >= sizeof(UINT2));
35443557
static_assert(alignof(MEM) >= alignof(UINT1));
35453558
static_assert(alignof(MEM) >= alignof(UINT2));
3559+
static_assert(alignof(MEM) >= alignof(UINT3));
35463560
constexpr uint32_t M = sizeof(UINT1) / 8;
35473561
constexpr uint32_t N = sizeof(UINT2) / 8;
35483562
uint64_t* rp = (uint64_t*)&r;
@@ -3552,23 +3566,31 @@ namespace CppCore
35523566
uint64_t* vno;
35533567
uint64_t* vne;
35543568
uint32_t n = N;
3569+
uint32_t m = M;
3570+
CppCore::clear(r);
35553571
while (n != 0U && vp[n-1] == 0U)
35563572
n--;
35573573
if (n == 0U)
35583574
return;
3575+
while (m != 0U && up[m-1] == 0U)
3576+
m--;
3577+
if (n > m) {
3578+
if constexpr (sizeof(UINT3) <= sizeof(UINT1))
3579+
CppCore::clone(r, *(UINT3*)&u);
3580+
else CppCore::clone(*(UINT1*)&r, u);
3581+
return;
3582+
}
35593583
if (n == 1U) {
3560-
CppCore::clear(r);
3561-
*rp = CppCore::umod128_64x(up, *vp, M);
3584+
*rp = CppCore::umod128_64x(up, *vp, m);
35623585
return;
35633586
}
35643587
const auto S((uint8_t)CppCore::lzcnt(vp[n-1]));
35653588
if (S) {
3566-
CppCore::clear(r);
35673589
CppCore::shl64x(vp, rp, n, S);
35683590
vno = rp;
35693591
vne = &rp[n];
3570-
unp[M] = up[M-1] >> (64U-S);
3571-
CppCore::shl64x(up, unp, M, S);
3592+
unp[m] = up[m-1] >> (64U-S);
3593+
CppCore::shl64x(up, unp, m, S);
35723594
}
35733595
else {
35743596
vno = vp;
@@ -3578,7 +3600,7 @@ namespace CppCore
35783600
}
35793601
const auto VNN1 = vno[n-1];
35803602
const auto VNN2 = vno[n-2];
3581-
for (uint32_t j = M-n; j != UINT32_MAX; j--)
3603+
for (uint32_t j = m-n; j != UINT32_MAX; j--)
35823604
{
35833605
auto* unpjo = &unp[j];
35843606
auto* unpj = unpjo;
@@ -3632,17 +3654,23 @@ namespace CppCore
36323654
*unpj = kl;
36333655
}
36343656
if (S) { CppCore::shr64x(unp, rp, n, S); }
3635-
else { CppCore::clone(r, *(UINT2*)&mem); }
3657+
else {
3658+
if constexpr (sizeof(UINT3) <= sizeof(UINT1))
3659+
CppCore::clone(r, *(UINT3*)&mem);
3660+
else CppCore::clone(*(UINT1*)&r, *(UINT1*)&mem);
3661+
}
36363662
}
3637-
else
36383663
#endif
3664+
else if constexpr (sizeof(UINT1) % 4 == 0 && sizeof(UINT2) % 4 == 0 && sizeof(UINT3) % 4 == 0)
36393665
{
36403666
// using 32-bit chunks
3641-
assert(&r != &v);
3667+
assert((void*)&r != (void*)&v);
3668+
assert((void*)&r != (void*)&u);
3669+
static_assert(sizeof(UINT3) >= MIN(sizeof(UINT1), sizeof(UINT2)));
36423670
static_assert(sizeof(MEM) >= sizeof(UINT1) + 4U);
3643-
static_assert(sizeof(UINT1) >= sizeof(UINT2));
36443671
static_assert(alignof(MEM) >= alignof(UINT1));
36453672
static_assert(alignof(MEM) >= alignof(UINT2));
3673+
static_assert(alignof(MEM) >= alignof(UINT3));
36463674
constexpr uint32_t M = sizeof(UINT1) / 4;
36473675
constexpr uint32_t N = sizeof(UINT2) / 4;
36483676
uint32_t* rp = (uint32_t*)&r;
@@ -3652,23 +3680,31 @@ namespace CppCore
36523680
uint32_t* vno;
36533681
uint32_t* vne;
36543682
uint32_t n = N;
3683+
uint32_t m = M;
3684+
CppCore::clear(r);
36553685
while (n != 0U && vp[n-1] == 0U)
36563686
n--;
36573687
if (n == 0U)
36583688
return;
3689+
while (m != 0U && up[m-1] == 0U)
3690+
m--;
3691+
if (n > m) {
3692+
if constexpr (sizeof(UINT3) <= sizeof(UINT1))
3693+
CppCore::clone(r, *(UINT3*)&u);
3694+
else CppCore::clone(*(UINT1*)&r, u);
3695+
return;
3696+
}
36593697
if (n == 1U) {
3660-
CppCore::clear(r);
3661-
*rp = CppCore::umod64_32x(up, *vp, M);
3698+
*rp = CppCore::umod64_32x(up, *vp, m);
36623699
return;
36633700
}
36643701
const auto S((uint8_t)CppCore::lzcnt(vp[n-1]));
36653702
if (S) {
3666-
CppCore::clear(r);
36673703
CppCore::shl32x(vp, rp, n, S);
36683704
vno = rp;
36693705
vne = &rp[n];
3670-
unp[M] = up[M-1] >> (32U-S);
3671-
CppCore::shl32x(up, unp, M, S);
3706+
unp[m] = up[m-1] >> (32U-S);
3707+
CppCore::shl32x(up, unp, m, S);
36723708
}
36733709
else {
36743710
vno = vp;
@@ -3678,7 +3714,7 @@ namespace CppCore
36783714
}
36793715
const auto VNN1 = vno[n-1];
36803716
const auto VNN2 = vno[n-2];
3681-
for (uint32_t j = M-n; j != UINT32_MAX; j--)
3717+
for (uint32_t j = m-n; j != UINT32_MAX; j--)
36823718
{
36833719
auto* unpjo = &unp[j];
36843720
auto* unpj = unpjo;
@@ -3732,17 +3768,22 @@ namespace CppCore
37323768
*unpj = kl;
37333769
}
37343770
if (S) { CppCore::shr32x(unp, rp, n, S); }
3735-
else { CppCore::clone(r, *(UINT2*)&mem); }
3771+
else {
3772+
if constexpr (sizeof(UINT3) <= sizeof(UINT1))
3773+
CppCore::clone(r, *(UINT3*)&mem);
3774+
else CppCore::clone(*(UINT1*)&r, *(UINT1*)&mem);
3775+
}
37363776
}
3777+
else assert(false);
37373778
}
37383779

37393780
/// <summary>
37403781
/// Like other variant but using temporary stack memory
37413782
/// </summary>
3742-
template<typename UINT1, typename UINT2>
3743-
INLINE static void umod(UINT2& r, const UINT1& u, const UINT2& v)
3783+
template<typename UINT1, typename UINT2, typename UINT3>
3784+
INLINE static void umod(UINT3& r, const UINT1& u, const UINT2& v)
37443785
{
3745-
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), alignof(UINT2)))) MEM {
3786+
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM {
37463787
Padded<UINT1> x;
37473788
size_t p;
37483789
};
@@ -3774,6 +3815,14 @@ namespace CppCore
37743815
r = u % v;
37753816
}
37763817

3818+
/// <summary>
3819+
/// 64%32=64
3820+
/// </summary>
3821+
template<> INLINE void umod(uint64_t& r, const uint64_t& u, const uint32_t& v)
3822+
{
3823+
r = u % v;
3824+
}
3825+
37773826
/// <summary>
37783827
/// TODO: Special Version for High Bit on Divisor set.
37793828
/// </summary>
@@ -3930,8 +3979,8 @@ namespace CppCore
39303979
/// <summary>
39313980
/// a*b mod m. For any sized integers that are multiples of 32-bit.
39323981
/// </summary>
3933-
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
3934-
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r, MEM& mem)
3982+
template<typename UINT1, typename UINT2, typename UINT3, typename UINT4, typename MEM>
3983+
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT4& r, MEM& mem)
39353984
{
39363985
static_assert(sizeof(MEM) >= sizeof(Padded<UINT1>) + sizeof(Padded<UINT2>) + sizeof(size_t));
39373986
struct UINTX2 { UINT1 a; UINT2 b; };
@@ -3942,10 +3991,10 @@ namespace CppCore
39423991
/// <summary>
39433992
/// a*b mod m. For any sized integers that are multiples of 32-bit.
39443993
/// </summary>
3945-
template<typename UINT1, typename UINT2, typename UINT3>
3946-
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT3& r)
3994+
template<typename UINT1, typename UINT2, typename UINT3, typename UINT4>
3995+
INLINE static void umulmod(const UINT1& a, const UINT2& b, const UINT3& m, UINT4& r)
39473996
{
3948-
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM {
3997+
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), MAX(alignof(UINT3), alignof(UINT4)))))) MEM {
39493998
Padded<UINT1> a;
39503999
Padded<UINT2> b;
39514000
size_t p;
@@ -4020,8 +4069,8 @@ namespace CppCore
40204069
/// <summary>
40214070
/// a*a mod m. For any sized integers that are multiples of 32-bit.
40224071
/// </summary>
4023-
template<typename UINT1, typename UINT2, typename MEM>
4024-
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r, MEM& mem)
4072+
template<typename UINT1, typename UINT2, typename UINT3, typename MEM>
4073+
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT3& r, MEM& mem)
40254074
{
40264075
static_assert(sizeof(MEM) >= sizeof(Padded<UINT1>) + sizeof(Padded<UINT1>) + sizeof(size_t));
40274076
struct UINTX2 { UINT1 a1; UINT1 a2; };
@@ -4032,10 +4081,10 @@ namespace CppCore
40324081
/// <summary>
40334082
/// a*a mod m. For any sized integers that are multiples of 32-bit.
40344083
/// </summary>
4035-
template<typename UINT1, typename UINT2>
4036-
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT2& r)
4084+
template<typename UINT1, typename UINT2, typename UINT3>
4085+
INLINE static void usquaremod(const UINT1& a, const UINT2& m, UINT3& r)
40374086
{
4038-
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), alignof(UINT2)))) MEM {
4087+
struct alignas(MAX(alignof(size_t), MAX(alignof(UINT1), MAX(alignof(UINT2), alignof(UINT3))))) MEM {
40394088
Padded<UINT1> a1;
40404089
Padded<UINT1> a2;
40414090
size_t p;

0 commit comments

Comments
 (0)