fixes #94, implement conversion constructor

This commit is contained in:
andri lim 2019-12-10 15:57:19 +07:00 committed by zah
parent 25c2604b4b
commit d98b77c88d
5 changed files with 543 additions and 79 deletions

View File

@ -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

View File

@ -24,4 +24,5 @@ import test_int_endianness,
test_int_boundchecks,
test_int_bitwise
import test_io
import test_io,
test_conversion

340
tests/test_conversion.nim Normal file
View File

@ -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()

View File

@ -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()

View File

@ -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()