From d98b77c88d0267415a3855149beab9e2ab33eb15 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 10 Dec 2019 15:57:19 +0700 Subject: [PATCH] fixes #94, implement conversion constructor --- stint/io.nim | 112 +++++++++++ tests/all_tests.nim | 3 +- tests/test_conversion.nim | 340 ++++++++++++++++++++++++++++++++++ tests/test_int_addsub.nim | 59 +++--- tests/test_int_comparison.nim | 108 ++++++----- 5 files changed, 543 insertions(+), 79 deletions(-) create mode 100644 tests/test_conversion.nim diff --git a/stint/io.nim b/stint/io.nim index 9838b39..3af205f 100644 --- a/stint/io.nim +++ b/stint/io.nim @@ -82,6 +82,118 @@ func truncate*(num: Stint or StUint, T: typedesc[SomeInteger]): T {.inline.}= func toInt*(num: Stint or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}= num.truncate(int) +func bigToSmall(result: var (UintImpl | IntImpl), x: auto) {.inline.} = + when bitsof(x) == bitsof(result): + when type(result) is type(x): + result = x + else: + result = convert[type(result)](x) + else: + bigToSmall(result, x.lo) + +func smallToBig(result: var (UintImpl | IntImpl), x: auto) {.inline.} = + when bitsof(x) == bitsof(result): + when type(result) is type(x): + result = x + else: + result = convert[type(result)](x) + else: + smallToBig(result.lo, x) + +func stuint*(x: StUint, bits: static[int]): StUint[bits] {.inline.} = + ## unsigned int to unsigned int conversion + ## smaller to bigger bits conversion will have the same value + ## bigger to smaller bits conversion, the result is truncated + const N = bitsof(x.data) + when N < bits: + when N <= 64: + result = stuint(x.data, bits) + else: + smallToBig(result.data, x.data) + elif N > bits: + when bits <= 64: + result = stuint(x.truncate(type(result.data)), bits) + else: + bigToSmall(result.data, x.data) + else: + result = x + +func stuint*(x: StInt, bits: static[int]): StUint[bits] {.inline.} = + ## signed int to unsigned int conversion + ## current behavior is cast-like, copying bit pattern + ## or truncating if input does not fit into destination + const N = bitsof(x.data) + when N < bits: + when N <= 64: + type T = StUint[N] + result = stuint(convert[T](x).data, bits) + else: + smallToBig(result.data, x.data) + elif N > bits: + when bits <= 64: + result = stuint(x.truncate(type(result.data)), bits) + else: + bigToSmall(result.data, x.data) + else: + result = convert[type(result)](x) + +func stint*(x: StInt, bits: static[int]): StInt[bits] {.inline.} = + ## signed int to signed int conversion + ## will raise exception if input does not fit into destination + const N = bitsof(x.data) + when N < bits: + when N <= 64: + result = stint(x.data, bits) + else: + if x.isNegative: + smallToBig(result.data, (-x).data) + result = -result + else: + smallToBig(result.data, x.data) + elif N > bits: + const dmax = stint((type result).high, N) + # due to bug #92, we skip negative range check + #const dmin = stint((type result).low, N) + #template checkNegativeRange() = + # if x < dmin: raise newException(RangeError, "value out of range") + template checkPositiveRange() = + if x > dmax: raise newException(RangeError, "value out of range") + when bits <= 64: + if x.isNegative: + #checkNegativeRange() + result = stint((-x).truncate(type(result.data)), bits) + result = -result + else: + checkPositiveRange() + result = stint(x.truncate(type(result.data)), bits) + else: + if x.isNegative: + #checkNegativeRange() + bigToSmall(result.data, (-x).data) + result = -result + else: + checkPositiveRange() + bigToSmall(result.data, x.data) + else: + result = x + +func stint*(x: StUint, bits: static[int]): StInt[bits] {.inline.} = + const N = bitsof(x.data) + const dmax = stuint((type result).high, N) + if x > dmax: raise newException(RangeError, "value out of range") + when N < bits: + when N <= 64: + result = stint(x.data, bits) + else: + smallToBig(result.data, x.data) + elif N > bits: + when bits <= 64: + result = stint(x.truncate(type(result.data)), bits) + else: + bigToSmall(result.data, x.data) + else: + result = convert[type(result)](x) + func readHexChar(c: char): int8 {.inline.}= ## Converts an hex char to an int case c diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 7d2f610..45b138d 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -24,4 +24,5 @@ import test_int_endianness, test_int_boundchecks, test_int_bitwise -import test_io +import test_io, + test_conversion diff --git a/tests/test_conversion.nim b/tests/test_conversion.nim new file mode 100644 index 0000000..cc41fa5 --- /dev/null +++ b/tests/test_conversion.nim @@ -0,0 +1,340 @@ +# 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, strutils, test_helpers + +template chkStuintToStuint(chk: untyped, N, bits: static[int]) = + block: + let x = StUint[N].high + let y = stuint(0, N) + let z = stuint(1, N) + + let xx = stuint(x, bits) + let yy = stuint(y, bits) + let zz = stuint(z, bits) + + when N <= bits: + chk $x == $xx + else: + chk $xx == $(StUint[bits].high) + chk $y == $yy + chk $z == $zz + +template chkStintToStuint(chk: untyped, N, bits: static[int]) = + block: + let w = StInt[N].low + let x = StInt[N].high + let y = stint(0, N) + let z = stint(1, N) + let v = stint(-1, N) + + let ww = stuint(w, bits) + let xx = stuint(x, bits) + let yy = stuint(y, bits) + let zz = stuint(z, bits) + let vv = stuint(v, bits) + + when N <= bits: + chk $x == $xx + chk w.toHex == ww.toHex + chk v.toHex == vv.toHex + else: + chk ww == stuint(0, bits) + chk $xx == $(StUint[bits].high) + chk $vv == $(StUint[bits].high) + + chk $y == $yy + chk $z == $zz + +template chkStintToStint(chk: untyped, N, bits: static[int]) = + block: + # TODO add low value tests if bug #92 fixed + let y = stint(0, N) + let z = stint(1, N) + let v = stint(-1, N) + + let yy = stint(y, bits) + let zz = stint(z, bits) + let vv = stint(v, bits) + + when bits >= N: + let x = StInt[N].high + let xx = stint(x, bits) + chk $x == $xx + else: + let x = fromHex(StInt[N], toHex(StInt[bits].high)) + let xx = stint(x, bits) + chk $x == $xx + + chk $y == $yy + chk $z == $zz + chk $v == $vv + +template chkStuintToStint(chk: untyped, N, bits: static[int]) = + block: + let y = stuint(0, N) + let z = stuint(1, N) + let v = StUint[N].high + + let yy = stint(y, bits) + let zz = stint(z, bits) + + when bits <= N: + when nimvm: + # expect(...) cannot run in Nim VM + discard + else: + expect(RangeError): + discard stint(v, bits) + else: + let vv = stint(v, bits) + chk v.toHex == vv.toHex + + chk $y == $yy + chk $z == $zz + +template testConversion(chk, tst: untyped) = + tst "stuint to stuint": + chkStuintToStuint(chk, 8, 8) + chkStuintToStuint(chk, 8, 16) + chkStuintToStuint(chk, 8, 32) + chkStuintToStuint(chk, 8, 64) + chkStuintToStuint(chk, 8, 128) + chkStuintToStuint(chk, 8, 256) + chkStuintToStuint(chk, 8, 512) + + chkStuintToStuint(chk, 16, 8) + chkStuintToStuint(chk, 16, 16) + chkStuintToStuint(chk, 16, 32) + chkStuintToStuint(chk, 16, 64) + chkStuintToStuint(chk, 16, 128) + chkStuintToStuint(chk, 16, 256) + chkStuintToStuint(chk, 16, 512) + + chkStuintToStuint(chk, 32, 8) + chkStuintToStuint(chk, 32, 16) + chkStuintToStuint(chk, 32, 32) + chkStuintToStuint(chk, 32, 64) + chkStuintToStuint(chk, 32, 128) + chkStuintToStuint(chk, 32, 256) + chkStuintToStuint(chk, 32, 512) + + chkStuintToStuint(chk, 64, 8) + chkStuintToStuint(chk, 64, 16) + chkStuintToStuint(chk, 64, 32) + chkStuintToStuint(chk, 64, 64) + chkStuintToStuint(chk, 64, 128) + chkStuintToStuint(chk, 64, 256) + chkStuintToStuint(chk, 64, 512) + + chkStuintToStuint(chk, 128, 8) + chkStuintToStuint(chk, 128, 16) + chkStuintToStuint(chk, 128, 32) + chkStuintToStuint(chk, 128, 64) + chkStuintToStuint(chk, 128, 128) + chkStuintToStuint(chk, 128, 256) + chkStuintToStuint(chk, 128, 512) + + chkStuintToStuint(chk, 256, 8) + chkStuintToStuint(chk, 256, 16) + chkStuintToStuint(chk, 256, 32) + chkStuintToStuint(chk, 256, 64) + chkStuintToStuint(chk, 256, 128) + chkStuintToStuint(chk, 256, 256) + chkStuintToStuint(chk, 256, 512) + + chkStuintToStuint(chk, 512, 8) + chkStuintToStuint(chk, 512, 16) + chkStuintToStuint(chk, 512, 32) + chkStuintToStuint(chk, 512, 64) + chkStuintToStuint(chk, 512, 128) + chkStuintToStuint(chk, 512, 256) + chkStuintToStuint(chk, 512, 512) + + tst "stint to stuint": + chkStintToStuint(chk, 8, 8) + chkStintToStuint(chk, 8, 16) + chkStintToStuint(chk, 8, 32) + chkStintToStuint(chk, 8, 64) + chkStintToStuint(chk, 8, 128) + chkStintToStuint(chk, 8, 256) + chkStintToStuint(chk, 8, 512) + + chkStintToStuint(chk, 16, 8) + chkStintToStuint(chk, 16, 16) + chkStintToStuint(chk, 16, 32) + chkStintToStuint(chk, 16, 64) + chkStintToStuint(chk, 16, 128) + chkStintToStuint(chk, 16, 256) + chkStintToStuint(chk, 16, 512) + + chkStintToStuint(chk, 32, 8) + chkStintToStuint(chk, 32, 16) + chkStintToStuint(chk, 32, 32) + chkStintToStuint(chk, 32, 64) + chkStintToStuint(chk, 32, 128) + chkStintToStuint(chk, 32, 256) + chkStintToStuint(chk, 32, 512) + + chkStintToStuint(chk, 64, 8) + chkStintToStuint(chk, 64, 16) + chkStintToStuint(chk, 64, 32) + chkStintToStuint(chk, 64, 64) + chkStintToStuint(chk, 64, 128) + chkStintToStuint(chk, 64, 256) + chkStintToStuint(chk, 64, 512) + + chkStintToStuint(chk, 128, 8) + chkStintToStuint(chk, 128, 16) + chkStintToStuint(chk, 128, 32) + chkStintToStuint(chk, 128, 64) + chkStintToStuint(chk, 128, 128) + chkStintToStuint(chk, 128, 256) + chkStintToStuint(chk, 128, 512) + + chkStintToStuint(chk, 256, 8) + chkStintToStuint(chk, 256, 16) + chkStintToStuint(chk, 256, 32) + chkStintToStuint(chk, 256, 64) + chkStintToStuint(chk, 256, 128) + chkStintToStuint(chk, 256, 256) + chkStintToStuint(chk, 256, 512) + + chkStintToStuint(chk, 512, 8) + chkStintToStuint(chk, 512, 16) + chkStintToStuint(chk, 512, 32) + chkStintToStuint(chk, 512, 64) + chkStintToStuint(chk, 512, 128) + chkStintToStuint(chk, 512, 256) + chkStintToStuint(chk, 512, 512) + + tst "stint to stint": + chkStintToStint(chk, 8, 8) + chkStintToStint(chk, 8, 16) + chkStintToStint(chk, 8, 32) + chkStintToStint(chk, 8, 64) + chkStintToStint(chk, 8, 128) + chkStintToStint(chk, 8, 256) + chkStintToStint(chk, 8, 512) + + chkStintToStint(chk, 16, 8) + chkStintToStint(chk, 16, 16) + chkStintToStint(chk, 16, 32) + chkStintToStint(chk, 16, 64) + chkStintToStint(chk, 16, 128) + chkStintToStint(chk, 16, 256) + chkStintToStint(chk, 16, 512) + + chkStintToStint(chk, 32, 8) + chkStintToStint(chk, 32, 16) + chkStintToStint(chk, 32, 32) + chkStintToStint(chk, 32, 64) + chkStintToStint(chk, 32, 128) + chkStintToStint(chk, 32, 256) + chkStintToStint(chk, 32, 512) + + chkStintToStint(chk, 64, 8) + chkStintToStint(chk, 64, 16) + chkStintToStint(chk, 64, 32) + chkStintToStint(chk, 64, 64) + chkStintToStint(chk, 64, 128) + chkStintToStint(chk, 64, 256) + chkStintToStint(chk, 64, 512) + + chkStintToStint(chk, 128, 8) + chkStintToStint(chk, 128, 16) + chkStintToStint(chk, 128, 32) + chkStintToStint(chk, 128, 64) + chkStintToStint(chk, 128, 128) + chkStintToStint(chk, 128, 256) + chkStintToStint(chk, 128, 512) + + chkStintToStint(chk, 256, 8) + chkStintToStint(chk, 256, 16) + chkStintToStint(chk, 256, 32) + chkStintToStint(chk, 256, 64) + chkStintToStint(chk, 256, 128) + chkStintToStint(chk, 256, 256) + chkStintToStint(chk, 256, 512) + + chkStintToStint(chk, 512, 8) + chkStintToStint(chk, 512, 16) + chkStintToStint(chk, 512, 32) + chkStintToStint(chk, 512, 64) + chkStintToStint(chk, 512, 128) + chkStintToStint(chk, 512, 256) + chkStintToStint(chk, 512, 512) + + tst "stuint to stint": + chkStuintToStint(chk, 8, 8) + chkStuintToStint(chk, 8, 16) + chkStuintToStint(chk, 8, 32) + chkStuintToStint(chk, 8, 64) + chkStuintToStint(chk, 8, 128) + chkStuintToStint(chk, 8, 256) + chkStuintToStint(chk, 8, 512) + + chkStuintToStint(chk, 16, 8) + chkStuintToStint(chk, 16, 16) + chkStuintToStint(chk, 16, 32) + chkStuintToStint(chk, 16, 64) + chkStuintToStint(chk, 16, 128) + chkStuintToStint(chk, 16, 256) + chkStuintToStint(chk, 16, 512) + + chkStuintToStint(chk, 32, 8) + chkStuintToStint(chk, 32, 16) + chkStuintToStint(chk, 32, 32) + chkStuintToStint(chk, 32, 64) + chkStuintToStint(chk, 32, 128) + chkStuintToStint(chk, 32, 256) + chkStuintToStint(chk, 32, 512) + + chkStuintToStint(chk, 64, 8) + chkStuintToStint(chk, 64, 16) + chkStuintToStint(chk, 64, 32) + chkStuintToStint(chk, 64, 64) + chkStuintToStint(chk, 64, 128) + chkStuintToStint(chk, 64, 256) + chkStuintToStint(chk, 64, 512) + + chkStuintToStint(chk, 128, 8) + chkStuintToStint(chk, 128, 16) + chkStuintToStint(chk, 128, 32) + chkStuintToStint(chk, 128, 64) + chkStuintToStint(chk, 128, 128) + chkStuintToStint(chk, 128, 256) + chkStuintToStint(chk, 128, 512) + + chkStuintToStint(chk, 256, 8) + chkStuintToStint(chk, 256, 16) + chkStuintToStint(chk, 256, 32) + chkStuintToStint(chk, 256, 64) + chkStuintToStint(chk, 256, 128) + chkStuintToStint(chk, 256, 256) + chkStuintToStint(chk, 256, 512) + + chkStuintToStint(chk, 512, 8) + chkStuintToStint(chk, 512, 16) + chkStuintToStint(chk, 512, 32) + chkStuintToStint(chk, 512, 64) + chkStuintToStint(chk, 512, 128) + chkStuintToStint(chk, 512, 256) + chkStuintToStint(chk, 512, 512) + +static: + testConversion(ctCheck, ctTest) + +proc main() = + # Nim GC protests we are using too much global variables + # so put it in a proc + suite "Testing conversion between big integers": + testConversion(check, test) + +main() diff --git a/tests/test_int_addsub.nim b/tests/test_int_addsub.nim index a181e96..110d770 100644 --- a/tests/test_int_addsub.nim +++ b/tests/test_int_addsub.nim @@ -284,46 +284,51 @@ template testAddSub(chk, tst: untyped) = static: testAddSub(ctCheck, ctTest) -suite "Wider signed int addsub coverage": - testAddSub(check, test) +proc main() = + # Nim GC protests we are using too much global variables + # so put it in a proc + suite "Wider signed int addsub coverage": + testAddSub(check, test) -suite "Testing signed addition implementation": - test "In-place addition gives expected result": + suite "Testing signed addition implementation": + test "In-place addition gives expected result": - var a = 20182018.stint(64) - let b = 20172017.stint(64) + var a = 20182018.stint(64) + let b = 20172017.stint(64) - a += b + a += b - check: cast[int64](a) == 20182018'i64 + 20172017'i64 + check: cast[int64](a) == 20182018'i64 + 20172017'i64 - test "Addition gives expected result": + test "Addition gives expected result": - let a = 20182018.stint(64) - let b = 20172017.stint(64) + let a = 20182018.stint(64) + let b = 20172017.stint(64) - check: cast[int64](a+b) == 20182018'i64 + 20172017'i64 + check: cast[int64](a+b) == 20182018'i64 + 20172017'i64 - test "When the low half overflows, it is properly carried": - # uint8 (low half) overflow at 255 - let a = 100'i16.stint(16) - let b = 100'i16.stint(16) + test "When the low half overflows, it is properly carried": + # uint8 (low half) overflow at 255 + let a = 100'i16.stint(16) + let b = 100'i16.stint(16) - check: cast[int16](a+b) == 200 + check: cast[int16](a+b) == 200 -suite "Testing signed substraction implementation": - test "In-place substraction gives expected result": + suite "Testing signed substraction implementation": + test "In-place substraction gives expected result": - var a = 20182018.stint(64) - let b = 20172017.stint(64) + var a = 20182018.stint(64) + let b = 20172017.stint(64) - a -= b + a -= b - check: cast[int64](a) == 20182018'i64 - 20172017'i64 + check: cast[int64](a) == 20182018'i64 - 20172017'i64 - test "Substraction gives expected result": + test "Substraction gives expected result": - let a = 20182018.stint(64) - let b = 20172017.stint(64) + let a = 20182018.stint(64) + let b = 20172017.stint(64) - check: cast[int64](a-b) == 20182018'i64 - 20172017'i64 + check: cast[int64](a-b) == 20182018'i64 - 20172017'i64 + +main() diff --git a/tests/test_int_comparison.nim b/tests/test_int_comparison.nim index dc7547b..c0e2c28 100644 --- a/tests/test_int_comparison.nim +++ b/tests/test_int_comparison.nim @@ -375,60 +375,66 @@ template testComparison(chk, tst: untyped) = static: testComparison(ctCheck, ctTest) -suite "Wider signed int comparison coverage": - testComparison(check, test) +proc main() = + # Nim GC protests we are using too much global variables + # so put it in a proc -suite "Signed int - Testing comparison operators": - let - a = 10'i16.stint(16) - b = 15'i16.stint(16) - c = 150'i16.stint(16) + suite "Wider signed int comparison coverage": + testComparison(check, test) - test "< operator": - check: - a < b - not (a + b < b) - not (a + a + a < b + b) - -c < c - -c < a - -b < -a - not(-b < -b) + suite "Signed int - Testing comparison operators": + let + a = 10'i16.stint(16) + b = 15'i16.stint(16) + c = 150'i16.stint(16) - test "<= operator": - check: - a <= b - not (a + b <= b) - a + a + a <= b + b - -c <= c - -c <= a - -b <= -a - -b <= -b + test "< operator": + check: + a < b + not (a + b < b) + not (a + a + a < b + b) + -c < c + -c < a + -b < -a + not(-b < -b) - test "> operator": - check: - b > a - not (b > a + b) - not (b + b > a + a + a) - c > -c - a > -c - b > -c - not(-b > -b) + test "<= operator": + check: + a <= b + not (a + b <= b) + a + a + a <= b + b + -c <= c + -c <= a + -b <= -a + -b <= -b - test ">= operator": - check: - b >= a - not (b >= a + b) - b + b >= a + a + a - c >= -c - a >= -c - b >= -c - -b >= -b + test "> operator": + check: + b > a + not (b > a + b) + not (b + b > a + a + a) + c > -c + a > -c + b > -c + not(-b > -b) - test "isOdd/isEven": - check: - a.isEven - not a.isOdd - b.isOdd - not b.isEven - c.isEven - not c.isOdd + test ">= operator": + check: + b >= a + not (b >= a + b) + b + b >= a + a + a + c >= -c + a >= -c + b >= -c + -b >= -b + + test "isOdd/isEven": + check: + a.isEven + not a.isOdd + b.isOdd + not b.isEven + c.isEven + not c.isOdd + +main()