diff --git a/bigints.nimble b/bigints.nimble
index 973e878..7651f9b 100644
--- a/bigints.nimble
+++ b/bigints.nimble
@@ -1,6 +1,6 @@
# Package
-version = "0.4.3"
+version = "0.4.4"
author = "Dennis Felsing"
description = "Arbitrary-precision integers implemented in pure Nim"
license = "MIT"
diff --git a/readme.md b/readme.md
index bc67db3..1032411 100644
--- a/readme.md
+++ b/readme.md
@@ -29,7 +29,6 @@ For examples of usage see the [examples](examples) folder.
## Current limitations and possible enhancements
* cannot multiply a number with itself (x *= x). An issue like this exist also for addition (#27) and possibly other operations
-* an uninitialized `BigInt` might raise error when performing some operations (e.g. #26)
* not expected to work on 32 bit
* some common bitwise operations (`and`, `or`, `xor`, `not`) are not implemented
* operations between `BigInt` and standard integer types besides `int32` are not implemented
@@ -121,8 +120,26 @@ template initBigInt(val: uint): BigInt
proc initBigInt(val: BigInt): BigInt
```
+## **const** zero
+
+
+```nim
+zero = ([0'u], {})
+```
+
+## **const** one
+
+
+```nim
+one = ([1'u], {})
+```
+
## **proc** cmp
+Returns:
+ * a value less than zero, if a < b
+ * a value greater than zero, if a > b
+ * zero, if a == b
```nim
proc cmp(a, b: BigInt): int64
@@ -130,16 +147,20 @@ proc cmp(a, b: BigInt): int64
## **proc** cmp
+Returns:
+ * a value less than zero, if a < b
+ * a value greater than zero, if a > b
+ * zero, if a == b
```nim
-proc cmp(a: int32; b: BigInt): int64
+proc cmp(a: BigInt; b: int32): int64
```
## **proc** cmp
```nim
-proc cmp(a: BigInt; b: int32): int64
+proc cmp(a: int32; b: BigInt): int64
```
## **proc** `<`
diff --git a/src/bigints.nim b/src/bigints.nim
index 4fffaef..f24b3c8 100644
--- a/src/bigints.nim
+++ b/src/bigints.nim
@@ -66,10 +66,18 @@ else:
proc initBigInt*(val: BigInt): BigInt =
result = val
-const zero = initBigInt(0)
-const one = initBigInt(1)
+const zero* = initBigInt(0)
+const one* = initBigInt(1)
+
+proc isZero(a: BigInt): bool {.inline.} =
+ for i in countdown(a.limbs.high, 0):
+ if a.limbs[i] != 0'u32:
+ return false
+ return true
proc unsignedCmp(a: BigInt, b: int32): int64 =
+ # here a and b have same sign a none of them is zero.
+ # in particular we have that a.limbs.len >= 1
result = int64(a.limbs.len) - 1
if result != 0:
@@ -77,8 +85,7 @@ proc unsignedCmp(a: BigInt, b: int32): int64 =
result = int64(a.limbs[0]) - int64(b)
-proc unsignedCmp(a: int32, b: BigInt): int64 =
- -unsignedCmp(b, a)
+proc unsignedCmp(a: int32, b: BigInt): int64 = -unsignedCmp(b, a)
proc unsignedCmp(a, b: BigInt): int64 =
result = int64(a.limbs.len) - int64(b.limbs.len)
@@ -93,40 +100,52 @@ proc unsignedCmp(a, b: BigInt): int64 =
return
proc cmp*(a, b: BigInt): int64 =
- if Negative in a.flags and a.limbs != @[0'u32]:
- if Negative in b.flags and b.limbs != @[0'u32]:
- return unsignedCmp(b, a)
- else:
- return -1
- else:
- if Negative in b.flags:
+ ## Returns:
+ ## * a value less than zero, if `a < b`
+ ## * a value greater than zero, if `a > b`
+ ## * zero, if `a == b`
+ if a.isZero:
+ if b.isZero:
+ return 0
+ elif Negative in b.flags: # b.isNegative
return 1
- else:
- return unsignedCmp(a, b)
-
-proc cmp*(a: int32, b: BigInt): int64 =
- if a < 0:
- if Negative in b.flags and b.limbs != @[0'u32]:
- return unsignedCmp(b, a)
else:
return -1
- else:
- if Negative in b.flags:
+ elif Negative in a.flags: # a.isNegative
+ if b.isZero or Negative notin b.flags: # b >= 0
+ return -1
+ else:
+ return unsignedCmp(b, a)
+ else: # a > 0
+ if b.isZero or Negative in b.flags: # b <= 0
return 1
else:
return unsignedCmp(a, b)
proc cmp*(a: BigInt, b: int32): int64 =
- if Negative in a.flags and a.limbs != @[0'u32]:
+ ## Returns:
+ ## * a value less than zero, if `a < b`
+ ## * a value greater than zero, if `a > b`
+ ## * zero, if `a == b`
+ if a.isZero:
if b < 0:
- return unsignedCmp(b, a)
+ return 1
+ elif b == 0:
+ return 0
else:
return -1
- else:
+ elif Negative in a.flags: # a.isNegative
if b < 0:
+ return unsignedCmp(b, a)
+ else:
+ return -1
+ else: # a > 0
+ if b <= 0:
return 1
else:
- return unsignedCmp(a, b)
+ return unsignedCmp(b, a)
+
+proc cmp*(a: int32, b: BigInt): int64 = -cmp(b, a)
proc `<` *(a, b: BigInt): bool = cmp(a, b) < 0
proc `<` *(a: BigInt, b: int32): bool = cmp(a, b) < 0
@@ -285,7 +304,9 @@ proc unsignedSubtraction(a: var BigInt, b, c: BigInt) =
negate(a)
proc additionInt(a: var BigInt, b: BigInt, c: int32) =
- if Negative in b.flags:
+ if b.isZero:
+ a = c.initBigInt
+ elif Negative in b.flags:
if c < 0:
unsignedAdditionInt(a, b, c)
a.flags.incl(Negative)
@@ -333,7 +354,9 @@ template optAddInt*{x = y + z}(x,y: BigInt, z: int32) = additionInt(x, y, z)
template optAdd*{x = y + z}(x,y,z: BigInt) = addition(x, y, z)
proc subtractionInt(a: var BigInt, b: BigInt, c: int32) =
- if Negative in b.flags:
+ if b.isZero:
+ a = (-c).initBigInt
+ elif Negative in b.flags:
if c < 0:
# TODO: is this right?
unsignedSubtractionInt(a, b, c)
@@ -390,6 +413,8 @@ template unsignedMultiplicationInt(a: BigInt, b: BigInt, c: int32, bl) =
normalize(a)
template unsignedMultiplication(a: BigInt, b, c: BigInt, bl, cl) =
+ # always called with bl >= cl
+
for i in 0 ..< bl:
tmp += uint64(b.limbs[i]) * uint64(c.limbs[0])
a.limbs[i] = uint32(tmp and uint32.high)
@@ -445,21 +470,20 @@ proc multiplicationInt(a: var BigInt, b: BigInt, c: int32) =
# This doesn't work when a = b
proc multiplication(a: var BigInt, b, c: BigInt) =
+ if b.isZero or c.isZero:
+ a = zero
+ return
let
bl = b.limbs.len
cl = c.limbs.len
var tmp: uint64
a.limbs.setXLen(bl + cl)
-
if cl > bl:
unsignedMultiplication(a, c, b, cl, bl)
else:
unsignedMultiplication(a, b, c, bl, cl)
- if a.limbs == @[0'u32]:
- return
-
if Negative in b.flags:
if Negative in c.flags:
a.flags.excl(Negative)
@@ -872,6 +896,8 @@ proc pow*(base: int32|BigInt, exp: int32|BigInt): BigInt =
base *= tmp
proc toString*(a: BigInt, base: range[2..36] = 10): string =
+ if a.isZero:
+ return "0"
if base in multiples:
return toStringMultipleTwo(a, base)
diff --git a/tests/tester.nim b/tests/tester.nim
index 28fbc1a..7f254bb 100644
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -109,3 +109,122 @@ test "validate digits in string parsing (https://github.com/def-/nim-bigints/iss
discard initBigInt("2", 2)
expect ValueError:
discard initBigInt("z", 35)
+
+test "empty limbs when uninitialized (https://github.com/def-/nim-bigints/issues/26)":
+ # reported issue has an example about multiplication and it is due to a call to a.limbs[0] for an uninitialized a: BigInt
+ # besides multiplication, one could look at appearances of [0] in source to find possible failures
+ # failures bound to reaching a line with [0] are fatal
+ # besides appearances of [0], also logic implemented through a.limbs.len might (and indeed does) show error
+ # logic around sign might also play a role
+ var
+ zeroEmpty: BigInt # should be treated as zero, same with -zeroEmpty
+ let
+ zeroInt32: int32 = 0
+ oneInt32: int32 = 1
+ bigOne: BigInt = initBigInt(@[0.uint32, 1])
+
+ # unsignedCmp(a: BigInt, b: int32) has [0]; used by public cmp and <
+ # never reached in the following cases (no fatal), since it cannot be reached (see comment in code)
+ # still, errors can be found in comparing zero to zero
+ check zeroEmpty < oneInt32 # ok
+ check zeroEmpty > -oneInt32 # ok
+ check -zeroEmpty < oneInt32 # ok
+ check -zeroEmpty > -oneInt32 # ok
+ check not(zeroEmpty < zeroInt32) # error: fixed
+ check not(zeroEmpty > zeroInt32) # ok
+ check not(-zeroEmpty < zeroInt32) # error: fixed
+ check not(-zeroEmpty > zeroInt32) # ok
+ check zeroEmpty == zeroInt32 # error: fixed
+ check -zeroEmpty == zeroInt32 # error: fixed
+
+ # this came up in the above testing and can be though as secondary effect of unitialization (fix in negate?)
+ check $zero == "0" # ok
+ check $zeroEmpty == "0" # ok
+ check $(-zeroEmpty) == "0" # error: fixed
+
+ # unsignedCmp(a, b: BigInt) has no [0] but it has logic with limbs.len
+ check zeroEmpty < one # ok
+ check zeroEmpty > -one # ok
+ check -zeroEmpty < one # ok
+ check -zeroEmpty > -one # ok
+ check not (zeroEmpty < zeroInt32) # error: fixed
+ check not (zeroEmpty > zeroInt32) # ok
+ check not (zeroEmpty < zero) # error: fixed
+ check not (zeroEmpty > zero) # ok
+ check zeroEmpty == zero # error: fixed
+ check -zeroEmpty == zero # error: fixed
+
+ # proc unsignedAdditionInt(a: var BigInt, b: BigInt, c: int32)
+ check zeroEmpty + 1.int32 == one # fixed: fatal[IndexError] in bigints.nim(181) unsignedAdditionInt
+ check -zeroEmpty + 1.int32 == one # fixed: fatal[IndexError] in bigints.nim(245) unsignedSubtractionInt
+ check zeroEmpty + (-1).int32 == -one # fixed: fatal[IndexError] in bigints.nim(245) unsignedSubtractionInt
+ check -zeroEmpty + (-1).int32 == -one # fixed: fatal[IndexError] in bigints.nim(181) unsignedAdditionInt
+
+ # proc unsignedAddition(a: var BigInt, b, c: BigInt)
+ check zeroEmpty + one == one # ok
+ check one + zeroEmpty == one # ok
+ check -zeroEmpty + one == one # ok
+ check one + -zeroEmpty == one # ok
+ check zeroEmpty + zeroEmpty == zero # ok
+ check -zeroEmpty + zeroEmpty == zero # ok
+ check -zeroEmpty + -zeroEmpty == zero # ok
+ check zeroEmpty + -zeroEmpty == zero # ok
+ check bigOne + zeroEmpty == bigOne # ok
+ check bigOne + -zeroEmpty == bigOne # ok
+ check zeroEmpty + bigOne == bigOne # ok
+ check -zeroEmpty + bigOne == bigOne # ok
+ check -bigOne + zeroEmpty == -bigOne # ok
+ check -bigOne + -zeroEmpty == -bigOne # ok
+ check zeroEmpty + -bigOne == -bigOne # ok
+ check -zeroEmpty + -bigOne == -bigOne # ok
+
+ # proc unsignedSubtractionInt(a: var BigInt, b: BigInt, c: int32)
+ check zeroEmpty - 1.int32 == -one # fixed: fatal[IndexError] in bigints.nim(245) unsignedSubtractionInt
+ check -zeroEmpty - 1.int32 == -one # fixed: fatal[IndexError] in bigints.nim(181) unsignedAdditionInt
+ check zeroEmpty - (-1).int32 == one # fixed: fatal[IndexError] in bigints.nim(181) unsignedAdditionInt
+ check -zeroEmpty - (-1).int32 == one # fixed: fatal[IndexError] in bigints.nim(245) unsignedSubtractionInt
+
+ # proc unsignedSubtraction(a: var BigInt, b, c: BigInt)
+ check zeroEmpty - one == -one # ok
+ check one - zeroEmpty == one # ok
+ check -zeroEmpty - one == -one # ok
+ check one - -zeroEmpty == one # ok
+ check zeroEmpty - zeroEmpty == zero # ok
+ check -zeroEmpty - zeroEmpty == zero # ok
+ check -zeroEmpty - -zeroEmpty == zero # ok
+ check zeroEmpty - -zeroEmpty == zero # ok
+ check bigOne - zeroEmpty == bigOne # ok
+ check bigOne - -zeroEmpty == bigOne # ok
+ check zeroEmpty - bigOne == -bigOne # ok
+ check -zeroEmpty - bigOne == -bigOne # ok
+ check -bigOne - zeroEmpty == -bigOne # ok
+ check -bigOne - -zeroEmpty == -bigOne # ok
+ check zeroEmpty - -bigOne == bigOne # ok
+ check -zeroEmpty - -bigOne == bigOne # ok
+
+ # multiplication
+ check zeroEmpty * 1.int32 == zero
+ check -zeroEmpty * 1.int32 == zero
+ check zeroEmpty * one == zero
+ check -zeroEmpty * one == zero
+ check one * zeroEmpty == zero
+ check one * -zeroEmpty == zero
+
+ # https://github.com/def-/nim-bigints/issues/26
+ block:
+ var
+ a: BigInt
+ b: BigInt = 12.initBigInt
+
+ check a*b == 0
+
+ # division does not have issues, but let's add some checks
+ check zeroEmpty div one == zero
+ check -zeroEmpty div one == zero
+ check zeroEmpty mod one == zero
+ check -zeroEmpty mod one == zero
+
+ check zeroEmpty div 1.int32 == zero
+ check -zeroEmpty div 1.int32 == zero
+ check zeroEmpty mod 1.int32 == zero
+ check -zeroEmpty mod 1.int32 == zero