Skip to content

Commit 9e4efd6

Browse files
committed
Merge branch 'master' into karatsuba
2 parents f2a48e2 + 13c3c33 commit 9e4efd6

File tree

5 files changed

+384
-34
lines changed

5 files changed

+384
-34
lines changed

bigints.nimble

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ requires "nim >= 1.4.0"
1414
task test, "Test bigints":
1515
for backend in ["c", "cpp"]:
1616
echo "testing " & backend & " backend"
17-
for file in ["tbigints.nim", "tbugs.nim"]:
18-
exec "nim r --hints:off --experimental:strictFuncs --backend:" & backend & " tests/" & file
17+
for gc in ["refc", "arc", "orc"]:
18+
echo " using " & gc & " GC"
19+
for file in ["tbigints.nim", "tbugs.nim"]:
20+
exec "nim r --hints:off --experimental:strictFuncs --backend:" & backend & " --gc:" & gc & " tests/" & file

examples/rc_modexp.nim

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/bigints.nim

Lines changed: 181 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
## Arbitrary precision integers.
22

3-
4-
import std/[algorithm, bitops, math]
3+
import std/[algorithm, bitops, math, options]
54

65
type
76
BigInt* = object
@@ -73,6 +72,14 @@ func isZero(a: BigInt): bool {.inline.} =
7372
return false
7473
return true
7574

75+
func abs*(a: BigInt): BigInt =
76+
# Returns the absolute value of `a`.
77+
runnableExamples:
78+
assert abs(42.initBigInt) == 42.initBigInt
79+
assert abs(-12.initBigInt) == 12.initBigInt
80+
result = a
81+
result.isNegative = false
82+
7683
func unsignedCmp(a: BigInt, b: uint32): int64 =
7784
# ignores the sign of `a`
7885
# `a` and `b` are assumed to not be zero
@@ -563,7 +570,7 @@ func `shl`*(x: BigInt, y: Natural): BigInt =
563570
result.limbs.add(uint32(carry shr (32 - b)))
564571

565572
# forward declaration for use in `shr`
566-
func dec*(a: var BigInt, b: int32 = 1)
573+
func dec*(a: var BigInt, b: int = 1)
567574

568575
func `shr`*(x: BigInt, y: Natural): BigInt =
569576
## Shifts a `BigInt` to the right (arithmetically).
@@ -826,7 +833,7 @@ func `mod`*(a, b: BigInt): BigInt =
826833
var tmp: BigInt
827834
division(tmp, result, a, b)
828835

829-
func `divmod`*(a, b: BigInt): tuple[q, r: BigInt] =
836+
func divmod*(a, b: BigInt): tuple[q, r: BigInt] =
830837
## Computes both the integer division and modulo (remainder) of two
831838
## `BigInt` numbers.
832839
## Raises a `DivByZeroDefect` if `b` is zero.
@@ -837,6 +844,92 @@ func `divmod`*(a, b: BigInt): tuple[q, r: BigInt] =
837844
assert divmod(a, b) == (3.initBigInt, 2.initBigInt)
838845
division(result.q, result.r, a, b)
839846

847+
func countTrailingZeroBits(a: BigInt): int =
848+
var count = 0
849+
for x in a.limbs:
850+
if x == 0:
851+
count += 32
852+
else:
853+
return count + countTrailingZeroBits(x)
854+
return count
855+
856+
func gcd*(a, b: BigInt): BigInt =
857+
## Returns the greatest common divisor (GCD) of `a` and `b`.
858+
runnableExamples:
859+
assert gcd(54.initBigInt, 24.initBigInt) == 6.initBigInt
860+
861+
# binary GCD algorithm
862+
var
863+
u = abs(a)
864+
v = abs(b)
865+
if u.isZero:
866+
return v
867+
elif v.isZero:
868+
return u
869+
let
870+
i = countTrailingZeroBits(u)
871+
j = countTrailingZeroBits(v)
872+
k = min(i, j)
873+
u = u shr i
874+
v = v shr j
875+
while true:
876+
# u and v are odd
877+
if u > v:
878+
swap(u, v)
879+
v -= u
880+
if v.isZero:
881+
return u shl k
882+
v = v shr countTrailingZeroBits(v)
883+
884+
885+
func toSignedInt*[T: SomeSignedInt](x: BigInt): Option[T] =
886+
## Converts a `BigInt` number to signed integer, if possible.
887+
## If the `BigInt` doesn't fit in a `T`', returns `none`;
888+
## otherwise returns `some(T)`.
889+
runnableExamples:
890+
import std/options
891+
let
892+
a = 44.initBigInt
893+
b = 130.initBigInt
894+
assert toSignedInt[int8](a) == some(44'i8)
895+
assert toSignedInt[int8](b) == none(int8)
896+
assert toSignedInt[int](b) == some(130)
897+
898+
when sizeof(T) == 8:
899+
if x.limbs.len > 2:
900+
result = none(T)
901+
elif x.limbs.len == 2:
902+
if x.limbs[1] > uint32.high shr 1:
903+
if x.isNegative and x.limbs[0] == 0:
904+
result = some(T(int64.low))
905+
else:
906+
result = none(T)
907+
else:
908+
let value = T(x.limbs[1]) shl 32 + T(x.limbs[0])
909+
if x.isNegative:
910+
result = some(not(value - 1))
911+
else:
912+
result = some(value)
913+
else:
914+
if x.isNegative:
915+
result = some(not T(x.limbs[0] - 1))
916+
else:
917+
result = some(T(x.limbs[0]))
918+
else:
919+
if x.limbs.len > 1:
920+
result = none(T)
921+
else:
922+
if x.isNegative:
923+
if x.limbs[0] - 1 > uint32(T.high):
924+
result = none(T)
925+
else:
926+
result = some(not T(x.limbs[0] - 1))
927+
else:
928+
if x.limbs[0] > uint32(T.high):
929+
result = none(T)
930+
else:
931+
result = some(T(x.limbs[0]))
932+
840933

841934
func calcSizes(): array[2..36, int] =
842935
for i in 2..36:
@@ -991,27 +1084,45 @@ func initBigInt*(str: string, base: range[2..36] = 10): BigInt =
9911084
when (NimMajor, NimMinor) >= (1, 5):
9921085
include bigints/private/literals
9931086

994-
func inc*(a: var BigInt, b: int32 = 1) =
1087+
func inc*(a: var BigInt, b: int = 1) =
9951088
## Increase the value of a `BigInt` by the specified amount (default: 1).
9961089
runnableExamples:
9971090
var a = 15.initBigInt
9981091
inc a
9991092
assert a == 16.initBigInt
10001093
inc(a, 7)
10011094
assert a == 23.initBigInt
1002-
var c = a
1003-
additionInt(a, c, b)
10041095

1005-
func dec*(a: var BigInt, b: int32 = 1) =
1096+
if b in int32.low..int32.high:
1097+
var c = a
1098+
additionInt(a, c, b.int32)
1099+
else:
1100+
a += initBigInt(b)
1101+
1102+
func dec*(a: var BigInt, b: int = 1) =
10061103
## Decrease the value of a `BigInt` by the specified amount (default: 1).
10071104
runnableExamples:
10081105
var a = 15.initBigInt
10091106
dec a
10101107
assert a == 14.initBigInt
10111108
dec(a, 5)
10121109
assert a == 9.initBigInt
1013-
var c = a
1014-
subtractionInt(a, c, b)
1110+
1111+
if b in int32.low..int32.high:
1112+
var c = a
1113+
subtractionInt(a, c, b.int32)
1114+
else:
1115+
a -= initBigInt(b)
1116+
1117+
func succ*(a: BigInt, b: int = 1): BigInt =
1118+
## Returns the `b`-th successor of a `BigInt`.
1119+
result = a
1120+
inc(result, b)
1121+
1122+
func pred*(a: BigInt, b: int = 1): BigInt =
1123+
## Returns the `b`-th predecessor of a `BigInt`.
1124+
result = a
1125+
dec(result, b)
10151126

10161127

10171128
iterator countup*(a, b: BigInt, step: int32 = 1): BigInt =
@@ -1041,3 +1152,63 @@ iterator `..<`*(a, b: BigInt): BigInt =
10411152
while res < b:
10421153
yield res
10431154
inc res
1155+
1156+
func invmod*(a, modulus: BigInt): BigInt =
1157+
## Compute the modular inverse of `a` modulo `modulus`.
1158+
## The return value is always in the range `[1, modulus-1]`
1159+
runnableExamples:
1160+
invmod(3.initBigInt, 7.initBigInt) = 5.initBigInt
1161+
1162+
# extended Euclidean algorithm
1163+
if modulus.isZero:
1164+
raise newException(DivByZeroDefect, "modulus must be nonzero")
1165+
elif modulus.isNegative:
1166+
raise newException(ValueError, "modulus must be strictly positive")
1167+
elif a.isZero:
1168+
raise newException(DivByZeroDefect, "0 has no modular inverse")
1169+
else:
1170+
var
1171+
r0 = ((a mod modulus) + modulus) mod modulus
1172+
r1 = modulus
1173+
s0 = one
1174+
s1 = zero
1175+
while r1 > 0:
1176+
let
1177+
q = r0 div r1
1178+
# the `q.isZero` check is needed because of an ARC/ORC bug (see https://github.com/nim-lang/bigints/issues/88)
1179+
rk = if q.isZero: r0 else: r0 - q * r1
1180+
sk = if q.isZero: s0 else: s0 - q * s1
1181+
r0 = r1
1182+
r1 = rk
1183+
s0 = s1
1184+
s1 = sk
1185+
if r0 != one:
1186+
raise newException(ValueError, $a & " has no modular inverse modulo " & $modulus)
1187+
result = ((s0 mod modulus) + modulus) mod modulus
1188+
1189+
func powmod*(base, exponent, modulus: BigInt): BigInt =
1190+
## Compute modular exponentation of `base` with power `exponent` modulo `modulus`.
1191+
## The return value is always in the range `[0, modulus-1]`.
1192+
runnableExamples:
1193+
assert powmod(2.initBigInt, 3.initBigInt, 7.initBigInt) == 1.initBigInt
1194+
if modulus.isZero:
1195+
raise newException(DivByZeroDefect, "modulus must be nonzero")
1196+
elif modulus.isNegative:
1197+
raise newException(ValueError, "modulus must be strictly positive")
1198+
elif modulus == 1:
1199+
return zero
1200+
else:
1201+
var
1202+
base = base
1203+
exponent = exponent
1204+
if exponent < 0:
1205+
base = invmod(base, modulus)
1206+
exponent = -exponent
1207+
var
1208+
basePow = ((base mod modulus) + modulus) mod modulus # Base stays in [0, m-1]
1209+
result = one
1210+
while not exponent.isZero:
1211+
if (exponent.limbs[0] and 1) != 0:
1212+
result = (result * basePow) mod modulus
1213+
basePow = (basePow * basePow) mod modulus
1214+
exponent = exponent shr 1

0 commit comments

Comments
 (0)