Skip to content

Commit 6d8c051

Browse files
authored
Add gcd & abs (#75)
1 parent 658733d commit 6d8c051

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

src/bigints.nim

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ func isZero(a: BigInt): bool {.inline.} =
7171
return false
7272
return true
7373

74+
func abs*(a: BigInt): BigInt =
75+
# Returns the absolute value of `a`.
76+
runnableExamples:
77+
assert abs(42.initBigInt) == 42.initBigInt
78+
assert abs(-12.initBigInt) == 12.initBigInt
79+
result = a
80+
result.isNegative = false
81+
7482
func unsignedCmp(a: BigInt, b: uint32): int64 =
7583
# ignores the sign of `a`
7684
# `a` and `b` are assumed to not be zero
@@ -751,6 +759,43 @@ func `divmod`*(a, b: BigInt): tuple[q, r: BigInt] =
751759
assert divmod(a, b) == (3.initBigInt, 2.initBigInt)
752760
division(result.q, result.r, a, b)
753761

762+
func countTrailingZeroBits(a: BigInt): int =
763+
var count = 0
764+
for x in a.limbs:
765+
if x == 0:
766+
count += 32
767+
else:
768+
return count + countTrailingZeroBits(x)
769+
return count
770+
771+
func gcd*(a, b: BigInt): BigInt =
772+
## Returns the greatest common divisor (GCD) of `a` and `b`.
773+
runnableExamples:
774+
assert gcd(54.initBigInt, 24.initBigInt) == 6.initBigInt
775+
776+
# binary GCD algorithm
777+
var
778+
u = abs(a)
779+
v = abs(b)
780+
if u.isZero:
781+
return v
782+
elif v.isZero:
783+
return u
784+
let
785+
i = countTrailingZeroBits(u)
786+
j = countTrailingZeroBits(v)
787+
k = min(i, j)
788+
u = u shr i
789+
v = v shr j
790+
while true:
791+
# u and v are odd
792+
if u > v:
793+
swap(u, v)
794+
v -= u
795+
if v.isZero:
796+
return u shl k
797+
v = v shr countTrailingZeroBits(v)
798+
754799

755800
func toSignedInt*[T: SomeSignedInt](x: BigInt): Option[T] =
756801
## Converts a `BigInt` number to signed integer, if possible.

tests/tbigints.nim

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,35 @@ proc main() =
461461
doAssertRaises(DivByZeroDefect): discard one mod zero
462462
doAssertRaises(DivByZeroDefect): discard divmod(one, zero)
463463

464+
block: # gcd
465+
let a = "866506".initBigInt
466+
let b = "140640".initBigInt
467+
let two = 2.initBigInt
468+
doAssert gcd(a, b) == two
469+
# gcd(a, b) = gcd(b, a)
470+
doAssert gcd(b, a) == two
471+
# gcd(a, -b) = gcd(a, |b|)
472+
doAssert gcd(-a, b) == two
473+
doAssert gcd(a, -b) == two
474+
doAssert gcd(-a, -b) == two
475+
476+
block: # properties of gcd
477+
let a = "668403".initBigInt
478+
let b = "753160".initBigInt
479+
let c = "249115".initBigInt
480+
doAssert gcd(a, b) == gcd(b, a)
481+
doAssert gcd(a, zero) == a
482+
doAssert gcd(a, a) == a
483+
doAssert gcd(c * a, c * b) == c * gcd(a,b)
484+
doAssert gcd(a, gcd(b, c)) == gcd(gcd(a, b), c)
485+
doAssert gcd(a, b) == gcd(b, a mod b)
486+
487+
block: # abs
488+
doAssert abs(zero) == zero
489+
doAssert abs(-zero) == zero
490+
doAssert abs(one) == one
491+
doAssert abs(-one) == one
492+
464493
block: # toSignedInt
465494
let
466495
a = initBigInt(7)
@@ -531,5 +560,6 @@ proc main() =
531560
doAssert pred(a, 3) == initBigInt(4)
532561
doAssert succ(a, 3) == initBigInt(10)
533562

563+
534564
static: main()
535565
main()

0 commit comments

Comments
 (0)