From 11f62d42c9926452e99159ca14c1b7336a415f5f Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Mon, 14 May 2018 19:02:39 +0200 Subject: [PATCH] Add exponentiation - closes #37 (#46) * Add exponentiation * Change name to pow (like ttmath) - `^` to discuss --- stint/private/uint_exp.nim | 31 +++++++++++++++++++++++++++++++ stint/uint_public.nim | 12 ++++++++++-- tests/property_based.nim | 15 ++++++++++++++- tests/property_based_uint256.nim | 20 ++++++++++++++++++++ tests/test_uint_exp.nim | 28 ++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 stint/private/uint_exp.nim create mode 100644 tests/test_uint_exp.nim diff --git a/stint/private/uint_exp.nim b/stint/private/uint_exp.nim new file mode 100644 index 0000000..166a428 --- /dev/null +++ b/stint/private/uint_exp.nim @@ -0,0 +1,31 @@ +# 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 + +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 (y and 1) != 0: + result = result * x + y = y shr 1 + if y == 0: + break + x = x * x diff --git a/stint/uint_public.nim b/stint/uint_public.nim index cf70132..bda1570 100644 --- a/stint/uint_public.nim +++ b/stint/uint_public.nim @@ -61,9 +61,9 @@ make_unary(`not`, StUint) make_binary(`or`, StUint) make_binary(`and`, StUint) make_binary(`xor`, StUint) -proc `shr`*(x: StUint, y: SomeInteger): StUint {.inline, noSideEffect.} = +func `shr`*(x: StUint, y: SomeInteger): StUint {.inline.} = result.data = x.data shr y -proc `shl`*(x: StUint, y: SomeInteger): StUint {.inline, noSideEffect.} = +func `shl`*(x: StUint, y: SomeInteger): StUint {.inline.} = result.data = x.data shl y import ./private/uint_highlow @@ -86,3 +86,11 @@ func one*[bits: static[int]](T: typedesc[Stuint[bits] or Stint[bits]]): T {.inli func zero*[bits: static[int]](T: typedesc[Stuint[bits] or Stint[bits]]): T {.inline.} = discard + +import ./private/uint_exp, math + +func pow*(x: StUint, y: Natural): StUint {.inline.} = + when x.data is UintImpl: + result.data = x.data.pow(y) + else: + result.data = x.data ^ y diff --git a/tests/property_based.nim b/tests/property_based.nim index e77f1e3..be3bdab 100644 --- a/tests/property_based.nim +++ b/tests/property_based.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../stint, unittest, quicktest +import ../stint, unittest, quicktest, math const itercount = 1000 @@ -216,3 +216,16 @@ suite "Property-based testing (testing with random inputs) - uint64 on 64-bit / tz = tx div ty check(cast[uint](tz) == x div y) + + quicktest "pow", itercount do(x: uint(min=0, max=hi), y: int(min = 0, max = high(int))): + + when sizeof(int) == 8: + let + tx = cast[StUint[64]](x) + tz = tx.pow(y) + else: + let + tx = cast[StUint[32]](x) + tz = tx.pow(y) + + check(cast[uint](tz) == x ^ y) diff --git a/tests/property_based_uint256.nim b/tests/property_based_uint256.nim index 1688fdd..defd118 100644 --- a/tests/property_based_uint256.nim +++ b/tests/property_based_uint256.nim @@ -297,6 +297,8 @@ suite "Property-based testing (testing with random inputs) of Uint256": ttm_z = ttm_x mod ttm_y mp_z = mp_x mod mp_y + check ttm_z.asSt == mp_z + quicktest "`div`", itercount do(x0: uint(min=0, max=hi), x1: uint(min=0, max=hi), x2: uint(min=0, max=hi), @@ -319,3 +321,21 @@ suite "Property-based testing (testing with random inputs) of Uint256": ttm_z = ttm_x div ttm_y mp_z = mp_x div mp_y + check ttm_z.asSt == mp_z + + quicktest "pow", itercount do(x0: uint(min=0, max=hi), + x1: uint(min=0, max=hi), + x2: uint(min=0, max=hi), + x3: uint(min=0, max=hi), + y : int(min=0, max=high(int))): + + let + x = [x0, x1, x2, x3] + ttm_x = x.asTT + mp_x = cast[StUint[256]](x) + + let + ttm_z = ttm_x.pow(y.uint) + mp_z = mp_x.pow y + + check ttm_z.asSt == mp_z diff --git a/tests/test_uint_exp.nim b/tests/test_uint_exp.nim new file mode 100644 index 0000000..c862898 --- /dev/null +++ b/tests/test_uint_exp.nim @@ -0,0 +1,28 @@ +# 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, math + +suite "Testing unsigned exponentiation": + test "Simple exponentiation 5^3": + + let + a = 5'u64 + b = 3 + u = a.stuint(64) + + check: cast[uint64](u.pow(b)) == a ^ b + + test "12 ^ 34 == 4922235242952026704037113243122008064": + # https://www.wolframalpha.com/input/?i=12+%5E+34 + let + a = 12.stuint(256) + b = 34 + + check: a.pow(b) == "4922235242952026704037113243122008064".u256