Implement multiprecision addition / substraction

This commit is contained in:
Mamy André-Ratsimbazafy 2020-06-12 19:59:03 +02:00 committed by jangko
parent cbbffe4e9c
commit 206ffa92cf
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
4 changed files with 53 additions and 45 deletions

View File

@ -82,26 +82,26 @@ iterator leastToMostSig*(limbs: var Limbs): var Word =
iterator leastToMostSig*(aLimbs, bLimbs: Limbs): (Word, Word) = iterator leastToMostSig*(aLimbs, bLimbs: Limbs): (Word, Word) =
## Iterate from least to most significant word ## Iterate from least to most significant word
when cpuEndian == littleEndian: when cpuEndian == littleEndian:
for i in 0 ..< limbs.len: for i in 0 ..< aLimbs.len:
yield (aLimbs[i], bLimbs[i]) yield (aLimbs[i], bLimbs[i])
else: else:
for i in countdown(limbs.len-1, 0): for i in countdown(aLimbs.len-1, 0):
yield (aLimbs[i], bLimbs[i]) yield (aLimbs[i], bLimbs[i])
iterator leastToMostSig*(aLimbs: var Limbs, bLimbs: Limbs): (var Word, Word) = iterator leastToMostSig*(aLimbs: var Limbs, bLimbs: Limbs): (var Word, Word) =
## Iterate from least to most significant word ## Iterate from least to most significant word
when cpuEndian == littleEndian: when cpuEndian == littleEndian:
for i in 0 ..< limbs.len: for i in 0 ..< aLimbs.len:
yield (aLimbs[i], bLimbs[i]) yield (aLimbs[i], bLimbs[i])
else: else:
for i in countdown(limbs.len-1, 0): for i in countdown(aLimbs.len-1, 0):
yield (aLimbs[i], bLimbs[i]) yield (aLimbs[i], bLimbs[i])
iterator leastToMostSig*(cLimbs: var Limbs, aLimbs: Limbs, bLimbs: Limbs): (var Word, Word, Word) = iterator leastToMostSig*(cLimbs: var Limbs, aLimbs: Limbs, bLimbs: Limbs): (var Word, Word, Word) =
## Iterate from least to most significant word ## Iterate from least to most significant word
when cpuEndian == littleEndian: when cpuEndian == littleEndian:
for i in 0 ..< limbs.len: for i in 0 ..< aLimbs.len:
yield (cLimbs[i], aLimbs[i], bLimbs[i]) yield (cLimbs[i], aLimbs[i], bLimbs[i])
else: else:
for i in countdown(limbs.len-1, 0): for i in countdown(aLimbs.len-1, 0):
yield (cLimbs[i], aLimbs[i], bLimbs[i]) yield (cLimbs[i], aLimbs[i], bLimbs[i])

View File

@ -7,36 +7,40 @@
# #
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./conversion, ./initialization, import
./datatypes, ./datatypes, ./uint_comparison, ./uint_bitwise_ops,
./uint_comparison, ./primitives/addcarry_subborrow
./uint_bitwise_ops
# ############ Addition & Substraction ############ # # ############ Addition & Substraction ############ #
{.push raises: [], inline, noInit, gcsafe.}
func `+`*(x, y: UintImpl): UintImpl {.inline.} func `+`*(x, y: Limbs): Limbs =
# Forward declaration
func `+=`*(x: var UintImpl, y: UintImpl) {.inline.}=
## In-place addition for multi-precision unsigned int
type SubTy = type x.lo
x.lo += y.lo
x.hi += (x.lo < y.lo).toSubtype(SubTy) + y.hi # This helps the compiler produce ADC (add with carry)
func `+`*(x, y: UintImpl): UintImpl {.inline.}=
# Addition for multi-precision unsigned int # Addition for multi-precision unsigned int
result = x var carry = Carry(0)
result += y for wr, wx, wy in leastToMostSig(result, x, y):
addC(carry, wr, wx, wy, carry)
func `-`*(x, y: UintImpl): UintImpl {.inline.}= func `+=`*(x: var Limbs, y: Limbs) =
## In-place addition for multi-precision unsigned int
var carry = Carry(0)
for wx, wy in leastToMostSig(x, y):
addC(carry, wx, wx, wy, carry)
func `-`*(x, y: Limbs): Limbs =
# Substraction for multi-precision unsigned int # Substraction for multi-precision unsigned int
type SubTy = type x.lo var borrow = Borrow(0)
result.lo = x.lo - y.lo for wr, wx, wy in leastToMostSig(result, x, y):
result.hi = x.hi - y.hi - (x.lo < y.lo).toSubtype(SubTy) # This might (?) help the compiler produce SBB (sub with borrow) subB(borrow, wr, wx, wy, borrow)
func `-=`*(x: var UintImpl, y: UintImpl) {.inline.}= func `-=`*(x: var Limbs, y: Limbs) =
## In-place substraction for multi-precision unsigned int ## In-place substraction for multi-precision unsigned int
x = x - y var borrow = Borrow(0)
for wx, wy in leastToMostSig(x, y):
subB(borrow, wx, wx, wy, borrow)
func inc*(x: var UintImpl){.inline.}= func inc*(x: var Limbs, w: SomeUnsignedInt = 1) =
x += one(type x) var carry = Carry(0)
when cpuEndian == littleEndian:
addC(carry, x[0], x[0], w, carry)
for i in 1 ..< x.len:
addC(carry, x[i], x[i], 0, carry)

View File

@ -9,27 +9,29 @@
import ./datatypes import ./datatypes
func `not`*(x: Limbs): Limbs {.inline.}= {.push raises: [], inline, noInit, gcsafe.}
func `not`*(x: Limbs): Limbs =
## Bitwise complement of unsigned integer x ## Bitwise complement of unsigned integer x
for wr, wx in leastToMostSig(result, x): for wr, wx in leastToMostSig(result, x):
wr = not wx wr = not wx
func `or`*(x, y: Limbs): Limbs {.inline.}= func `or`*(x, y: Limbs): Limbs =
## `Bitwise or` of numbers x and y ## `Bitwise or` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y): for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx or wy wr = wx or wy
func `and`*(x, y: Limbs): Limbs {.inline.}= func `and`*(x, y: Limbs): Limbs =
## `Bitwise and` of numbers x and y ## `Bitwise and` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y): for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx and wy wr = wx and wy
func `xor`*(x, y: Limbs): Limbs {.inline.}= func `xor`*(x, y: Limbs): Limbs =
## `Bitwise xor` of numbers x and y ## `Bitwise xor` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y): for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx xor wy wr = wx xor wy
func `shr`*(x: Limbs, k: SomeInteger): Limbs {.inline.} = func `shr`*(x: Limbs, k: SomeInteger): Limbs =
## Shift right by k. ## Shift right by k.
## ##
## k MUST be less than the base word size (2^32 or 2^64) ## k MUST be less than the base word size (2^32 or 2^64)
@ -46,7 +48,7 @@ func `shr`*(x: Limbs, k: SomeInteger): Limbs {.inline.} =
result[i] = (x[i] shr k) or (x[i-1] shl (WordBitWidth - k)) result[i] = (x[i] shr k) or (x[i-1] shl (WordBitWidth - k))
result[0] = x[0] shr k result[0] = x[0] shr k
func `shl`*(x: Limbs, k: SomeInteger): Limbs {.inline.}= func `shl`*(x: Limbs, k: SomeInteger): Limbs =
## Compute the `shift left` operation of x and k ## Compute the `shift left` operation of x and k
when cpuEndian == littleEndian: when cpuEndian == littleEndian:
result[0] = x[0] shl k result[0] = x[0] shl k

View File

@ -11,16 +11,18 @@ import
./datatypes, ./datatypes,
./primitives/addcarry_subborrow ./primitives/addcarry_subborrow
func isZero*(n: SomeUnsignedInt): bool {.inline.} = {.push raises: [], inline, noInit, gcsafe.}
func isZero*(n: SomeUnsignedInt): bool =
n == 0 n == 0
func isZero*(limbs: Limbs): bool {.inline.} = func isZero*(limbs: Limbs): bool =
for word in limbs: for word in limbs:
if not word.isZero(): if not word.isZero():
return false return false
return true return true
func `<`*(x, y: Limbs): bool {.inline.}= func `<`*(x, y: Limbs): bool =
# Lower comparison for multi-precision integers # Lower comparison for multi-precision integers
var diff: Word var diff: Word
var borrow: Borrow var borrow: Borrow
@ -28,25 +30,25 @@ func `<`*(x, y: Limbs): bool {.inline.}=
subB(borrow, diff, wx, wy, borrow) subB(borrow, diff, wx, wy, borrow)
return bool(borrow) return bool(borrow)
func `==`*(x, y: Limbs): bool {.inline.}= func `==`*(x, y: Limbs): bool =
# Equal comparison for multi-precision integers # Equal comparison for multi-precision integers
for wx, wy in leastToMostSig(x, y): for wx, wy in leastToMostSig(x, y):
if wx != wy: if wx != wy:
return false return false
return true return true
func `<=`*(x, y: Limbs): bool {.inline.}= func `<=`*(x, y: Limbs): bool =
# Lower or equal comparison for multi-precision integers # Lower or equal comparison for multi-precision integers
not(y < x) not(y < x)
func isEven*(x: SomeUnsignedInt): bool {.inline.} = func isEven*(x: SomeUnsignedInt): bool =
(x and 1) == 0 (x and 1) == 0
func isEven*(x: Limbs): bool {.inline.}= func isEven*(x: Limbs): bool =
x.leastSignificantWord.isEven x.leastSignificantWord.isEven
func isOdd*(x: SomeUnsignedInt): bool {.inline.} = func isOdd*(x: SomeUnsignedInt): bool =
not x.isEven not x.isEven
func isOdd*(x: Limbs): bool {.inline.}= func isOdd*(x: Limbs): bool =
not x.isEven not x.isEven