diff --git a/src/uint_init.nim b/src/init.nim similarity index 53% rename from src/uint_init.nim rename to src/init.nim index 76358da..d013198 100644 --- a/src/uint_init.nim +++ b/src/init.nim @@ -33,3 +33,36 @@ func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}= r_ptr[r_ptr[].len - 1] = n else: result.data = (type result.data)(n) + +func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}= + + when result.data is UintImpl: + when getSize(n) > bits: + # To avoid a costly runtime check, we refuse storing into StUint types smaller + # than the input type. + raise newException(ValueError, "Input " & $n & " (" & $T & + ") cannot be stored in a multi-precision " & + $bits & "-bit integer." & + "\nUse a smaller input type instead. This is a compile-time check" & + " to avoid a costly run-time bit_length check at each StUint initialization.") + else: + let r_ptr = cast[ptr array[bits div (sizeof(T) * 8), T]](result.addr) + when system.cpuEndian == littleEndian: + # "Least significant byte are at the beginning" + if n < 0: + r_ptr[0] = -n + result = -result + else: + r_ptr[0] = n + else: + if n < 0: + r_ptr[r_ptr[].len - 1] = -n + result = -result + else: + r_ptr[r_ptr[].len - 1] = n + else: + if n < 0: + result.data = (type result.data)(-n) + result = -result + else: + result.data = (type result.data)(n) diff --git a/src/int_public.nim b/src/int_public.nim new file mode 100644 index 0000000..a71fd6e --- /dev/null +++ b/src/int_public.nim @@ -0,0 +1,79 @@ +# Mpint +# 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 ./private/datatypes, macros +export StInt, IntImpl, intImpl # TODO remove the need to export intImpl and this macro + +type + Int128* = Stint[128] + Int256* = Stint[256] + +template make_conv(conv_name: untyped, size: int): untyped = + func `convname`*(n: SomeInteger): Stint[size] {.inline, noInit.}= + n.stint(size) + +make_conv(i128, 128) +make_conv(i256, 256) + +template make_unary(op, ResultTy): untyped = + func `op`*(x: Stint): ResultTy {.noInit, inline.} = + when ResultTy is Stint: + result.data = op(x.data) + else: + op(x.data) + export op + +template make_binary(op, ResultTy): untyped = + func `op`*(x, y: Stint): ResultTy {.noInit, inline.} = + when ResultTy is Stint: + result.data = op(x.data, y.data) + else: + op(x.data, y.data) + export `op` + +template make_binary_inplace(op): untyped = + func `op`*(x: var Stint, y: Stint) {.inline.} = + op(x.data, y.data) + export op + +import ./private/int_addsubneg + +make_binary(`+`, Stint) +make_binary_inplace(`+=`) +make_unary(`-`, Stint) +# make_binary(`-`, Stint) +# make_binary_inplace(`-=`) + +# import ./private/int_mul +# make_binary(`*`, Stint) + +# import ./private/int_div + +# make_binary(`div`, Stint) +# make_binary(`mod`, Stint) +# func divmod*(x, y: Stint): tuple[quot, rem: Stint] {.noInit, inline.} = +# (result.quot.data, result.rem.data) = divmod(x.data, y.data) + +import ./private/int_comparison + +make_binary(`<`, bool) +make_binary(`<=`, bool) +make_binary(`==`, bool) +func isZero*(x: Stint): bool {.inline.} = isZero x.data + +import ./private/int_bitwise_ops + +make_unary(`not`, Stint) +make_binary(`or`, Stint) +make_binary(`and`, Stint) +make_binary(`xor`, Stint) +# proc `shr`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} = +# result.data = x.data shr y +# proc `shl`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} = +# result.data = x.data shl y diff --git a/src/private/bithacks.nim b/src/private/bithacks.nim index a6d6d01..116451d 100644 --- a/src/private/bithacks.nim +++ b/src/private/bithacks.nim @@ -25,7 +25,7 @@ func countLeadingZeroBits*(n: UintImpl): int {.inline.} = n.lo.countLeadingZeroBits + maxHalfRepr else: hi_clz -func msb*[T: SomeInteger](n: T): T = +func msb*[T: SomeInteger](n: T): T {.inline.}= ## Returns the most significant bit of an integer. when T is int64 or (T is int and sizeof(int) == 8): @@ -42,7 +42,7 @@ func msb*[T: SomeInteger](n: T): T = const msb_pos = sizeof(T) * 8 - 1 result = T(cast[Uint](n) shr msb_pos) -func msb*(n: IntImpl): auto = +func msb*(n: IntImpl): auto {.inline.}= ## Returns the most significant bit of an arbitrary precision integer. result = msb most_significant_word(n) diff --git a/src/private/datatypes.nim b/src/private/datatypes.nim index 31799d1..96aa7fe 100644 --- a/src/private/datatypes.nim +++ b/src/private/datatypes.nim @@ -109,8 +109,6 @@ macro getSize*(x: typed): untyped = type # ### Private ### # - # If this is not in the same type section - # the compiler has trouble BaseUint* = UintImpl or SomeUnsignedInt UintImpl*[Baseuint] = object @@ -120,6 +118,7 @@ type hi*, lo*: BaseUint IntImpl*[Baseuint] = object + # Ints are implemented in terms of uints when system.cpuEndian == littleEndian: lo*, hi*: BaseUint else: diff --git a/src/private/initialization.nim b/src/private/initialization.nim index 46ccd13..96d709e 100644 --- a/src/private/initialization.nim +++ b/src/private/initialization.nim @@ -40,3 +40,13 @@ func one*[T: BaseUint](_: typedesc[T]): T {.inline.}= r_ptr[0] = 1 else: r_ptr[r_ptr[].len - 1] = 1 + +func one*[T: IntImpl](_: typedesc[T]): T {.inline.}= + when T is SomeInteger: + result = T(1) + else: + let r_ptr = cast[ptr array[getSize(result) div 8, byte]](result.addr) + when system.cpuEndian == bigEndian: + r_ptr[0] = 1 + else: + r_ptr[r_ptr[].len - 1] = 1 diff --git a/src/private/int_addsubneg.nim b/src/private/int_addsubneg.nim new file mode 100644 index 0000000..6a68b5a --- /dev/null +++ b/src/private/int_addsubneg.nim @@ -0,0 +1,26 @@ +# Mpint +# 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, ./int_bitwise_ops, ./initialization + +func `+=`*(x: var IntImpl, y: IntImpl) {.inline.}= + ## In-place addition for multi-precision signed int + + type SubTy = type x.lo + x.lo += y.lo + x.hi += (x.lo < y.lo).toSubtype(SubTy) + y.hi + +func `+`*(x, y: UintImpl): UintImpl {.noInit, inline.}= + # Addition for multi-precision signed int + result = x + result += y + +func `-`*[T: IntImpl](x: T): T {.noInit, inline.}= + result = not x + result += one(T) diff --git a/src/private/int_bitwise_ops.nim b/src/private/int_bitwise_ops.nim new file mode 100644 index 0000000..b363863 --- /dev/null +++ b/src/private/int_bitwise_ops.nim @@ -0,0 +1,31 @@ +# Mpint +# 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, ./as_words + + +func `not`*(x: IntImpl): IntImpl {.noInit, inline.}= + ## Bitwise complement of unsigned integer x + m_asWordsZip(result, x, ignoreEndianness = true): + result = not x + +func `or`*(x, y: IntImpl): IntImpl {.noInit, inline.}= + ## `Bitwise or` of numbers x and y + m_asWordsZip(result, x, y, ignoreEndianness = true): + result = x or y + +func `and`*(x, y: IntImpl): IntImpl {.noInit, inline.}= + ## `Bitwise and` of numbers x and y + m_asWordsZip(result, x, y, ignoreEndianness = true): + result = x and y + +func `xor`*(x, y: IntImpl): IntImpl {.noInit, inline.}= + ## `Bitwise xor` of numbers x and y + m_asWordsZip(result, x, y, ignoreEndianness = true): + result = x xor y diff --git a/src/private/uint_mul.nim b/src/private/uint_mul.nim index cf43162..041a7c4 100644 --- a/src/private/uint_mul.nim +++ b/src/private/uint_mul.nim @@ -115,7 +115,7 @@ func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) = if result.lo.hi < z1.lo: result.hi += one(UintImpl[T]) -func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline.}= +func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline, noInit.}= ## Multiplication for multi-precision unsigned uint # # For our representation, it is similar to school grade multiplication diff --git a/src/mpint.nim b/src/stint.nim similarity index 82% rename from src/mpint.nim rename to src/stint.nim index b047a3b..5295369 100644 --- a/src/mpint.nim +++ b/src/stint.nim @@ -7,5 +7,5 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ./uint_public, ./uint_init -export uint_public, uint_init +import ./uint_public, ./int_public, ./init +export uint_public, int_public, init diff --git a/tests/property_based.nim b/tests/property_based.nim index 5cc884e..a2ec256 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 ../src/mpint, unittest, quicktest +import ../src/stint, unittest, quicktest const itercount = 1000 diff --git a/tests/property_based_uint256.nim b/tests/property_based_uint256.nim index 9b2efdc..a5d78f1 100644 --- a/tests/property_based_uint256.nim +++ b/tests/property_based_uint256.nim @@ -10,7 +10,7 @@ # Requires "https://github.com/status-im/nim-ttmath#master" # Note that currently importing both Stint and TTMath will crash the compiler for unknown reason -import ../src/mpint, unittest, quicktest, ttmath +import ../src/stint, unittest, quicktest, ttmath const itercount = 1000 diff --git a/tests/test_int_comparison.nim b/tests/test_int_comparison.nim new file mode 100644 index 0000000..36c4729 --- /dev/null +++ b/tests/test_int_comparison.nim @@ -0,0 +1,41 @@ +# Mpint +# 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 ../src/stint, unittest + +suite "Signed int - Testing comparison operators": + let + a = 10'i16.stint(16) + b = 15'i16.stint(16) + c = 150'u16 + + test "< operator": + check: + a < b + not (a + b < b) + not (a + a + a < b + b) + + test "<= operator": + check: + a <= b + not (a + b <= b) + a + a + a <= b + b + + test "> operator": + check: + b > a + not (b > a + b) + not (b + b > a + a + a) + + test ">= operator": + check: + b >= a + not (b >= a + b) + b + b >= a + a + a + diff --git a/tests/test_uint_addsub.nim b/tests/test_uint_addsub.nim index 007be7b..e8c0e05 100644 --- a/tests/test_uint_addsub.nim +++ b/tests/test_uint_addsub.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../src/mpint, unittest +import ../src/stint, unittest suite "Testing addition implementation": test "In-place addition gives expected result": diff --git a/tests/test_uint_bitwise.nim b/tests/test_uint_bitwise.nim index 0a782d9..39d2dba 100644 --- a/tests/test_uint_bitwise.nim +++ b/tests/test_uint_bitwise.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../src/mpint, unittest +import ../src/stint, unittest suite "Testing bitwise operations": let a = 100'i16.stuint(16) diff --git a/tests/test_uint_comparison.nim b/tests/test_uint_comparison.nim index 373b831..34728f2 100644 --- a/tests/test_uint_comparison.nim +++ b/tests/test_uint_comparison.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../src/mpint, unittest +import ../src/stint, unittest suite "Testing comparison operators": let diff --git a/tests/test_uint_endianness.nim b/tests/test_uint_endianness.nim index 00775d5..eea24f7 100644 --- a/tests/test_uint_endianness.nim +++ b/tests/test_uint_endianness.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../src/mpint, unittest +import ../src/stint, unittest suite "Testing byte representation": test "Byte representation conforms to the platform endianness": diff --git a/tests/test_uint_muldiv.nim b/tests/test_uint_muldiv.nim index 01b13ba..5c331f0 100644 --- a/tests/test_uint_muldiv.nim +++ b/tests/test_uint_muldiv.nim @@ -7,7 +7,7 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ../src/mpint, unittest +import ../src/stint, unittest suite "Testing multiplication implementation": test "Multiplication with result fitting in low half":