mirror of
https://github.com/status-im/nim-stint.git
synced 2025-02-19 18:38:13 +00:00
Add bigint serialization (hex and decimal) (#29)
* Add serialization for decimal and hex * Fix carry bug in signed add * Add parsing test * Improve highlow for int, remove most_significant_word_mut * make conversion toString compile * Add division corner case test * Remove a buggy division shortcut * Fix decimal string conversion * Fix hex dumping * Fix power of 2 division (what was I thinking?) * Add hexdump test * Move runtime check to compile-time * Fix static assert check * more compile-time asserts * Fix parsing of negative hex numbers, add test_io to the suite * dump default to bigEndian, split toString in Stint and Stuint * Add (failing) tests with big ints conversion * Temporarily remove all the noInit pragma
This commit is contained in:
parent
f520f7817b
commit
fd0482180f
@ -1,35 +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.
|
||||
|
||||
# Utilities to debug MpInt
|
||||
|
||||
import
|
||||
strutils,
|
||||
../private/datatypes
|
||||
|
||||
func tohexBE*(x: UintImpl or IntImpl or SomeInteger): string =
|
||||
## Stringify an uint to hex, Most significant byte on the left
|
||||
## i.e.
|
||||
## - 1.uint64 will be 00000001
|
||||
## - (2.uint128)^64 + 1 will be 0000000100000001
|
||||
|
||||
const size = getSize(x) div 8
|
||||
|
||||
let bytes = cast[ptr array[size, byte]](x.unsafeaddr)
|
||||
|
||||
result = ""
|
||||
when system.cpuEndian == littleEndian:
|
||||
for i in countdown(size - 1, 0):
|
||||
result.add toHex(bytes[i])
|
||||
else:
|
||||
for i in 0 ..< size:
|
||||
result.add toHex(bytes[i])
|
||||
|
||||
func tohexBE*(x: Stint or StUint): string {.inline.}=
|
||||
x.data.tohexBE
|
63
src/init.nim
63
src/init.nim
@ -1,63 +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
|
||||
./private/datatypes,
|
||||
./private/int_negabs,
|
||||
typetraits
|
||||
|
||||
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
||||
assert n >= 0.T
|
||||
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"
|
||||
r_ptr[0] = n
|
||||
else:
|
||||
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 IntImpl:
|
||||
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:
|
||||
result.data = (type result.data)(n)
|
@ -6,7 +6,7 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./private/datatypes, macros
|
||||
export StInt, IntImpl, intImpl # TODO remove the need to export intImpl and this macro
|
||||
|
||||
@ -15,14 +15,14 @@ type
|
||||
Int256* = Stint[256]
|
||||
|
||||
template make_conv(conv_name: untyped, size: int): untyped =
|
||||
func `convname`*(n: SomeInteger): Stint[size] {.inline, noInit.}=
|
||||
func `convname`*(n: SomeInteger): Stint[size] {.inline, fooPragma.}=
|
||||
n.stint(size)
|
||||
|
||||
make_conv(i128, 128)
|
||||
make_conv(i256, 256)
|
||||
|
||||
template make_unary(op, ResultTy): untyped =
|
||||
func `op`*(x: Stint): ResultTy {.noInit, inline.} =
|
||||
func `op`*(x: Stint): ResultTy {.fooPragma, inline.} =
|
||||
when ResultTy is Stint:
|
||||
result.data = op(x.data)
|
||||
else:
|
||||
@ -30,7 +30,7 @@ template make_unary(op, ResultTy): untyped =
|
||||
export op
|
||||
|
||||
template make_binary(op, ResultTy): untyped =
|
||||
func `op`*(x, y: Stint): ResultTy {.noInit, inline.} =
|
||||
func `op`*(x, y: Stint): ResultTy {.fooPragma, inline.} =
|
||||
when ResultTy is Stint:
|
||||
result.data = op(x.data, y.data)
|
||||
else:
|
||||
@ -60,7 +60,7 @@ import ./private/int_div
|
||||
|
||||
make_binary(`div`, Stint)
|
||||
make_binary(`mod`, Stint)
|
||||
func divmod*(x, y: Stint): tuple[quot, rem: Stint] {.noInit, inline.} =
|
||||
func divmod*(x, y: Stint): tuple[quot, rem: Stint] {.fooPragma, inline.} =
|
||||
(result.quot.data, result.rem.data) = divmod(x.data, y.data)
|
||||
|
||||
import ./private/int_comparison
|
||||
@ -69,6 +69,7 @@ make_binary(`<`, bool)
|
||||
make_binary(`<=`, bool)
|
||||
make_binary(`==`, bool)
|
||||
func isZero*(x: Stint): bool {.inline.} = isZero x.data
|
||||
func isNegative*(x: Stint): bool {.inline.} = isNegative x.data
|
||||
|
||||
import ./private/int_bitwise_ops
|
||||
|
||||
@ -76,7 +77,7 @@ 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.} =
|
||||
# proc `shr`*(x: Stint, y: SomeInteger): Stint {.fooPragma, inline, noSideEffect.} =
|
||||
# result.data = x.data shr y
|
||||
# proc `shl`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} =
|
||||
# proc `shl`*(x: Stint, y: SomeInteger): Stint {.fooPragma, inline, noSideEffect.} =
|
||||
# result.data = x.data shl y
|
||||
|
283
src/io.nim
Normal file
283
src/io.nim
Normal file
@ -0,0 +1,283 @@
|
||||
# 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.
|
||||
{.pragma: fooPragma.}
|
||||
import
|
||||
./private/datatypes,
|
||||
./private/int_negabs,
|
||||
./private/initialization,
|
||||
./private/[as_words, as_signed_words],
|
||||
./int_public, ./uint_public,
|
||||
typetraits, algorithm
|
||||
|
||||
template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
|
||||
# To avoid a costly runtime check, we refuse storing into StUint types smaller
|
||||
# than the input type.
|
||||
|
||||
static: assert sizeof(T) * 8 <= bits, "Input type (" & $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."
|
||||
|
||||
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
||||
assert n >= 0.T
|
||||
when result.data is UintImpl:
|
||||
static_check_size(T, bits)
|
||||
|
||||
let r_ptr = cast[ptr array[bits div (sizeof(T) * 8), T]](result.addr)
|
||||
when system.cpuEndian == littleEndian:
|
||||
# "Least significant byte is at the beginning"
|
||||
r_ptr[0] = n
|
||||
else:
|
||||
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 IntImpl:
|
||||
static_check_size(T, bits)
|
||||
|
||||
let r_ptr = cast[ptr array[bits div (sizeof(T) * 8), T]](result.addr)
|
||||
when system.cpuEndian == littleEndian:
|
||||
# "Least significant byte is 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:
|
||||
result.data = (type result.data)(n)
|
||||
|
||||
func toInt*(num: Stint or StUint): int {.inline.}=
|
||||
# Returns as int. Result is modulo 2^(sizeof(int)
|
||||
num.data.least_significant_word.int
|
||||
|
||||
func readHexChar(c: char): int8 {.inline.}=
|
||||
## Converts an hex char to an int
|
||||
case c
|
||||
of '0'..'9': result = int8 ord(c) - ord('0')
|
||||
of 'a'..'f': result = int8 ord(c) - ord('a') + 10
|
||||
of 'A'..'F': result = int8 ord(c) - ord('A') + 10
|
||||
else:
|
||||
raise newException(ValueError, $c & "is not a hexadecimal character")
|
||||
|
||||
func skipPrefixes(current_idx: var int, str: string, base: range[2..16]) {.inline.} =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
|
||||
assert current_idx == 0, "skipPrefixes only works for prefixes (position 0 and 1 of the string)"
|
||||
if str[0] == '0':
|
||||
if str[1] in {'x', 'X'}:
|
||||
assert base == 16, "Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)"
|
||||
current_idx = 2
|
||||
elif str[1] in {'o', 'O'}:
|
||||
assert base == 8, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)"
|
||||
current_idx = 2
|
||||
elif str[1] in {'b', 'B'}:
|
||||
assert base == 2, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2)"
|
||||
current_idx = 2
|
||||
|
||||
func nextNonBlank(current_idx: var int, s: string) {.inline.} =
|
||||
## Move the current index, skipping white spaces and "_" characters.
|
||||
|
||||
const blanks = {' ', '_'}
|
||||
|
||||
inc current_idx
|
||||
while s[current_idx] in blanks and current_idx < s.len:
|
||||
inc current_idx
|
||||
|
||||
func readDecChar(c: range['0'..'9']): int {.inline.}=
|
||||
## Converts a decimal char to an int
|
||||
# specialization without branching for base <= 10.
|
||||
ord(c) - ord('0')
|
||||
|
||||
func unsafeConv[bits: static[int]](n: range[0..16], T: typedesc[Stint[bits]|Stuint[bits]]): T =
|
||||
## Fast convert a small int to a Stint/Stuint
|
||||
## This assumes that the int always fit.
|
||||
## Purpose:
|
||||
## - Converting bases in the range [2..16]
|
||||
## - Converting decimal/hexadecimal in range [0..15]
|
||||
|
||||
let r_ptr = cast[ptr array[bits div 8, byte]](result.addr)
|
||||
when system.cpuEndian == littleEndian:
|
||||
r_ptr[0] = n.byte
|
||||
else:
|
||||
r_ptr[r_ptr[].len - 1] = n.byte
|
||||
|
||||
func parse*[bits: static[int]](T: typedesc[Stuint[bits]], input: string, base: static[int]): T =
|
||||
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
|
||||
|
||||
static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported"
|
||||
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
|
||||
|
||||
# TODO: we can special case hex result/input as an array of bytes
|
||||
# and be much faster
|
||||
|
||||
let radix = unsafeConv(base, T)
|
||||
var curr = 0 # Current index in the string
|
||||
skipPrefixes(curr, input, base)
|
||||
|
||||
while curr < input.len:
|
||||
# TODO: overflow detection
|
||||
when base <= 10:
|
||||
result = result * radix + input[curr].readDecChar.unsafeConv(T)
|
||||
else:
|
||||
result = result * radix + input[curr].readHexChar.unsafeConv(T)
|
||||
nextNonBlank(curr, input)
|
||||
|
||||
func parse*[bits: static[int]](T: typedesc[Stint[bits]], input: string, base: static[int]): T =
|
||||
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
|
||||
|
||||
static: assert (base >= 2) and base <= 16, "Only base from 2..16 are supported"
|
||||
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
|
||||
|
||||
# TODO: we can special case hex result/input as an array of bytes
|
||||
# and be much faster
|
||||
|
||||
# For conversion we require overflowing operations (for example for negative hex numbers)
|
||||
let radix = unsafeConv(base, Stuint[bits])
|
||||
|
||||
var
|
||||
curr = 0 # Current index in the string
|
||||
isNeg = false
|
||||
no_overflow: Stuint[bits]
|
||||
|
||||
if input[curr] == '-':
|
||||
assert base == 10, "Negative numbers are only supported with base 10 input."
|
||||
isNeg = true
|
||||
inc curr
|
||||
else:
|
||||
skipPrefixes(curr, input, base)
|
||||
|
||||
while curr < input.len:
|
||||
# TODO: overflow detection
|
||||
when base <= 10:
|
||||
no_overflow = no_overflow * radix + input[curr].readDecChar.unsafeConv(Stuint[bits])
|
||||
else:
|
||||
no_overflow = no_overflow * radix + input[curr].readHexChar.unsafeConv(Stuint[bits])
|
||||
nextNonBlank(curr, input)
|
||||
|
||||
# TODO: we can't create the lowest int this way
|
||||
if isNeg:
|
||||
result = -cast[Stint[bits]](no_overflow)
|
||||
else:
|
||||
result = cast[Stint[bits]](no_overflow)
|
||||
|
||||
func parse*[bits: static[int]](T: typedesc[Stint[bits]|Stuint[bits]], input: string): T {.inline, fooPragma.}=
|
||||
## Parse a string and store the result in a Stint[bits] or Stuint[bits].
|
||||
## Input is considered a decimal string.
|
||||
# TODO: Have a default static argument in the previous proc. Currently we get
|
||||
# "Cannot evaluate at compile-time" in several places (2018-04-26).
|
||||
parse(T, input, 10)
|
||||
|
||||
func toString*[bits: static[int]](num: StUint[bits], base: static[int]): 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.
|
||||
|
||||
static: assert (base >= 2) and base <= 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"
|
||||
let radix = unsafeConv(base, type num)
|
||||
|
||||
result = ""
|
||||
var (q, r) = divmod(num, radix)
|
||||
|
||||
while true:
|
||||
result.add hexChars[r.toInt]
|
||||
if q.isZero:
|
||||
break
|
||||
(q, r) = divmod(q, radix)
|
||||
|
||||
reverse(result)
|
||||
|
||||
func toString*[bits: static[int]](num: Stint[bits], base: static[int]): 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.
|
||||
|
||||
static: assert (base >= 2) and base <= 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"
|
||||
let radix = unsafeConv(base, type num)
|
||||
|
||||
result = ""
|
||||
|
||||
let isNeg = num.isNegative
|
||||
let num = if base == 10 and isNeg: -num
|
||||
else: num
|
||||
|
||||
var (q, r) = divmod(num, radix)
|
||||
|
||||
while true:
|
||||
result.add hexChars[r.toInt]
|
||||
if q.isZero:
|
||||
break
|
||||
(q, r) = divmod(q, radix)
|
||||
|
||||
if isNeg:
|
||||
result.add '-'
|
||||
|
||||
reverse(result)
|
||||
|
||||
func toString*[bits: static[int]](num: Stint[bits] or StUint[bits]): string {.inline, fooPragma.}=
|
||||
## Convert to a string.
|
||||
## Output is considered a decimal string.
|
||||
#
|
||||
# TODO: Have a default static argument in the previous proc. Currently we get
|
||||
# "Error: type mismatch: got <int, type StInt[128]>, required type static[int]"
|
||||
toString(num, 10)
|
||||
|
||||
func dumpHex*(x: Stint or StUint, order: static[Endianness]): string =
|
||||
## Stringify an int to hex.
|
||||
## Note. Leading 0 are not removed. Use toString(n, base = 16) instead.
|
||||
##
|
||||
## You can specify bigEndian or littleEndian order.
|
||||
## i.e. in bigEndian:
|
||||
## - 1.uint64 will be 00000001
|
||||
## - (2.uint128)^64 + 1 will be 0000000100000001
|
||||
##
|
||||
## in littleEndian:
|
||||
## - 1.uint64 will be 01000000
|
||||
## - (2.uint128)^64 + 1 will be 0100000001000000
|
||||
|
||||
const
|
||||
hexChars = "0123456789abcdef"
|
||||
size = getSize(x.data) div 8
|
||||
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
|
||||
let bytes {.restrict.}= cast[ptr array[size, byte]](x.unsafeaddr)
|
||||
|
||||
result = newString(2*size)
|
||||
|
||||
for i in 0 ..< size:
|
||||
when order == system.cpuEndian:
|
||||
result[2*i] = hexChars[int bytes[i] shr 4 and 0xF]
|
||||
result[2*i+1] = hexChars[int bytes[i] and 0xF]
|
||||
else:
|
||||
result[2*i] = hexChars[int bytes[bytes[].high - i] shr 4 and 0xF]
|
||||
result[2*i+1] = hexChars[int bytes[bytes[].high - i] and 0xF]
|
||||
|
||||
func dumpHex*(x: Stint or StUint): string {.inline.}=
|
||||
## Stringify an int to hex.
|
||||
## By default, dump is done in bigEndian order.
|
||||
dumpHex(x, bigEndian)
|
||||
# TODO: Have a default static argument in the previous proc. Currently we get
|
||||
# "Cannot evaluate at compile-time".
|
@ -32,10 +32,11 @@ proc optimInt*(x: NimNode): NimNode =
|
||||
error "Unreachable path reached"
|
||||
|
||||
proc isInt*(x: NimNode): static[bool] =
|
||||
if eqIdent(x, "int64"): true
|
||||
elif eqIdent(x, "int32"): true
|
||||
elif eqIdent(x, "int16"): true
|
||||
elif eqIdent(x, "int8"): true
|
||||
if eqIdent(x, "uint64"): true
|
||||
elif eqIdent(x, "int64"): true
|
||||
elif eqIdent(x, "int32"): true
|
||||
elif eqIdent(x, "int16"): true
|
||||
elif eqIdent(x, "int8"): true
|
||||
else: false
|
||||
|
||||
macro most_significant_word*(x: IntImpl): untyped =
|
||||
@ -47,26 +48,28 @@ macro most_significant_word*(x: IntImpl): untyped =
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
let msw_pos = size div 64 - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
result = quote do:
|
||||
# most significant word must be returned signed for addition/substraction
|
||||
# overflow checking
|
||||
cast[int](cast[`optim_type`](`x`)[`msw_pos`])
|
||||
|
||||
macro most_significant_word_mut*(x: var IntImpl): untyped =
|
||||
macro least_significant_word*(x: IntImpl): untyped =
|
||||
|
||||
let optim_type = optimInt(x)
|
||||
if optim_type.isInt:
|
||||
result = quote do:
|
||||
cast[var `optim_type`](`x`.addr)
|
||||
cast[`optim_type`](`x`)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
else:
|
||||
let msw_pos = size div 8 - 1
|
||||
result = quote do:
|
||||
(cast[ptr `optim_type`](`x`.unsafeAddr)[`msw_pos`])[]
|
||||
cast[int](cast[`optim_type`](`x`)[`msw_pos`])
|
||||
|
||||
macro asSignedWordsZip*[T](
|
||||
x, y: IntImpl[T],
|
||||
|
@ -60,6 +60,21 @@ proc replaceNodes*(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNo
|
||||
return rTree
|
||||
result = inspect(ast)
|
||||
|
||||
macro least_significant_word*(x: UintImpl): untyped =
|
||||
|
||||
let optim_type = optimUInt(x)
|
||||
if optim_type.isUInt:
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = 0
|
||||
else:
|
||||
let msw_pos = size div 64 - 1
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)[`msw_pos`]
|
||||
|
||||
macro asWords*(n: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over n, as an array of words.
|
||||
## Input:
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
import macros
|
||||
|
||||
|
||||
# The macro uintImpl must be exported
|
||||
|
||||
when defined(mpint_test):
|
||||
|
@ -6,14 +6,14 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./conversion, ./as_signed_words
|
||||
|
||||
func `+`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
func `+`*(x, y: IntImpl): IntImpl {.fooPragma, inline.}=
|
||||
# Addition for multi-precision signed int.
|
||||
type SubTy = type x.lo
|
||||
result.lo = x.lo + y.lo
|
||||
result.hi = (x.lo < y.lo).toSubtype(SubTy) + x.hi + y.hi
|
||||
result.hi = (result.lo < y.lo).toSubtype(SubTy) + x.hi + y.hi
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(
|
||||
@ -27,7 +27,7 @@ func `+=`*(x: var IntImpl, y: IntImpl) {.inline.}=
|
||||
## In-place addition for multi-precision signed int.
|
||||
x = x + y
|
||||
|
||||
func `-`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
func `-`*(x, y: IntImpl): IntImpl {.fooPragma, inline.}=
|
||||
# Substraction for multi-precision signed int.
|
||||
|
||||
type SubTy = type x.lo
|
||||
|
@ -6,26 +6,26 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./as_words
|
||||
|
||||
|
||||
func `not`*(x: IntImpl): IntImpl {.noInit, inline.}=
|
||||
func `not`*(x: IntImpl): IntImpl {.fooPragma, inline.}=
|
||||
## Bitwise complement of unsigned integer x
|
||||
m_asWordsZip(result, x, ignoreEndianness = true):
|
||||
result = not x
|
||||
|
||||
func `or`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
func `or`*(x, y: IntImpl): IntImpl {.fooPragma, 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.}=
|
||||
func `and`*(x, y: IntImpl): IntImpl {.fooPragma, 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.}=
|
||||
func `xor`*(x, y: IntImpl): IntImpl {.fooPragma, inline.}=
|
||||
## `Bitwise xor` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x xor y
|
||||
|
@ -6,8 +6,8 @@
|
||||
# * 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_negabs, ./uint_div
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./int_negabs, ./uint_div, ./int_comparison
|
||||
|
||||
# Here are the expected signs for division/modulo by opposite signs and both negative numbers
|
||||
# in EVM
|
||||
@ -32,11 +32,11 @@ import ./datatypes, ./int_negabs, ./uint_div
|
||||
# echo "-10 mod -3: " & $(-10 mod -3) # -1
|
||||
# echo '\n'
|
||||
|
||||
func divmod*(x, y: SomeSignedInt): tuple[quot, rem: SomeSignedInt] {.noInit, inline.}=
|
||||
func divmod*(x, y: SomeSignedInt): tuple[quot, rem: SomeSignedInt] {.fooPragma, inline.}=
|
||||
# hopefully the compiler fuse that in a single op
|
||||
(x div y, x mod y)
|
||||
|
||||
proc divmod*[T](x, y: IntImpl[T]): tuple[quot, rem: IntImpl[T]] {.noInit.}=
|
||||
proc divmod*[T](x, y: IntImpl[T]): tuple[quot, rem: IntImpl[T]] {.fooPragma.}=
|
||||
## Divmod operation for multi-precision signed integer
|
||||
|
||||
result = cast[type result](divmod(
|
||||
|
@ -6,22 +6,20 @@
|
||||
# * 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.
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./uint_bitwise_ops, ./int_bitwise_ops, ./initialization
|
||||
|
||||
import ./datatypes, ./as_signed_words
|
||||
|
||||
func low*(T: typedesc[IntImpl]): T {.inline.}=
|
||||
|
||||
# The lowest signed int has representation
|
||||
# 0b1000_0000_0000_0000 ....
|
||||
# so we only have to set the most significant bit.
|
||||
type Msw = type result.most_significant_word
|
||||
type U = Msw
|
||||
|
||||
most_significant_word_mut(result) = low(U)
|
||||
|
||||
func high*(T: typedesc[IntImpl]): T {.inline, noInit.}=
|
||||
func high*[T](_: typedesc[IntImpl[T]]): IntImpl[T] {.inline, fooPragma.}=
|
||||
|
||||
# The lowest signed int has representation
|
||||
# 0b0111_1111_1111_1111 ....
|
||||
# so we only have to unset the most significant bit.
|
||||
not low(T)
|
||||
let only_msb_set = UintImpl[T].zero.not shr 1
|
||||
result = cast[IntImpl[T]](only_msb_set)
|
||||
|
||||
func low*[T](_: typedesc[IntImpl[T]]): IntImpl[T] {.inline, fooPragma.}=
|
||||
|
||||
# The lowest signed int has representation
|
||||
# 0b1000_0000_0000_0000 ....
|
||||
# so we only have to set the most significant bit.
|
||||
not high(IntImpl[T])
|
||||
|
@ -6,10 +6,10 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./uint_mul
|
||||
|
||||
func `*`*[T](x, y: IntImpl[T]): IntImpl[T] {.inline, noInit.}=
|
||||
func `*`*[T](x, y: IntImpl[T]): IntImpl[T] {.inline, fooPragma.}=
|
||||
## Multiplication for multi-precision signed integers
|
||||
# For 2-complement representation this is the exact same
|
||||
# as unsigned multiplication. We don't need to deal with the sign
|
||||
|
@ -6,13 +6,13 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import
|
||||
./datatypes,
|
||||
./initialization, ./int_highlow,
|
||||
./int_addsub
|
||||
./int_addsub, ./int_comparison
|
||||
|
||||
func `-`*[T: IntImpl](x: T): T {.noInit, inline.}=
|
||||
func `-`*[T: IntImpl](x: T): T {.fooPragma, inline.}=
|
||||
# Negate a multi-precision signed int.
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
@ -22,7 +22,7 @@ func `-`*[T: IntImpl](x: T): T {.noInit, inline.}=
|
||||
result = not x
|
||||
result += one(T)
|
||||
|
||||
func abs*[T: IntImpl](x: T): T {.noInit, inline.}=
|
||||
func abs*[T: IntImpl](x: T): T {.fooPragma, inline.}=
|
||||
## Returns the absolute value of a signed int.
|
||||
|
||||
result = if x.isNegative: -x
|
||||
|
@ -6,8 +6,8 @@
|
||||
# * 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 ./bithacks, ./conversion,
|
||||
{.pragma: fooPragma.}
|
||||
import ./bithacks, ./conversion, ./initialization,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
./uint_bitwise_ops
|
||||
@ -21,12 +21,12 @@ proc `+=`*(x: var UintImpl, y: UintImpl) {.noSideEffect, inline.}=
|
||||
x.lo += y.lo
|
||||
x.hi += (x.lo < y.lo).toSubtype(SubTy) + y.hi
|
||||
|
||||
proc `+`*(x, y: UintImpl): UintImpl {.noSideEffect, noInit, inline.}=
|
||||
proc `+`*(x, y: UintImpl): UintImpl {.noSideEffect, fooPragma, inline.}=
|
||||
# Addition for multi-precision unsigned int
|
||||
result = x
|
||||
result += y
|
||||
|
||||
proc `-`*(x, y: UintImpl): UintImpl {.noSideEffect, noInit, inline.}=
|
||||
proc `-`*(x, y: UintImpl): UintImpl {.noSideEffect, fooPragma, inline.}=
|
||||
# Substraction for multi-precision unsigned int
|
||||
|
||||
type SubTy = type x.lo
|
||||
@ -36,3 +36,6 @@ proc `-`*(x, y: UintImpl): UintImpl {.noSideEffect, noInit, inline.}=
|
||||
proc `-=`*(x: var UintImpl, y: UintImpl) {.noSideEffect, inline.}=
|
||||
## In-place substraction for multi-precision unsigned int
|
||||
x = x - y
|
||||
|
||||
func inc*(x: var UintImpl){.inline.}=
|
||||
x += one(type x)
|
||||
|
@ -6,26 +6,26 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./datatypes, ./as_words
|
||||
|
||||
|
||||
func `not`*(x: UintImpl): UintImpl {.noInit, inline.}=
|
||||
func `not`*(x: UintImpl): UintImpl {.fooPragma, inline.}=
|
||||
## Bitwise complement of unsigned integer x
|
||||
m_asWordsZip(result, x, ignoreEndianness = true):
|
||||
result = not x
|
||||
|
||||
func `or`*(x, y: UintImpl): UintImpl {.noInit, inline.}=
|
||||
func `or`*(x, y: UintImpl): UintImpl {.fooPragma, inline.}=
|
||||
## `Bitwise or` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x or y
|
||||
|
||||
func `and`*(x, y: UintImpl): UintImpl {.noInit, inline.}=
|
||||
func `and`*(x, y: UintImpl): UintImpl {.fooPragma, inline.}=
|
||||
## `Bitwise and` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x and y
|
||||
|
||||
func `xor`*(x, y: UintImpl): UintImpl {.noInit, inline.}=
|
||||
func `xor`*(x, y: UintImpl): UintImpl {.fooPragma, inline.}=
|
||||
## `Bitwise xor` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x xor y
|
||||
|
@ -6,7 +6,7 @@
|
||||
# * 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.
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
import ./bithacks, ./conversion, ./initialization,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
@ -48,7 +48,7 @@ func div2n1n[T: SomeunsignedInt](q, r: var T, n_hi, n_lo, d: T)
|
||||
func div2n1n(q, r: var UintImpl, ah, al, b: UintImpl)
|
||||
# Forward declaration
|
||||
|
||||
func divmod*(x, y: SomeUnsignedInt): tuple[quot, rem: SomeUnsignedInt] {.noInit, inline.}=
|
||||
func divmod*(x, y: SomeUnsignedInt): tuple[quot, rem: SomeUnsignedInt] {.fooPragma, inline.}=
|
||||
# hopefully the compiler fuse that in a single op
|
||||
(x div y, x mod y)
|
||||
|
||||
@ -174,49 +174,34 @@ func divmodBZ[T](x, y: UintImpl[T], q, r: var UintImpl[T])=
|
||||
|
||||
if y.hi.isZero:
|
||||
# Shortcut if divisor is smaller than half the size of the type
|
||||
|
||||
# Normalize
|
||||
let
|
||||
clz = countLeadingZeroBits(y.lo)
|
||||
xx = x shl clz
|
||||
yy = y.lo shl clz
|
||||
|
||||
if x.hi < y.lo:
|
||||
# Normalize
|
||||
let
|
||||
clz = countLeadingZeroBits(y.lo)
|
||||
xx = x shl clz
|
||||
yy = y.lo shl clz
|
||||
|
||||
# If y is smaller than the base, normalizing x does not overflow.
|
||||
# Compute directly
|
||||
# Compute directly the low part
|
||||
div2n1n(q.lo, r.lo, xx.hi, xx.lo, yy)
|
||||
# Undo normalization
|
||||
r.lo = r.lo shr clz
|
||||
else:
|
||||
# Normalizing x overflowed, we need to compute the high remainder first
|
||||
(q.hi, r.hi) = divmod(x.hi, y.lo)
|
||||
return
|
||||
|
||||
# Normalize the remainder. (x.lo is already normalized)
|
||||
r.hi = r.hi shl clz
|
||||
# General case
|
||||
|
||||
# Compute
|
||||
div2n1n(q.lo, r.lo, r.hi, xx.lo, yy)
|
||||
# Normalization
|
||||
let clz = countLeadingZeroBits(y)
|
||||
|
||||
# Undo normalization
|
||||
r.lo = r.lo shr clz
|
||||
let
|
||||
xx = UintImpl[type x](lo: x) shl clz
|
||||
yy = y shl clz
|
||||
|
||||
# Given size n, dividing a 2n number by a 1n normalized number
|
||||
# always gives a 1n remainder.
|
||||
r.hi = zero(T)
|
||||
# Compute
|
||||
div2n1n(q, r, xx.hi, xx.lo, yy)
|
||||
|
||||
else: # General case
|
||||
# Normalization
|
||||
let clz = countLeadingZeroBits(y)
|
||||
|
||||
let
|
||||
xx = UintImpl[type x](lo: x) shl clz
|
||||
yy = y shl clz
|
||||
|
||||
# Compute
|
||||
div2n1n(q, r, xx.hi, xx.lo, yy)
|
||||
|
||||
# Undo normalization
|
||||
r = r shr clz
|
||||
# Undo normalization
|
||||
r = r shr clz
|
||||
|
||||
func divmodBS(x, y: UintImpl, q, r: var UintImpl) =
|
||||
## Division for multi-precision unsigned uint
|
||||
@ -266,8 +251,7 @@ func divmod*[T](x, y: UintImpl[T]): tuple[quot, rem: UintImpl[T]]=
|
||||
# It is a bit tricky with recursive types. An empty n.lo means 0 or sizeof(n.lo)
|
||||
let y_ctz = getSize(y) - y_clz - 1
|
||||
result.quot = x shr y_ctz
|
||||
result.rem = y_ctz.initUintImpl(UintImpl[T])
|
||||
result.rem = result.rem and x
|
||||
result.rem = x and (y - one(type y))
|
||||
elif x == y:
|
||||
result.quot.lo = one(T)
|
||||
elif x < y:
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
import ./datatypes, ./initialization
|
||||
|
||||
func low*(T: typedesc[UintImpl]): T {.inline, noInit.}=
|
||||
func low*(T: typedesc[UintImpl]): T {.inline, fooPragma.}=
|
||||
zero(T)
|
||||
|
||||
func high*(T: typedesc[UintImpl]): T {.inline, noInit.}=
|
||||
func high*(T: typedesc[UintImpl]): T {.inline, fooPragma.}=
|
||||
not zero(T)
|
||||
|
@ -6,8 +6,9 @@
|
||||
# * 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 ./conversion,
|
||||
{.pragma: fooPragma.}
|
||||
import macros,
|
||||
./conversion,
|
||||
./initialization,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
@ -53,23 +54,23 @@ template extPrecMulImpl(result: var UintImpl[uint64], op: untyped, u, v: uint64)
|
||||
x0, x1, x2, x3: uint64
|
||||
|
||||
let
|
||||
ul = u.lo
|
||||
uh = u.hi
|
||||
vl = v.lo
|
||||
vh = v.hi
|
||||
ul = lo(u)
|
||||
uh = hi(u)
|
||||
vl = lo(v)
|
||||
vh = hi(v)
|
||||
|
||||
x0 = ul * vl
|
||||
x1 = ul * vh
|
||||
x2 = uh * vl
|
||||
x3 = uh * vh
|
||||
|
||||
x1 += x0.hi # This can't carry
|
||||
x1 += hi(x0) # This can't carry
|
||||
x1 += x2 # but this can
|
||||
if x1 < x2: # if carry, add it to x3
|
||||
x3 += base
|
||||
|
||||
op(result.hi, x3 + x1.hi)
|
||||
op(result.lo, (x1 shl p) or x0.lo)
|
||||
op(result.hi, x3 + hi(x1))
|
||||
op(result.lo, (x1 shl p) or lo(x0))
|
||||
|
||||
func extPrecMul*(result: var UintImpl[uint64], u, v: uint64) =
|
||||
## Extended precision multiplication
|
||||
@ -79,7 +80,15 @@ func extPrecAddMul(result: var UintImpl[uint64], u, v: uint64) =
|
||||
## Extended precision fused in-place addition & multiplication
|
||||
extPrecMulImpl(result, `+=`, u, v)
|
||||
|
||||
func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) =
|
||||
macro eqSym(x, y: untyped): untyped =
|
||||
let eq = $x == $y # Unfortunately eqIdent compares to string.
|
||||
result = quote do: `eq`
|
||||
|
||||
func extPrecAddMul[T](result: var UintImpl[UintImpl[T]], u, v: UintImpl[T])
|
||||
func extPrecMul*[T](result: var UintImpl[UintImpl[T]], u, v: UintImpl[T])
|
||||
# Forward declaration
|
||||
|
||||
template extPrecMulImpl*[T](result: var UintImpl[UintImpl[T]], op: untyped, x, y: UintImpl[T]) =
|
||||
# See details at
|
||||
# https://en.wikipedia.org/wiki/Karatsuba_algorithm
|
||||
# https://locklessinc.com/articles/256bit_arithmetic/
|
||||
@ -94,17 +103,20 @@ func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) =
|
||||
# and introduce branching
|
||||
# - More total operations means more register moves
|
||||
|
||||
var z1: UintImpl[T]
|
||||
var z1: type x
|
||||
|
||||
# Low part - z0
|
||||
extPrecMul(result.lo, x.lo, y.lo)
|
||||
when eqSym(op, `+=`):
|
||||
extPrecAddMul(result.lo, x.lo, y.lo)
|
||||
else:
|
||||
extPrecMul(result.lo, x.lo, y.lo)
|
||||
|
||||
# Middle part - z1
|
||||
extPrecMul(z1, x.hi, y.lo)
|
||||
let carry_check = z1
|
||||
extPrecAddMul(z1, x.lo, y.hi)
|
||||
if z1 < carry_check:
|
||||
result.hi.lo = one(T)
|
||||
inc result.hi.lo
|
||||
|
||||
# High part - z2
|
||||
result.hi.lo += z1.hi
|
||||
@ -113,9 +125,17 @@ func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) =
|
||||
# Finalize low part
|
||||
result.lo.hi += z1.lo
|
||||
if result.lo.hi < z1.lo:
|
||||
result.hi += one(UintImpl[T])
|
||||
inc result.hi
|
||||
|
||||
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline, noInit.}=
|
||||
func extPrecAddMul[T](result: var UintImpl[UintImpl[T]], u, v: UintImpl[T]) =
|
||||
## Extended precision fused in-place addition & multiplication
|
||||
extPrecMulImpl(result, `+=`, u, v)
|
||||
|
||||
func extPrecMul*[T](result: var UintImpl[UintImpl[T]], u, v: UintImpl[T]) =
|
||||
## Extended precision multiplication
|
||||
extPrecMulImpl(result, `=`, u, v)
|
||||
|
||||
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline, fooPragma.}=
|
||||
## Multiplication for multi-precision unsigned uint
|
||||
#
|
||||
# For our representation, it is similar to school grade multiplication
|
||||
|
@ -7,5 +7,5 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_public, ./int_public, ./init
|
||||
export uint_public, int_public, init
|
||||
import ./uint_public, ./int_public, ./io
|
||||
export uint_public, int_public, io
|
||||
|
@ -9,20 +9,20 @@
|
||||
|
||||
import ./private/datatypes, macros
|
||||
export StUint, UintImpl, uintImpl # TODO remove the need to export UintImpl and this macro
|
||||
|
||||
{.pragma: fooPragma.}
|
||||
type
|
||||
UInt128* = StUint[128]
|
||||
UInt256* = StUint[256]
|
||||
|
||||
template make_conv(conv_name: untyped, size: int): untyped =
|
||||
func `convname`*(n: SomeInteger): StUint[size] {.inline, noInit.}=
|
||||
func `convname`*(n: SomeInteger): StUint[size] {.inline, fooPragma.}=
|
||||
n.stuint(size)
|
||||
|
||||
make_conv(u128, 128)
|
||||
make_conv(u256, 256)
|
||||
|
||||
template make_unary(op, ResultTy): untyped =
|
||||
func `op`*(x: StUint): ResultTy {.noInit, inline.} =
|
||||
func `op`*(x: StUint): ResultTy {.fooPragma, inline.} =
|
||||
when ResultTy is StUint:
|
||||
result.data = op(x.data)
|
||||
else:
|
||||
@ -30,7 +30,7 @@ template make_unary(op, ResultTy): untyped =
|
||||
export op
|
||||
|
||||
template make_binary(op, ResultTy): untyped =
|
||||
func `op`*(x, y: StUint): ResultTy {.noInit, inline.} =
|
||||
func `op`*(x, y: StUint): ResultTy {.fooPragma, inline.} =
|
||||
when ResultTy is StUint:
|
||||
result.data = op(x.data, y.data)
|
||||
else:
|
||||
@ -56,7 +56,7 @@ import ./private/uint_div
|
||||
|
||||
make_binary(`div`, StUint)
|
||||
make_binary(`mod`, StUint)
|
||||
func divmod*(x, y: StUint): tuple[quot, rem: StUint] {.noInit, inline.} =
|
||||
func divmod*(x, y: StUint): tuple[quot, rem: StUint] {.fooPragma, inline.} =
|
||||
(result.quot.data, result.rem.data) = divmod(x.data, y.data)
|
||||
|
||||
import ./private/uint_comparison
|
||||
@ -72,7 +72,7 @@ make_unary(`not`, StUint)
|
||||
make_binary(`or`, StUint)
|
||||
make_binary(`and`, StUint)
|
||||
make_binary(`xor`, StUint)
|
||||
proc `shr`*(x: StUint, y: SomeInteger): StUint {.noInit, inline, noSideEffect.} =
|
||||
proc `shr`*(x: StUint, y: SomeInteger): StUint {.fooPragma, inline, noSideEffect.} =
|
||||
result.data = x.data shr y
|
||||
proc `shl`*(x: StUint, y: SomeInteger): StUint {.noInit, inline, noSideEffect.} =
|
||||
proc `shl`*(x: StUint, y: SomeInteger): StUint {.fooPragma, inline, noSideEffect.} =
|
||||
result.data = x.data shl y
|
||||
|
@ -16,3 +16,5 @@ import test_uint_endianness,
|
||||
import test_int_endianness,
|
||||
test_int_comparison,
|
||||
test_int_addsub
|
||||
|
||||
import test_io
|
||||
|
@ -107,3 +107,15 @@ suite "Testing signed int division and modulo implementation":
|
||||
# check: q.hi == 0'u64
|
||||
# check: r.lo == 1'u64
|
||||
# check: r.hi == 0'u64
|
||||
|
||||
test "Divmod(1234567891234567890, 10) returns the correct result":
|
||||
let a = cast[Stint[64]](1234567891234567890'i64)
|
||||
let b = cast[Stint[64]](10'i64)
|
||||
|
||||
let qr = divmod(a, b)
|
||||
|
||||
let q = cast[int64](qr.quot)
|
||||
let r = cast[int64](qr.rem)
|
||||
|
||||
check: q == 123456789123456789'i64
|
||||
check: r == 0'i64
|
||||
|
99
tests/test_io.nim
Normal file
99
tests/test_io.nim
Normal file
@ -0,0 +1,99 @@
|
||||
# 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 ../src/stint, unittest, strutils
|
||||
|
||||
suite "Testing input and output procedures":
|
||||
test "Creation from decimal strings":
|
||||
block:
|
||||
let a = parse(Stint[64], "123456789")
|
||||
let b = 123456789.stint(64)
|
||||
|
||||
check: a == b
|
||||
check: 123456789'i64 == cast[int64](a)
|
||||
|
||||
block:
|
||||
let a = parse(Stuint[64], "123456789")
|
||||
let b = 123456789.stuint(64)
|
||||
|
||||
check: a == b
|
||||
check: 123456789'u64 == cast[uint64](a)
|
||||
|
||||
block:
|
||||
let a = parse(Stint[64], "-123456789")
|
||||
let b = (-123456789).stint(64)
|
||||
|
||||
check: a == b
|
||||
check: -123456789'i64 == cast[int64](a)
|
||||
|
||||
test "Creation from hex strings":
|
||||
block:
|
||||
let a = parse(Stint[64], "0xFF", 16)
|
||||
let b = 255.stint(64)
|
||||
|
||||
check: a == b
|
||||
check: 255'i64 == cast[int64](a)
|
||||
|
||||
block:
|
||||
let a = parse(Stuint[64], "0xFF", 16)
|
||||
let b = 255.stuint(64)
|
||||
|
||||
check: a == b
|
||||
check: 255'u64 == cast[uint64](a)
|
||||
|
||||
block:
|
||||
let a = parse(Stint[16], "0xFFFF", 16)
|
||||
let b = (-1'i16).stint(16)
|
||||
|
||||
check: a == b
|
||||
check: -1'i16 == cast[int16](a)
|
||||
|
||||
test "Conversion to decimal strings":
|
||||
block:
|
||||
let a = 1234567891234567890.stint(128)
|
||||
check: a.toString == "1234567891234567890"
|
||||
|
||||
block:
|
||||
let a = 1234567891234567890.stuint(128)
|
||||
check: a.toString == "1234567891234567890"
|
||||
|
||||
block:
|
||||
let a = (-1234567891234567890).stint(128)
|
||||
check: a.toString == "-1234567891234567890"
|
||||
|
||||
test "Conversion to hex strings":
|
||||
block:
|
||||
let a = 0x1234567890ABCDEF.stint(128)
|
||||
check: a.toString(base = 16).toUpperAscii == "1234567890ABCDEF"
|
||||
|
||||
block:
|
||||
let a = 0x1234567890ABCDEF.stuint(128)
|
||||
check: a.toString(base = 16).toUpperAscii == "1234567890ABCDEF"
|
||||
|
||||
# TODO: negative hex
|
||||
|
||||
test "Hex dump":
|
||||
block:
|
||||
let a = 0x1234'i32.stint(32)
|
||||
check: a.dumpHex(bigEndian).toUpperAscii == "00001234"
|
||||
|
||||
block:
|
||||
let a = 0x1234'i32.stint(32)
|
||||
check: a.dumpHex(littleEndian).toUpperAscii == "34120000"
|
||||
|
||||
test "Back and forth bigint conversion consistency":
|
||||
block:
|
||||
let s = "1234567890123456789012345678901234567890123456789"
|
||||
let a = parse(StInt[512], s)
|
||||
check: a.toString == s
|
||||
|
||||
block:
|
||||
let s = "1234567890123456789012345678901234567890123456789"
|
||||
let a = parse(StUInt[512], s)
|
||||
check: a.toString == s
|
@ -56,3 +56,15 @@ suite "Testing unsigned int division and modulo implementation":
|
||||
check: q.hi == 0'u64
|
||||
check: r.lo == 1'u64
|
||||
check: r.hi == 0'u64
|
||||
|
||||
test "Divmod(1234567891234567890, 10) returns the correct result":
|
||||
let a = cast[StUint[64]](1234567891234567890'u64)
|
||||
let b = cast[StUint[64]](10'u64)
|
||||
|
||||
let qr = divmod(a, b)
|
||||
|
||||
let q = cast[uint64](qr.quot)
|
||||
let r = cast[uint64](qr.rem)
|
||||
|
||||
check: q == 123456789123456789'u64
|
||||
check: r == 0'u64
|
||||
|
Loading…
x
Reference in New Issue
Block a user