diff --git a/stint/intops.nim b/stint/intops.nim index 90ed0d2..293c3b4 100644 --- a/stint/intops.nim +++ b/stint/intops.nim @@ -7,155 +7,204 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import ./private/[datatypes] +import + ./private/datatypes, + ./private/uint_bitwise, + ./private/uint_shift, + ./private/uint_addsub, + ./uintops export StInt -#export IntImpl, intImpl, UintImpl, uintImpl, bitsof # TODO: remove the need to export those -#import ./private/initialization +const + signMask = 1.Word shl WordBitWidth + clearSignMask = not signMask -func zero*[bits: static[int]](T: typedesc[StInt[bits]]): T {.inline.} = +# Signedness +# -------------------------------------------------------- +{.push raises: [], inline, noinit, gcsafe.} + +func sign*(a: StInt): int = + if a.imp.isZero: return 0 + if a.limbs[^1] < signMask: 1 + else: -1 + +func isNegative*(a: StInt): bool = + a.sign < 0 + +func clearSign(a: var StInt) = + a.limbs[^1] = a.limbs[^1] and clearSignMask + +func setSign(a: var StInt) = + a.limbs[^1] = a.limbs[^1] or signMask + +func negate*(a: var StInt) = + a.imp.bitnot(a.imp) + a.imp.inc + +func neg*(a: StInt): StInt = + result.imp.bitnot(a.imp) + result.imp.inc + +func abs*(a: StInt): StInt = + if a.isNegative: + a.neg + else: + a + +func `-`*(a: StInt): StInt = + a.neg + +{.pop.} + +# Initialization +# -------------------------------------------------------- +{.push raises: [], inline, noinit, gcsafe.} + +func setZero*(a: var StInt) = + ## Set ``a`` to 0 + a.imp.setZero + +func setOne*(a: var StInt) = + a.imp.setOne + +func zero*[bits: static[int]](T: typedesc[StInt[bits]]): T = ## Returns the zero of the input type - discard - -#[ -func one*[bits: static[int]](T: typedesc[StUint[bits]]): T {.inline.} = + result.setZero + +func one*[bits: static[int]](T: typedesc[StInt[bits]]): T = ## Returns the one of the input type - result.data = one(type result.data) + result.setOne -import ./private/[int_addsub, uint_addsub] +func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] = + # The highest signed int has representation + # 0b0111_1111_1111_1111 .... + # so we only have to unset the most significant bit. + for i in 0 ..< result.limbs.len: + result[i] = high(Word) + result.clearSign -func `+`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = - ## Integer addition - result.data = x.data + y.data -func `+=`*(x: var SomeBigInteger, y: SomeBigInteger) {.inline.} = - ## Integer addition - x.data += y.data -func `-`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = - ## Integer substraction - result.data = x.data - y.data -func `-=`*(x: var SomeBigInteger, y: SomeBigInteger) {.inline.} = - ## Integer substraction - x.data -= y.data +func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] = + # The lowest signed int has representation + # 0b1000_0000_0000_0000 .... + # so we only have to set the most significant bit. + result.setZero + result.setSign -import ./private/int_negabs +{.pop.} -func `-`*(x: StInt): StInt {.inline.} = - ## Returns true if input is zero - ## false otherwise - result.data = -x.data +# Comparisons +# -------------------------------------------------------- +{.push raises: [], inline, noinit, gcsafe.} -func abs*(x: StInt): StInt {.inline.} = - ## Returns true if input is zero - ## false otherwise - result.data = abs(x.data) +func isZero*(a: StInt): bool = + a.imp.isZero -import ./private/[int_mul, uint_mul] - -func `*`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = - ## Integer multiplication - result.data = x.data * y.data - -import ./private/[int_div, uint_div] - -func `div`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = - ## Integer division - result.data = x.data div y.data -func `mod`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = - ## Integer modulo - ## This returns the remainder of x / y. - ## i.e. x = y * quotient + remainder - result.data = x.data mod y.data -func divmod*(x, y: SomeBigInteger): tuple[quot, rem: SomeBigInteger] {.inline.} = - ## Fused integer division and modulo - ## Return both the quotient and remainder - ## of x / y - (result.quot.data, result.rem.data) = divmod(x.data, y.data) - -import ./private/[int_comparison, uint_comparison] - -func `<`*(x, y: SomeBigInteger): bool {.inline.} = - ## Unsigned `less than` comparison - x.data < y.data -func `<=`*(x, y: SomeBigInteger): bool {.inline.} = - ## Unsigned `less or equal` comparison - x.data <= y.data -func `==`*(x, y: SomeBigInteger): bool {.inline.} = +func `==`*(a, b: StInt): bool = ## Unsigned `equal` comparison - x.data == y.data -export `<`, `<=`, `==` # Address Generic Instantiation too nested: https://github.com/status-im/nim-stint/pull/66#issuecomment-427557655 + a.imp == b.imp -# TODO these exports are needed for the SomeInteger versions - move to stew? -export isZero, isOdd, isEven, isNegative +func `<`*(a, b: StInt): bool = + ## Unsigned `less than` comparison + let + aSign = a.Sign + bSign = b.Sign -func isZero*(x: SomeBigInteger): bool {.inline.} = + if aSign >= 0: + if bSign < 0: + return false + elif bSign >= 0: + return true + + a.imp < b.imp + +func `<=`*(a, b: StInt): bool = + ## Unsigned `less or equal` comparison + not(b < a) + +func isOdd*(a: StInt): bool = + ## Returns true if input is off + ## false otherwise + bool(a[0] and 1) + +func isEven*(a: StInt): bool = ## Returns true if input is zero ## false otherwise - x.data.isZero + not a.isOdd() -func isNegative*(x: StInt): bool {.inline.} = - ## Returns true if input is negative (< 0) - ## false otherwise - x.data.isNegative +{.pop.} -func isOdd*(x: SomeBigInteger): bool {.inline.} = - ## Returns true if input is zero - ## false otherwise - x.data.isOdd +# Bitwise operations +# -------------------------------------------------------- +{.push raises: [], inline, noinit, gcsafe.} -func isEven*(x: SomeBigInteger): bool {.inline.} = - ## Returns true if input is zero - ## false otherwise - x.data.isEven +func `not`*(a: StInt): StInt = + ## Bitwise complement of unsigned integer a + ## i.e. flips all bits of the input + result.imp.bitnot(a.imp) -export isEven, isOdd +func `or`*(a, b: StInt): StInt = + ## `Bitwise or` of numbers a and b + result.imp.bitor(a.imp, b.imp) -import ./private/[int_bitwise_ops, uint_bitwise_ops] +func `and`*(a, b: StInt): StInt = + ## `Bitwise and` of numbers a and b + result.imp.bitand(a.imp, b.imp) -func `not`*(x: SomeBigInteger): SomeBigInteger {.inline.}= - ## Bitwise `not` i.e. flips all bits of the input - result.data = x.data.not -func `or`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}= - ## Bitwise `or` - result.data = x.data or y.data -func `and`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}= - ## Bitwise `and` - result.data = x.data and y.data -func `xor`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}= - ## Bitwise `xor` - result.data = x.data xor y.data +func `xor`*(a, b: StInt): StInt = + ## `Bitwise xor` of numbers x and y + result.imp.bitxor(a.imp, b.imp) -func `shr`*(x: SomeBigInteger, y: SomeInteger): SomeBigInteger {.inline.} = - result.data = x.data shr y +{.pop.} # End noInit -func `shl`*(x: SomeBigInteger, y: SomeInteger): SomeBigInteger {.inline.} = - result.data = x.data shl y +{.push raises: [], inline, gcsafe.} -import ./private/[int_highlow, uint_highlow] +func `shr`*(a: StInt, k: SomeInteger): StInt = + ## Shift right by k bits, arithmetically + ## ~(~a >> k) + var tmp: type a + result.imp.bitnot(a.imp) + tmp.imp.shiftRight(result.imp, k) + result.imp.bitnot(tmp.imp) -func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] {.inline.} = - result.data = high(type result.data) -func high*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} = - result.data = high(type result.data) +func `shl`*(a: StInt, k: SomeInteger): StInt = + ## Shift left by k bits + result.imp.shiftLeft(a.imp, k) -func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] {.inline.} = - result.data = low(type result.data) -func low*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} = - result.data = low(type result.data) +{.pop.} -import ./private/uint_exp, math +# Addsub +# -------------------------------------------------------- +{.push raises: [], inline, noinit, gcsafe.} -func pow*(x: StUint, y: Natural): StUint {.inline.} = - ## Returns x raised at the power of y - when x.data is UintImpl: - result.data = x.data.pow(y) - else: - result.data = x.data ^ y +#[ +func `+`*(a, b: StInt): StInt = + ## Addition for multi-precision unsigned int + result.sum(a, b) -func pow*(x: StUint, y: StUint): StUint {.inline.} = - ## Returns x raised at the power of y - when x.data is UintImpl: - result.data = x.data.pow(y.data) - else: - result.data = x.data ^ y.data -]# \ No newline at end of file +func `+=`*(a: var StInt, b: StInt) = + ## In-place addition for multi-precision unsigned int + a.sum(a, b) + +func `-`*(a, b: StInt): StInt = + ## Substraction for multi-precision unsigned int + result.diff(a, b) + +func `-=`*(a: var StInt, b: StInt) = + ## In-place substraction for multi-precision unsigned int + a.diff(a, b) + +func inc*(a: var StInt, w: Word = 1) = + +func `+`*(a: StInt, b: SomeUnsignedInt): StInt = + ## Addition for multi-precision unsigned int + ## with an unsigned integer + result.sum(a, Word(b)) + +func `+=`*(a: var StInt, b: SomeUnsignedInt) = + ## In-place addition for multi-precision unsigned int + ## with an unsigned integer + a.inc(Word(b)) +]# + +{.pop.} \ No newline at end of file diff --git a/stint/io.nim b/stint/io.nim index bd311bb..e85c5e5 100644 --- a/stint/io.nim +++ b/stint/io.nim @@ -46,32 +46,24 @@ template static_check_size(T: typedesc[SomeInteger], bits: static[int]) = func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}= ## Converts an integer to an arbitrary precision integer. when sizeof(n) > sizeof(Word): - result.limbs[0] = Word(n and Word.high) + result.limbs[0] = Word(n and Word.high) result.limbs[1] = Word(n shr WordBitWidth) else: - result.limbs[0] = Word(n) + result.limbs[0] = Word(n) -# func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}= -# ## Converts an integer to an arbitrary precision signed integer. -# -# when result.data is IntImpl: -# static_check_size(T, bits) -# when T is SomeSignedInt: -# if n < 0: -# # TODO: when bits >= 128, cannot create from -# # low(int8-64) -# # see: status-im/nim-stint/issues/92 -# assignLo(result.data, -n) -# result = -result -# else: -# assignLo(result.data, n) -# else: -# assignLo(result.data, n) -# else: -# result.data = (type result.data)(n) +func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}= + ## Converts an integer to an arbitrary precision signed integer. + when T is SomeUnsignedInt: + result.imp = stuint(n, bits) + else: + if n < 0: + result.imp = stuint(-n, bits) + result.negate + else: + result.imp = stuint(n, bits) -# func to*(a: SomeInteger, T: typedesc[Stint]): T = -# stint(a, result.bits) +func to*(a: SomeInteger, T: typedesc[Stint]): T = + stint(a, result.bits) func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T = stuint(a, result.bits) @@ -83,9 +75,6 @@ func truncate*(num: StInt or StUint, T: typedesc[SomeInteger]): T {.inline.}= ## For signed result type, result is undefined if input does not fit in the target type. result = T(num.leastSignificantWord()) -func toInt*(num: StInt or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}= - num.truncate(int) - func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} = ## unsigned int to unsigned int conversion ## smaller to bigger bits conversion will have the same value @@ -307,46 +296,19 @@ func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10): reverse(result) -# func toString*[bits: static[int]](num: Stint[bits], radix: static[int8] = 10): string = -# ## Convert a Stint or StUint to string. -# ## In case of negative numbers: -# ## - they are prefixed with "-" for base 10. -# ## - if not base 10, they are returned raw in two-complement form. +func toString*[bits: static[int]](num: StInt[bits], radix: static[int8] = 10): string = + ## Convert a Stint or StUint to string. + ## In case of negative numbers: + ## - they are prefixed with "-" for base 10. + ## - if not base 10, they are returned raw in two-complement form. + let isNeg = num.isNegative + if radix == 10 and isNeg: + "-" & toString(num.neg.imp, radix) + else: + toString(num.imp, radix) -# static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported" -# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26) - -# const hexChars = "0123456789abcdef" -# const base = radix.int8.StUint(bits) - -# result = "" - -# type T = StUint[bits] -# let isNeg = num.isNegative -# let num = convert[T](if radix == 10 and isNeg: -num -# else: num) - -# var (q, r) = divmod(num, base) - -# while true: -# when bitsof(r.data) <= 64: -# result.add hexChars[r.data.int] -# else: -# result.add hexChars[r.truncate(int)] -# if q.isZero: -# break -# (q, r) = divmod(q, base) - -# if isNeg and radix == 10: -# result.add '-' - -# reverse(result) - -# func `$`*(num: Stint or StUint): string {.inline.}= -# when num.data is SomeInteger: -# $num.data -# else: -# toString(num, 10) +func `$`*(num: StInt or StUint): string {.inline.}= + toString(num, 10) func toHex*[bits: static[int]](num: StInt[bits] or StUint[bits]): string {.inline.}= ## Convert to a hex string. diff --git a/stint/private/datatypes.nim b/stint/private/datatypes.nim index 569245c..b485ab0 100644 --- a/stint/private/datatypes.nim +++ b/stint/private/datatypes.nim @@ -40,7 +40,7 @@ type StInt*[bits: static[int]] = object ## Stack-based integer ## Signed - limbs*: array[bits.wordsRequired, Word] + imp*: StUint[bits] # {.borrow: `.`.} only works with nim-devel # StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits] @@ -94,6 +94,10 @@ func usedBitsAndWords*(a: openArray[Word]): tuple[bits, words: int] = # Accessors # -------------------------------------------------------- +template limbs*(a: StInt): untyped = + # TODO: remove this when we switch to borrow `.` + a.imp.limbs + template `[]`*(a: SomeBigInteger, i: SomeInteger or BackwardsIndex): Word = a.limbs[i] diff --git a/stint/private/int_addsub.nim b/stint/private/int_addsub.nim deleted file mode 100644 index b8d38e4..0000000 --- a/stint/private/int_addsub.nim +++ /dev/null @@ -1,49 +0,0 @@ -# Stint -# Copyright 2018-2023 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, ./conversion, ./int_comparison, - ./uint_addsub, ./uint_comparison - -func `+`*(x, y: IntImpl): IntImpl {.inline.}= - # Addition for multi-precision signed int. - type SubTy = type x.hi - result.lo = x.lo + y.lo - result.hi = (result.lo < y.lo).toSubtype(SubTy) + x.hi + y.hi - - when compileOption("boundChecks"): - if unlikely( - not(result.isNegative xor x.isNegative) or - not(result.isNegative xor y.isNegative) - ): - return - raise newException(OverflowDefect, "Addition overflow") - -func `+=`*(x: var IntImpl, y: IntImpl) {.inline.}= - ## In-place addition for multi-precision signed int. - x = x + y - -func `-`*(x, y: IntImpl): IntImpl {.inline.}= - # Substraction for multi-precision signed int. - - type SubTy = type x.hi - result.lo = x.lo - y.lo - result.hi = x.hi - y.hi - (x.lo < y.lo).toSubtype(SubTy) - - when compileOption("boundChecks"): - if unlikely( - not(result.isNegative xor x.isNegative) or - not(result.isNegative xor y.isNegative.not) - ): - return - raise newException(OverflowDefect, "Substraction underflow") - -func `-=`*(x: var IntImpl, y: IntImpl) {.inline.}= - ## In-place substraction for multi-precision signed int. - x = x - y diff --git a/stint/private/int_bitwise_ops.nim b/stint/private/int_bitwise_ops.nim deleted file mode 100644 index ab9bc5c..0000000 --- a/stint/private/int_bitwise_ops.nim +++ /dev/null @@ -1,72 +0,0 @@ -# Stint -# Copyright 2018-2023 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, ./bitops2_priv, ./uint_bitwise_ops, ./compiletime_cast - -func `not`*(x: IntImpl): IntImpl {.inline.}= - ## Bitwise complement of unsigned integer x - applyHiLo(x, `not`) - -func `or`*(x, y: IntImpl): IntImpl {.inline.}= - ## `Bitwise or` of numbers x and y - applyHiLo(x, y, `or`) - -func `and`*(x, y: IntImpl): IntImpl {.inline.}= - ## `Bitwise and` of numbers x and y - applyHiLo(x, y, `and`) - -func `xor`*(x, y: IntImpl): IntImpl {.inline.}= - ## `Bitwise xor` of numbers x and y - applyHiLo(x, y, `xor`) - -func `shl`*(x: IntImpl, y: SomeInteger): IntImpl {.inline.}= - ## Compute the `shift left` operation of x and y - # Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1` - - # TODO: would it be better to reimplement this with words iteration? - const halfSize: type(y) = bitsof(x) div 2 - type HiType = type(result.hi) - - if y == 0: - return x - elif y == halfSize: - result.hi = convert[HiType](x.lo) - elif y < halfSize: - # `shr` in this equation uses uint version - result.hi = (x.hi shl y) or convert[HiType](x.lo shr (halfSize - y)) - result.lo = x.lo shl y - else: - result.hi = convert[HiType](x.lo shl (y - halfSize)) - -template createShr(name, operator: untyped) = - template name(x, y: SomeInteger): auto = - operator(x, y) - - func name*(x: IntImpl, y: SomeInteger): IntImpl {.inline.}= - ## Compute the `arithmetic shift right` operation of x and y - ## Similar to C standard, result is undefined if y is bigger - ## than the number of bits in x. - const halfSize: type(y) = bitsof(x) div 2 - type LoType = type(result.lo) - if y == 0: - return x - elif y == halfSize: - result.lo = convert[LoType](x.hi) - result.hi = name(x.hi, halfSize-1) - elif y < halfSize: - result.lo = (x.lo shr y) or convert[LoType](x.hi shl (halfSize - y)) - result.hi = name(x.hi, y) - else: - result.lo = convert[LoType](name(x.hi, (y - halfSize))) - result.hi = name(x.hi, halfSize-1) - -createShr(shrOfShr, `shr`) - -template `shr`*(a, b: typed): untyped = - shrOfShr(a, b) diff --git a/stint/private/int_comparison.nim b/stint/private/int_comparison.nim deleted file mode 100644 index 2f4adf9..0000000 --- a/stint/private/int_comparison.nim +++ /dev/null @@ -1,49 +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_comparison - -func isZero*(n: SomeSignedInt): bool {.inline.} = - n == 0 - -func isZero*(n: IntImpl): bool {.inline.} = - n.hi.isZero and n.lo.isZero - -func isNegative*(n: SomeSignedInt): bool {.inline.} = - n < 0 - -func isNegative*(n: IntImpl): bool {.inline.} = - ## Returns true if a number is negative: - n.hi.isNegative - -func `<`*(x, y: IntImpl): bool {.inline.}= - # Lower comparison for multi-precision integers - x.hi < y.hi or - (x.hi == y.hi and x.lo < y.lo) - -func `==`*(x, y: IntImpl): bool {.inline.}= - # Equal comparison for multi-precision integers - x.hi == y.hi and x.lo == y.lo - -func `<=`*(x, y: IntImpl): bool {.inline.}= - # Lower or equal comparison for multi-precision integers - x.hi < y.hi or - (x.hi == y.hi and x.lo <= y.lo) - -func isOdd*(x: SomeSignedInt): bool {.inline.}= - bool(x and 1) - -func isEven*(x: SomeSignedInt): bool {.inline.}= - not x.isOdd - -func isEven*(x: IntImpl): bool {.inline.}= - x.lo.isEven - -func isOdd*(x: IntImpl): bool {.inline.}= - not x.isEven diff --git a/stint/private/int_highlow.nim b/stint/private/int_highlow.nim deleted file mode 100644 index 1337065..0000000 --- a/stint/private/int_highlow.nim +++ /dev/null @@ -1,27 +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, ./int_bitwise_ops, ./initialization, ./uint_highlow, typetraits - -# XXX There's some Araq reason why this isn't part of the std lib.. -func high(T: typedesc[SomeUnsignedInt]): T = - not T(0) - -func high*[T, T2](_: typedesc[IntImpl[T, T2]]): IntImpl[T, T2] {.inline.}= - # The highest signed int has representation - # 0b0111_1111_1111_1111 .... - # so we only have to unset the most significant bit. - result.hi = high(type result.hi) - result.lo = high(type result.lo) - -func low*[T, T2](_: typedesc[IntImpl[T, T2]]): IntImpl[T, T2] {.inline.}= - # The lowest signed int has representation - # 0b1000_0000_0000_0000 .... - # so we only have to set the most significant bit. - not high(IntImpl[T, T2]) diff --git a/stint/private/int_negabs.nim b/stint/private/int_negabs.nim deleted file mode 100644 index 8803123..0000000 --- a/stint/private/int_negabs.nim +++ /dev/null @@ -1,29 +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, - ./initialization, ./int_highlow, - ./int_addsub, ./int_comparison, ./int_bitwise_ops - -func `-`*(x: IntImpl): IntImpl {.inline.}= - # Negate a multi-precision signed int. - - when compileOption("boundChecks"): - if unlikely(x == low(type x)): - raise newException(OverflowError, "The lowest negative number cannot be negated") - - result = not x - result += one(type x) - -func abs*[T: IntImpl](x: T): T {.inline.}= - ## Returns the absolute value of a signed int. - - result = if x.isNegative: -x - else: x diff --git a/stint/private/uint_div.nim b/stint/private/uint_div.nim index c0a0798..b033a4c 100644 --- a/stint/private/uint_div.nim +++ b/stint/private/uint_div.nim @@ -164,7 +164,7 @@ func divRem*( let rLen = bLen if unlikely(bBits == 0): - raise newException(DivByZeroError, "You attempted to divide by zero") + raise newException(DivByZeroDefect, "You attempted to divide by zero") if aBits < bBits: # if a uses less bits than b, diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index f3264a5..0c5fe55 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -5,5 +5,6 @@ template ctCheck*(cond: untyped) = doAssert(cond) template ctTest*(name: string, body: untyped) = - body - echo "[OK] compile time ", name + block: + body + echo "[OK] compile time ", name diff --git a/tests/test_int_signedness.nim b/tests/test_int_signedness.nim new file mode 100644 index 0000000..8e78a12 --- /dev/null +++ b/tests/test_int_signedness.nim @@ -0,0 +1,116 @@ +# Stint +# Copyright 2018-2023 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 chkAddition(chk, a, b, c, bits: untyped) = + block: + let x = stuint(a, bits) + let y = stuint(b, bits) + chk x + y == stuint(c, bits) +]# + +template testSignedness(chk, tst: untyped) = + tst "positive sign": + let a = stint(1, 128) + chk a.sign > 0 + chk a.isNegative == false + + let b = stint(uint32.high, 128) + chk b.sign > 0 + chk b.isNegative == false + + let c = stint(uint64.high, 128) + chk c.sign > 0 + chk c.isNegative == false + + let aa = stint(1, 256) + chk aa.sign > 0 + chk aa.isNegative == false + + let bb = stint(uint32.high, 256) + chk bb.sign > 0 + chk bb.isNegative == false + + let cc = stint(uint64.high, 256) + chk cc.sign > 0 + chk cc.isNegative == false + + var zz: StInt[128] + zz.setOne + chk zz.sign > 0 + chk zz.isNegative == false + + let yy = StInt[128].one + chk yy.sign > 0 + chk yy.isNegative == false + + tst "zero sign": + let a = stint(0, 128) + chk a.sign == 0 + chk a.isNegative == false + + let aa = stint(0, 256) + chk aa.sign == 0 + chk aa.isNegative == false + + var zz: StInt[128] + zz.setZero + chk zz.sign == 0 + chk zz.isNegative == false + + let yy = StInt[128].zero + chk yy.sign == 0 + chk yy.isNegative == false + + tst "negative sign": + let a = stint(-1, 128) + chk a.sign < 0 + chk a.isNegative == true + + let aa = stint(-1, 256) + chk aa.sign < 0 + chk aa.isNegative == true + + let zz = -1.i128 + chk zz.sign < 0 + chk zz.isNegative == true + + let yy = -1.i256 + chk yy.sign < 0 + chk yy.isNegative == true + + tst "abs": + let a = -1.i128 + let aa = a.abs + chk aa == 1.i128 + + let b = -1.i256 + let bb = b.abs + chk bb == 1.i256 + + tst "negate": + var a = -1.i128 + a.negate + chk a == 1.i128 + + var b = -1.i256 + b.negate + chk b == 1.i256 + + let c = -1.i256 + let d = -c + chk d == 1.i256 + +static: + testSignedness(ctCheck, ctTest) + +suite "Signed integer signedness": + testSignedness(check, test)