Skip to content

Commit 29d9761

Browse files
authored
Optimize ((a mod modulus) + modulus) mod modulo (#93)
1 parent 88dfbd9 commit 29d9761

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

src/bigints.nim

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,14 @@ iterator `..<`*(a, b: BigInt): BigInt =
10681068
yield res
10691069
inc res
10701070

1071+
1072+
func modulo(a, modulus: BigInt): BigInt =
1073+
## Like `mod`, but the result is always in the range `[0, modulus-1]`.
1074+
## `modulus` should be greater than zero.
1075+
result = a mod modulus
1076+
if result < 0:
1077+
result += modulus
1078+
10711079
func fastLog2*(a: BigInt): int =
10721080
## Computes the logarithm in base 2 of `a`.
10731081
## If `a` is negative, returns the logarithm of `abs(a)`.
@@ -1076,6 +1084,7 @@ func fastLog2*(a: BigInt): int =
10761084
return -1
10771085
bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high)
10781086

1087+
10791088
func invmod*(a, modulus: BigInt): BigInt =
10801089
## Compute the modular inverse of `a` modulo `modulus`.
10811090
## The return value is always in the range `[1, modulus-1]`
@@ -1091,23 +1100,22 @@ func invmod*(a, modulus: BigInt): BigInt =
10911100
raise newException(DivByZeroDefect, "0 has no modular inverse")
10921101
else:
10931102
var
1094-
r0 = ((a mod modulus) + modulus) mod modulus
1095-
r1 = modulus
1096-
s0 = one
1097-
s1 = zero
1103+
r0 = modulus
1104+
r1 = a.modulo(modulus)
1105+
t0 = zero
1106+
t1 = one
1107+
var rk, tk: BigInt # otherwise t1 is incorrectly inferred as cursor (https://github.com/nim-lang/Nim/issues/19457)
10981108
while r1 > 0:
1099-
let
1100-
q = r0 div r1
1101-
# the `q.isZero` check is needed because of an ARC/ORC bug (see https://github.com/nim-lang/bigints/issues/88)
1102-
rk = if q.isZero: r0 else: r0 - q * r1
1103-
sk = if q.isZero: s0 else: s0 - q * s1
1109+
let q = r0 div r1
1110+
rk = r0 - q * r1
1111+
tk = t0 - q * t1
11041112
r0 = r1
11051113
r1 = rk
1106-
s0 = s1
1107-
s1 = sk
1114+
t0 = t1
1115+
t1 = tk
11081116
if r0 != one:
11091117
raise newException(ValueError, $a & " has no modular inverse modulo " & $modulus)
1110-
result = ((s0 mod modulus) + modulus) mod modulus
1118+
result = t0.modulo(modulus)
11111119

11121120
func powmod*(base, exponent, modulus: BigInt): BigInt =
11131121
## Compute modular exponentation of `base` with power `exponent` modulo `modulus`.
@@ -1127,8 +1135,7 @@ func powmod*(base, exponent, modulus: BigInt): BigInt =
11271135
if exponent < 0:
11281136
base = invmod(base, modulus)
11291137
exponent = -exponent
1130-
var
1131-
basePow = ((base mod modulus) + modulus) mod modulus # Base stays in [0, m-1]
1138+
var basePow = base.modulo(modulus)
11321139
result = one
11331140
while not exponent.isZero:
11341141
if (exponent.limbs[0] and 1) != 0:

0 commit comments

Comments
 (0)