Implement exponentiation, test mul, split mul/div tests
This commit is contained in:
parent
254d4da649
commit
dc9e0a43ca
|
@ -1,50 +0,0 @@
|
|||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
./datatypes,
|
||||
./uint_bitwise_ops, ./uint_mul, ./initialization, ./uint_comparison
|
||||
|
||||
func pow*(x: UintImpl, y: Natural): UintImpl =
|
||||
## Compute ``x`` to the power of ``y``,
|
||||
## ``x`` must be non-negative
|
||||
|
||||
# Implementation uses exponentiation by squaring
|
||||
# See Nim math module: https://github.com/nim-lang/Nim/blob/4ed24aa3eb78ba4ff55aac3008ec3c2427776e50/lib/pure/math.nim#L429
|
||||
# And Eli Bendersky's blog: https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms
|
||||
|
||||
var (x, y) = (x, y)
|
||||
result = one(type x)
|
||||
|
||||
while true:
|
||||
if bool(y and 1): # if y is odd
|
||||
result = result * x
|
||||
y = y shr 1
|
||||
if y == 0:
|
||||
break
|
||||
x = x * x
|
||||
|
||||
func pow*(x: UintImpl, y: UintImpl): UintImpl =
|
||||
## Compute ``x`` to the power of ``y``,
|
||||
## ``x`` must be non-negative
|
||||
|
||||
# Implementation uses exponentiation by squaring
|
||||
# See Nim math module: https://github.com/nim-lang/Nim/blob/4ed24aa3eb78ba4ff55aac3008ec3c2427776e50/lib/pure/math.nim#L429
|
||||
# And Eli Bendersky's blog: https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms
|
||||
|
||||
var (x, y) = (x, y)
|
||||
result = one(type x)
|
||||
|
||||
while true:
|
||||
if y.isOdd:
|
||||
result = result * x
|
||||
y = y shr 1
|
||||
if y.isZero:
|
||||
break
|
||||
x = x * x
|
|
@ -23,18 +23,22 @@ export StUint
|
|||
|
||||
func setZero*(a: var StUint) =
|
||||
## Set ``a`` to 0
|
||||
zeroMem(a[0].addr, sizeof(a))
|
||||
for i in 0 ..< a.limbs.len:
|
||||
a[i] = 0
|
||||
|
||||
func setSmallInt(a: var StUint, k: Word) =
|
||||
## Set ``a`` to k
|
||||
when cpuEndian == littleEndian:
|
||||
a.limbs[0] = k
|
||||
for i in 1 ..< a.limbs.len:
|
||||
a.limbs[i] = 0
|
||||
else:
|
||||
a.limbs[^1] = k
|
||||
for i in 0 ..< a.limb.len - 1:
|
||||
a.limbs[i] = 0
|
||||
|
||||
func setOne*(a: var StUint) =
|
||||
## Set ``a`` to 1
|
||||
when cpuEndian == littleEndian:
|
||||
a.limbs[0] = 1
|
||||
when a.limbs.len > 1:
|
||||
zeroMem(a.limbs[1].addr, (a.limbs.len - 1) * sizeof(SecretWord))
|
||||
else:
|
||||
a.limbs[^1] = 1
|
||||
when a.limbs.len > 1:
|
||||
zeroMem(a.limbs[0].addr, (a.len - 1) * sizeof(SecretWord))
|
||||
setSmallInt(a, 1)
|
||||
|
||||
func zero*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
||||
## Returns the zero of the input type
|
||||
|
@ -42,7 +46,7 @@ func zero*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
|||
|
||||
func one*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
||||
## Returns the one of the input type
|
||||
result.limbs.setOne()
|
||||
result.setOne()
|
||||
|
||||
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||
for wr in leastToMostSig(result):
|
||||
|
@ -279,8 +283,52 @@ func `*`*(a, b: Stuint): Stuint =
|
|||
result.clearExtraBits()
|
||||
|
||||
{.pop.}
|
||||
# Division & Modulo
|
||||
# --------------------------------------------------------
|
||||
|
||||
# Exponentiation
|
||||
# --------------------------------------------------------
|
||||
|
||||
{.push raises: [], noInit, gcsafe.}
|
||||
|
||||
func pow*(a: Stuint, e: Natural): Stuint =
|
||||
## Compute ``a`` to the power of ``e``,
|
||||
## ``e`` must be non-negative
|
||||
|
||||
# Implementation uses exponentiation by squaring
|
||||
# See Nim math module: https://github.com/nim-lang/Nim/blob/4ed24aa3eb78ba4ff55aac3008ec3c2427776e50/lib/pure/math.nim#L429
|
||||
# And Eli Bendersky's blog: https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms
|
||||
|
||||
var (a, e) = (a, e)
|
||||
result.setOne()
|
||||
|
||||
while true:
|
||||
if bool(e and 1): # if y is odd
|
||||
result = result * a
|
||||
e = e shr 1
|
||||
if e == 0:
|
||||
break
|
||||
a = a * a
|
||||
|
||||
func pow*[aBits, eBits](a: Stuint[aBits], e: Stuint[eBits]): Stuint[aBits] =
|
||||
## Compute ``x`` to the power of ``y``,
|
||||
## ``x`` must be non-negative
|
||||
|
||||
# Implementation uses exponentiation by squaring
|
||||
# See Nim math module: https://github.com/nim-lang/Nim/blob/4ed24aa3eb78ba4ff55aac3008ec3c2427776e50/lib/pure/math.nim#L429
|
||||
# And Eli Bendersky's blog: https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms
|
||||
|
||||
var (a, e) = (a, e)
|
||||
result.setOne()
|
||||
|
||||
while true:
|
||||
if e.isOdd:
|
||||
result = result * a
|
||||
e = e shr 1
|
||||
if e.isZero:
|
||||
break
|
||||
a = a * a
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
# Division & Modulo
|
||||
# --------------------------------------------------------
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
|
||||
import ../stint, unittest, test_helpers
|
||||
|
||||
template chkMul(chk: untyped, a, b, c: string, bits: int) =
|
||||
chk (fromHex(StUint[bits], a) * fromHex(StUint[bits], b)) == fromHex(StUint[bits], c)
|
||||
|
||||
template chkDiv(chk: untyped, a, b, c: string, bits: int) =
|
||||
chk (fromHex(StUint[bits], a) div fromHex(StUint[bits], b)) == fromHex(StUint[bits], c)
|
||||
|
||||
|
@ -21,41 +18,7 @@ template chkMod(chk: untyped, a, b, c: string, bits: int) =
|
|||
template chkDivMod(chk: untyped, a, b, c, d: string, bits: int) =
|
||||
chk divmod(fromHex(StUint[bits], a), fromHex(StUint[bits], b)) == (fromHex(StUint[bits], c), fromHex(StUint[bits], d))
|
||||
|
||||
template testMuldiv(chk, tst: untyped) =
|
||||
tst "operator `mul`":
|
||||
chkMul(chk, "0", "3", "0", 8)
|
||||
chkMul(chk, "1", "3", "3", 8)
|
||||
chkMul(chk, "64", "3", "2C", 8) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 16)
|
||||
chkMul(chk, "1", "3", "3", 16)
|
||||
chkMul(chk, "64", "3", "12C", 16)
|
||||
chkMul(chk, "1770", "46", "68A0", 16) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 32)
|
||||
chkMul(chk, "1", "3", "3", 32)
|
||||
chkMul(chk, "64", "3", "12C", 32)
|
||||
chkMul(chk, "1770", "46", "668A0", 32)
|
||||
chkMul(chk, "13880", "13880", "7D784000", 32) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 64)
|
||||
chkMul(chk, "1", "3", "3", 64)
|
||||
chkMul(chk, "64", "3", "12C", 64)
|
||||
chkMul(chk, "1770", "46", "668A0", 64)
|
||||
chkMul(chk, "13880", "13880", "17D784000", 64)
|
||||
chkMul(chk, "3B9ACA00", "E8D4A51000", "35C9ADC5DEA00000", 64) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 128)
|
||||
chkMul(chk, "1", "3", "3", 128)
|
||||
chkMul(chk, "64", "3", "12C", 128)
|
||||
chkMul(chk, "1770", "46", "668A0", 128)
|
||||
chkMul(chk, "13880", "13880", "17D784000", 128)
|
||||
chkMul(chk, "3B9ACA00", "E8D4A51000", "3635C9ADC5DEA00000", 128)
|
||||
chkMul(chk, "25295F0D1", "10", "25295F0D10", 128)
|
||||
chkMul(chk, "123456789ABCDEF00", "123456789ABCDEF00", "4b66dc33f6acdca5e20890f2a5210000", 128) # overflow
|
||||
|
||||
chkMul(chk, "123456789ABCDEF00", "123456789ABCDEF00", "14b66dc33f6acdca5e20890f2a5210000", 256)
|
||||
|
||||
template testdivmod(chk, tst: untyped) =
|
||||
tst "operator `div`":
|
||||
chkDiv(chk, "0", "3", "0", 8)
|
||||
chkDiv(chk, "1", "3", "0", 8)
|
||||
|
@ -212,44 +175,10 @@ template testMuldiv(chk, tst: untyped) =
|
|||
chkDivMod(chk, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "27", "6906906906906906906906906906906", "15", 128)
|
||||
|
||||
static:
|
||||
testMuldiv(ctCheck, ctTest)
|
||||
testdivmod(ctCheck, ctTest)
|
||||
|
||||
suite "Wider unsigned int muldiv coverage":
|
||||
testMuldiv(check, test)
|
||||
|
||||
suite "Testing unsigned int multiplication implementation":
|
||||
test "Multiplication with result fitting in low half":
|
||||
|
||||
let a = 10000.stuint(64)
|
||||
let b = 10000.stuint(64)
|
||||
|
||||
check: cast[uint64](a*b) == 100_000_000'u64 # need 27-bits
|
||||
|
||||
test "Multiplication with result overflowing low half":
|
||||
|
||||
let a = 1_000_000.stuint(64)
|
||||
let b = 1_000_000.stuint(64)
|
||||
|
||||
check: cast[uint64](a*b) == 1_000_000_000_000'u64 # need 40 bits
|
||||
|
||||
test "Full overflow is handled like native unsigned types":
|
||||
|
||||
let a = 1_000_000_000.stuint(64)
|
||||
let b = 1_000_000_000.stuint(64)
|
||||
let c = 1_000.stuint(64)
|
||||
|
||||
let x = 1_000_000_000'u64
|
||||
let y = 1_000_000_000'u64
|
||||
let z = 1_000'u64
|
||||
let w = x*y*z
|
||||
|
||||
#check: cast[uint64](a*b*c) == 1_000_000_000_000_000_000_000'u64 # need 70-bits
|
||||
check: cast[uint64](a*b*c) == w
|
||||
|
||||
test "Nim v1.0.2 32 bit type inference rule changed":
|
||||
let x = 9975492817.stuint(256)
|
||||
let y = 16.stuint(256)
|
||||
check x * y == 159607885072.stuint(256)
|
||||
testdivmod(check, test)
|
||||
|
||||
suite "Testing unsigned int division and modulo implementation":
|
||||
test "Divmod(100, 13) returns the correct result":
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
import ../stint, unittest, stew/byteutils, test_helpers
|
||||
|
||||
|
||||
template chkSwapBytes(chk: untyped, bits: int, hex: string) =
|
||||
# dumpHex already do the job to swap the output if
|
||||
# we use `littleEndian` on both platform
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../stint, unittest, test_helpers
|
||||
|
||||
template chkMul(chk: untyped, a, b, c: string, bits: int) =
|
||||
chk (fromHex(Stuint[bits], a) * fromHex(Stuint[bits], b)) == fromHex(Stuint[bits], c)
|
||||
|
||||
template testMul(chk, tst: untyped) =
|
||||
tst "operator `mul`":
|
||||
chkMul(chk, "0", "3", "0", 8)
|
||||
chkMul(chk, "1", "3", "3", 8)
|
||||
chkMul(chk, "64", "3", "2C", 8) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 16)
|
||||
chkMul(chk, "1", "3", "3", 16)
|
||||
chkMul(chk, "64", "3", "12C", 16)
|
||||
chkMul(chk, "1770", "46", "68A0", 16) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 32)
|
||||
chkMul(chk, "1", "3", "3", 32)
|
||||
chkMul(chk, "64", "3", "12C", 32)
|
||||
chkMul(chk, "1770", "46", "668A0", 32)
|
||||
chkMul(chk, "13880", "13880", "7D784000", 32) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 64)
|
||||
chkMul(chk, "1", "3", "3", 64)
|
||||
chkMul(chk, "64", "3", "12C", 64)
|
||||
chkMul(chk, "1770", "46", "668A0", 64)
|
||||
chkMul(chk, "13880", "13880", "17D784000", 64)
|
||||
chkMul(chk, "3B9ACA00", "E8D4A51000", "35C9ADC5DEA00000", 64) # overflow
|
||||
|
||||
chkMul(chk, "0", "3", "0", 128)
|
||||
chkMul(chk, "1", "3", "3", 128)
|
||||
chkMul(chk, "64", "3", "12C", 128)
|
||||
chkMul(chk, "1770", "46", "668A0", 128)
|
||||
chkMul(chk, "13880", "13880", "17D784000", 128)
|
||||
chkMul(chk, "3B9ACA00", "E8D4A51000", "3635C9ADC5DEA00000", 128)
|
||||
chkMul(chk, "25295F0D1", "10", "25295F0D10", 128)
|
||||
chkMul(chk, "123456789ABCDEF00", "123456789ABCDEF00", "4b66dc33f6acdca5e20890f2a5210000", 128) # overflow
|
||||
|
||||
chkMul(chk, "123456789ABCDEF00", "123456789ABCDEF00", "14b66dc33f6acdca5e20890f2a5210000", 256)
|
||||
|
||||
static:
|
||||
testMul(ctCheck, ctTest)
|
||||
|
||||
suite "Wider unsigned int muldiv coverage":
|
||||
testMul(check, test)
|
||||
|
||||
suite "Testing unsigned int multiplication implementation":
|
||||
test "Multiplication with result fitting in low half":
|
||||
|
||||
let a = 10000.stuint(64)
|
||||
let b = 10000.stuint(64)
|
||||
|
||||
check: cast[uint64](a*b) == 100_000_000'u64 # need 27-bits
|
||||
|
||||
test "Multiplication with result overflowing low half":
|
||||
|
||||
let a = 1_000_000.stuint(64)
|
||||
let b = 1_000_000.stuint(64)
|
||||
|
||||
check: cast[uint64](a*b) == 1_000_000_000_000'u64 # need 40 bits
|
||||
|
||||
test "Full overflow is handled like native unsigned types":
|
||||
|
||||
let a = 1_000_000_000.stuint(64)
|
||||
let b = 1_000_000_000.stuint(64)
|
||||
let c = 1_000.stuint(64)
|
||||
|
||||
let x = 1_000_000_000'u64
|
||||
let y = 1_000_000_000'u64
|
||||
let z = 1_000'u64
|
||||
let w = x*y*z
|
||||
|
||||
#check: cast[uint64](a*b*c) == 1_000_000_000_000_000_000_000'u64 # need 70-bits
|
||||
check: cast[uint64](a*b*c) == w
|
||||
|
||||
test "Nim v1.0.2 32 bit type inference rule changed":
|
||||
let x = 9975492817.stuint(256)
|
||||
let y = 16.stuint(256)
|
||||
check x * y == 159607885072.stuint(256)
|
Loading…
Reference in New Issue