Merge pull request #27 from status-im/signed-integers
Add Signed integers
This commit is contained in:
commit
f520f7817b
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -11,25 +11,13 @@
|
|||
|
||||
import
|
||||
strutils,
|
||||
../private/[uint_type, getSize]
|
||||
../private/datatypes
|
||||
|
||||
func tohexBE*[T: uint8 or uint16 or uint32 or uint64](x: T): string =
|
||||
func tohexBE*(x: UintImpl or IntImpl or SomeInteger): string =
|
||||
## Stringify an uint to hex, Most significant byte on the left
|
||||
## i.e. a 1.uint64 will be 00000001
|
||||
|
||||
let bytes = cast[ptr array[T.sizeof, byte]](x.unsafeaddr)
|
||||
|
||||
result = ""
|
||||
when system.cpuEndian == littleEndian:
|
||||
for i in countdown(T.sizeof - 1, 0):
|
||||
result.add toHex(bytes[i])
|
||||
else:
|
||||
for i in 0 ..< T.sizeof:
|
||||
result.add toHex(bytes[i])
|
||||
|
||||
func tohexBE*(x: UintImpl): string =
|
||||
## Stringify an uint to hex, Most significant byte on the left
|
||||
## i.e. a (2.uint128)^64 + 1 will be 0000000100000001
|
||||
## i.e.
|
||||
## - 1.uint64 will be 00000001
|
||||
## - (2.uint128)^64 + 1 will be 0000000100000001
|
||||
|
||||
const size = getSize(x) div 8
|
||||
|
||||
|
@ -42,3 +30,6 @@ func tohexBE*(x: UintImpl): string =
|
|||
else:
|
||||
for i in 0 ..< size:
|
||||
result.add toHex(bytes[i])
|
||||
|
||||
func tohexBE*(x: Stint or StUint): string {.inline.}=
|
||||
x.data.tohexBE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,11 +7,10 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import typetraits
|
||||
|
||||
import ./private/uint_type
|
||||
|
||||
import typetraits
|
||||
import
|
||||
./private/datatypes,
|
||||
./private/int_negabs,
|
||||
typetraits
|
||||
|
||||
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
||||
assert n >= 0.T
|
||||
|
@ -33,3 +32,32 @@ func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
|||
r_ptr[r_ptr[].len - 1] = n
|
||||
else:
|
||||
result.data = (type result.data)(n)
|
||||
|
||||
func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
|
||||
|
||||
when result.data is 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)
|
|
@ -0,0 +1,82 @@
|
|||
# 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, macros
|
||||
export StInt, IntImpl, intImpl # TODO remove the need to export intImpl and this macro
|
||||
|
||||
type
|
||||
Int128* = Stint[128]
|
||||
Int256* = Stint[256]
|
||||
|
||||
template make_conv(conv_name: untyped, size: int): untyped =
|
||||
func `convname`*(n: SomeInteger): Stint[size] {.inline, noInit.}=
|
||||
n.stint(size)
|
||||
|
||||
make_conv(i128, 128)
|
||||
make_conv(i256, 256)
|
||||
|
||||
template make_unary(op, ResultTy): untyped =
|
||||
func `op`*(x: Stint): ResultTy {.noInit, inline.} =
|
||||
when ResultTy is Stint:
|
||||
result.data = op(x.data)
|
||||
else:
|
||||
op(x.data)
|
||||
export op
|
||||
|
||||
template make_binary(op, ResultTy): untyped =
|
||||
func `op`*(x, y: Stint): ResultTy {.noInit, inline.} =
|
||||
when ResultTy is Stint:
|
||||
result.data = op(x.data, y.data)
|
||||
else:
|
||||
op(x.data, y.data)
|
||||
export `op`
|
||||
|
||||
template make_binary_inplace(op): untyped =
|
||||
func `op`*(x: var Stint, y: Stint) {.inline.} =
|
||||
op(x.data, y.data)
|
||||
export op
|
||||
|
||||
import ./private/int_addsub
|
||||
|
||||
make_binary(`+`, Stint)
|
||||
make_binary_inplace(`+=`)
|
||||
make_binary(`-`, Stint)
|
||||
make_binary_inplace(`-=`)
|
||||
|
||||
import ./private/int_negabs
|
||||
make_unary(`-`, Stint)
|
||||
make_unary(abs, Stint)
|
||||
|
||||
import ./private/int_mul
|
||||
make_binary(`*`, Stint)
|
||||
|
||||
import ./private/int_div
|
||||
|
||||
make_binary(`div`, Stint)
|
||||
make_binary(`mod`, Stint)
|
||||
func divmod*(x, y: Stint): tuple[quot, rem: Stint] {.noInit, inline.} =
|
||||
(result.quot.data, result.rem.data) = divmod(x.data, y.data)
|
||||
|
||||
import ./private/int_comparison
|
||||
|
||||
make_binary(`<`, bool)
|
||||
make_binary(`<=`, bool)
|
||||
make_binary(`==`, bool)
|
||||
func isZero*(x: Stint): bool {.inline.} = isZero x.data
|
||||
|
||||
import ./private/int_bitwise_ops
|
||||
|
||||
make_unary(`not`, Stint)
|
||||
make_binary(`or`, Stint)
|
||||
make_binary(`and`, Stint)
|
||||
make_binary(`xor`, Stint)
|
||||
# proc `shr`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} =
|
||||
# result.data = x.data shr y
|
||||
# proc `shl`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} =
|
||||
# result.data = x.data shl y
|
|
@ -0,0 +1,149 @@
|
|||
# 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, macros, as_words
|
||||
|
||||
proc optimInt*(x: NimNode): NimNode =
|
||||
let size = getSize(x)
|
||||
|
||||
if size > 64:
|
||||
result = quote do:
|
||||
# We represent as unsigned int. Signedness will be managed at a higher level.
|
||||
array[`size` div 64, uint64]
|
||||
elif size == 64:
|
||||
result = quote do:
|
||||
int64
|
||||
elif size == 32:
|
||||
result = quote do:
|
||||
int32
|
||||
elif size == 16:
|
||||
result = quote do:
|
||||
int16
|
||||
elif size == 8:
|
||||
result = quote do:
|
||||
int8
|
||||
else:
|
||||
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
|
||||
else: false
|
||||
|
||||
macro most_significant_word*(x: IntImpl): untyped =
|
||||
|
||||
let optim_type = optimInt(x)
|
||||
if optim_type.isInt:
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
result = quote do:
|
||||
cast[int](cast[`optim_type`](`x`)[`msw_pos`])
|
||||
|
||||
macro most_significant_word_mut*(x: var IntImpl): untyped =
|
||||
|
||||
let optim_type = optimInt(x)
|
||||
if optim_type.isInt:
|
||||
result = quote do:
|
||||
cast[var `optim_type`](`x`.addr)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
result = quote do:
|
||||
(cast[ptr `optim_type`](`x`.unsafeAddr)[`msw_pos`])[]
|
||||
|
||||
macro asSignedWordsZip*[T](
|
||||
x, y: IntImpl[T],
|
||||
loopBody: untyped): untyped =
|
||||
## Iterates over x and y, as an array of words.
|
||||
## Input:
|
||||
## - x, y: The multiprecision ints
|
||||
## - loopBody: the operation you want to do.
|
||||
## For the most significant word,
|
||||
## the operation will be sign aware.
|
||||
## for the next words it will ignore sign.
|
||||
## Iteration is always done from most significant to least significant
|
||||
let
|
||||
optim_type = optimInt(x)
|
||||
idx = ident("idx_asSignedWordsRawZip")
|
||||
var
|
||||
first_x, first_y: NimNode
|
||||
next_x, next_y: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
to_replace.add x
|
||||
to_replace.add y
|
||||
|
||||
# We directly cast the first x and y if the result fits in a word
|
||||
# Otherwise we special case the most significant word
|
||||
if optim_type.isInt:
|
||||
first_x = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
first_y = quote do:
|
||||
cast[`optim_type`](`y`)
|
||||
else:
|
||||
first_x = getAST(most_significant_word(x))
|
||||
first_y = getAST(most_significant_word(y))
|
||||
|
||||
replacing.add first_x
|
||||
replacing.add first_y
|
||||
|
||||
let firstReplacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
# Reset the replacement array
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
# Setup the loop variables
|
||||
next_x = ident("x_asSignedWordsRawZip")
|
||||
next_y = ident("y_asSignedWordsRawZip")
|
||||
|
||||
# We replace the inner loop with the next_x[idx]
|
||||
replacing.add quote do:
|
||||
`next_x`[`idx`]
|
||||
replacing.add quote do:
|
||||
`next_y`[`idx`]
|
||||
|
||||
let nextReplacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
# Result:
|
||||
result = newStmtList()
|
||||
result.add firstReplacedAST
|
||||
|
||||
if not optim_type.isInt:
|
||||
# if we have multiple iterations to do
|
||||
if system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
|
||||
let
|
||||
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in 1 ..< `next_x`[].len:
|
||||
# We start from the second word
|
||||
`nextReplacedAST`
|
||||
else:
|
||||
# Little-Endian, iteration in reverse
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
|
||||
let
|
||||
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in countdown(`next_x`[].len - 2, 0):
|
||||
# We stop stop at the second to last word
|
||||
`nextReplacedAST`
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type, macros
|
||||
import ./datatypes, macros
|
||||
|
||||
proc optim(x: NimNode): NimNode =
|
||||
proc optimUint(x: NimNode): NimNode =
|
||||
let size = getSize(x)
|
||||
|
||||
if size > 64:
|
||||
|
@ -30,7 +30,14 @@ proc optim(x: NimNode): NimNode =
|
|||
else:
|
||||
error "Unreachable path reached"
|
||||
|
||||
proc replaceNodes(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNode =
|
||||
proc isUint(x: NimNode): static[bool] =
|
||||
if eqIdent(x, "uint64"): true
|
||||
elif eqIdent(x, "uint32"): true
|
||||
elif eqIdent(x, "uint16"): true
|
||||
elif eqIdent(x, "uint8"): true
|
||||
else: false
|
||||
|
||||
proc replaceNodes*(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNode =
|
||||
# Args:
|
||||
# - The full syntax tree
|
||||
# - an array of replacement value
|
||||
|
@ -53,21 +60,16 @@ proc replaceNodes(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNod
|
|||
return rTree
|
||||
result = inspect(ast)
|
||||
|
||||
proc isUint(x: NimNode): static[bool] =
|
||||
if eqIdent(x, "uint64"): true
|
||||
elif eqIdent(x, "uint32"): true
|
||||
elif eqIdent(x, "uint16"): true
|
||||
elif eqIdent(x, "uint8"): true
|
||||
else: false
|
||||
|
||||
macro asWords*[T](n: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro asWords*(n: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over n, as an array of words.
|
||||
## Input:
|
||||
## - n: The Multiprecision int
|
||||
## - body: the operation you want to do on each word of n
|
||||
## If iteration is needed, it is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(n)
|
||||
optim_type = optimUint(n)
|
||||
var
|
||||
inner_n: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
|
@ -96,13 +98,16 @@ macro asWords*[T](n: UintImpl[T], ignoreEndianness: static[bool], loopBody: unty
|
|||
else:
|
||||
assert false, "Not implemented"
|
||||
|
||||
macro asWordsZip*[T](x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over x and y, as an array of words.
|
||||
## Input:
|
||||
## - x, y: The multiprecision ints
|
||||
## If iteration is needed, it is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_x, inner_y: NimNode
|
||||
|
@ -155,15 +160,19 @@ macro asWordsZip*[T](x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody
|
|||
for `idx` in countdown(`inner_x`[].len - 1, 0):
|
||||
`replacedAST`
|
||||
|
||||
macro m_asWordsZip*[T](m: var UintImpl[T], x: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## Iteration is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x: NimNode
|
||||
|
@ -217,15 +226,19 @@ macro m_asWordsZip*[T](m: var UintImpl[T], x: UintImpl[T], ignoreEndianness: sta
|
|||
`replacedAST`
|
||||
|
||||
|
||||
macro m_asWordsZip*[T](m: var UintImpl[T], x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## Iteration is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x, inner_y: NimNode
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type, stdlib_bitops
|
||||
import ./datatypes, stdlib_bitops, as_signed_words
|
||||
export stdlib_bitops
|
||||
|
||||
# We reuse bitops from Nim standard lib, and expand it for multi-precision int.
|
||||
|
@ -24,3 +24,25 @@ func countLeadingZeroBits*(n: UintImpl): int {.inline.} =
|
|||
result = if hi_clz == maxHalfRepr:
|
||||
n.lo.countLeadingZeroBits + maxHalfRepr
|
||||
else: hi_clz
|
||||
|
||||
func msb*[T: SomeInteger](n: T): T {.inline.}=
|
||||
## Returns the most significant bit of an integer.
|
||||
|
||||
when T is int64 or (T is int and sizeof(int) == 8):
|
||||
type UInt = uint64
|
||||
elif T is int32 or (T is int and sizeof(int) == 4):
|
||||
type Uint = uint32
|
||||
elif T is int16:
|
||||
type Uint = uint16
|
||||
elif T is int8:
|
||||
type Uint = uint8
|
||||
else:
|
||||
type Uint = T
|
||||
|
||||
const msb_pos = sizeof(T) * 8 - 1
|
||||
result = T(cast[Uint](n) shr msb_pos)
|
||||
|
||||
func msb*(n: IntImpl): auto {.inline.}=
|
||||
## Returns the most significant bit of an arbitrary precision integer.
|
||||
|
||||
result = msb most_significant_word(n)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,27 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type,
|
||||
macros, typetraits
|
||||
|
||||
func initUintImpl*[InType, OutType](x: InType, _: typedesc[OutType]): OutType {.inline.} =
|
||||
|
||||
const
|
||||
size_in = getSize(x)
|
||||
size_out = getSize(result)
|
||||
|
||||
static:
|
||||
assert size_out >= size_in, "The result type size (" & $size_out &
|
||||
" for " & $OutType.name &
|
||||
") should be equal or bigger than the input type size (" & $size_in &
|
||||
" for " & $InType.name & ")."
|
||||
|
||||
when OutType is SomeUnsignedInt:
|
||||
result = x.OutType
|
||||
elif size_in == size_out:
|
||||
result = cast[type result](x)
|
||||
else:
|
||||
result.lo = initUintImpl(x, type result.lo)
|
||||
import ./datatypes
|
||||
|
||||
func toSubtype*[T: SomeInteger](b: bool, _: typedesc[T]): T {.inline.}=
|
||||
b.T
|
||||
|
@ -36,21 +16,8 @@ func toSubtype*[T: UintImpl](b: bool, _: typedesc[T]): T {.inline.}=
|
|||
type SubTy = type result.lo
|
||||
result.lo = toSubtype(b, SubTy)
|
||||
|
||||
func zero*[T: BaseUint](_: typedesc[T]): T {.inline.}=
|
||||
discard
|
||||
|
||||
func one*[T: BaseUint](_: typedesc[T]): T {.inline.}=
|
||||
when T is SomeUnsignedInt:
|
||||
result = T(1)
|
||||
else:
|
||||
let r_ptr = cast[ptr array[getSize(result) div 8, byte]](result.addr)
|
||||
when system.cpuEndian == bigEndian:
|
||||
r_ptr[0] = 1
|
||||
else:
|
||||
r_ptr[r_ptr[].len - 1] = 1
|
||||
|
||||
func toUint*(n: UintImpl): auto {.inline.}=
|
||||
## Casts a multiprecision integer to an uint of the same size
|
||||
## Casts an unsigned integer to an uint of the same size
|
||||
|
||||
# TODO: uint128 support
|
||||
when n.sizeof > 8:
|
||||
|
@ -78,19 +45,3 @@ func asDoubleUint*(n: BaseUint): auto {.inline.} =
|
|||
)
|
||||
|
||||
n.toUint.Double
|
||||
|
||||
|
||||
func toUintImpl*(n: uint16|uint32|uint64): auto {.inline.} =
|
||||
## Cast an integer to the corresponding size UintImpl
|
||||
# Sometimes direct casting doesn't work and we must cast through a pointer
|
||||
|
||||
when n is uint64:
|
||||
return (cast[ptr [UintImpl[uint32]]](unsafeAddr n))[]
|
||||
elif n is uint32:
|
||||
return (cast[ptr [UintImpl[uint16]]](unsafeAddr n))[]
|
||||
elif n is uint16:
|
||||
return (cast[ptr [UintImpl[uint8]]](unsafeAddr n))[]
|
||||
|
||||
func toUintImpl*(n: UintImpl): UintImpl {.inline.} =
|
||||
## No op
|
||||
n
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -12,16 +12,16 @@
|
|||
import macros
|
||||
|
||||
|
||||
# The macro getUintImpl must be exported
|
||||
# The macro uintImpl must be exported
|
||||
|
||||
when defined(mpint_test):
|
||||
macro getUintImpl*(bits: static[int]): untyped =
|
||||
macro uintImpl*(bits: static[int]): untyped =
|
||||
# Test version, StUint[64] = 2 uint32. Test the logic of the library
|
||||
assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"
|
||||
assert bits >= 16, "The number of bits in a should be greater or equal to 16"
|
||||
|
||||
if bits >= 128:
|
||||
let inner = getAST(getUintImpl(bits div 2))
|
||||
let inner = getAST(uintImpl(bits div 2))
|
||||
result = newTree(nnkBracketExpr, ident("UintImpl"), inner)
|
||||
elif bits == 64:
|
||||
result = newTree(nnkBracketExpr, ident("UintImpl"), ident("uint32"))
|
||||
|
@ -31,14 +31,34 @@ when defined(mpint_test):
|
|||
result = newTree(nnkBracketExpr, ident("UintImpl"), ident("uint8"))
|
||||
else:
|
||||
error "Fatal: unreachable"
|
||||
|
||||
macro intImpl*(bits: static[int]): untyped =
|
||||
# Test version, StInt[64] = 2 uint32. Test the logic of the library
|
||||
# Note that ints are implemented in terms of unsigned ints
|
||||
# Signed operatiosn will be built on top of that.
|
||||
assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"
|
||||
assert bits >= 16, "The number of bits in a should be greater or equal to 16"
|
||||
|
||||
if bits >= 128:
|
||||
let inner = getAST(uintImpl(bits div 2)) # IntImpl is built on top of UintImpl
|
||||
result = newTree(nnkBracketExpr, ident("IntImpl"), inner)
|
||||
elif bits == 64:
|
||||
result = newTree(nnkBracketExpr, ident("IntImpl"), ident("uint32"))
|
||||
elif bits == 32:
|
||||
result = newTree(nnkBracketExpr, ident("IntImpl"), ident("uint16"))
|
||||
elif bits == 16:
|
||||
result = newTree(nnkBracketExpr, ident("IntImpl"), ident("uint8"))
|
||||
else:
|
||||
error "Fatal: unreachable"
|
||||
|
||||
else:
|
||||
macro getUintImpl*(bits: static[int]): untyped =
|
||||
macro uintImpl*(bits: static[int]): untyped =
|
||||
# Release version, StUint[64] = uint64.
|
||||
assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"
|
||||
assert bits >= 8, "The number of bits in a should be greater or equal to 8"
|
||||
|
||||
if bits >= 128:
|
||||
let inner = getAST(getUintImpl(bits div 2))
|
||||
let inner = getAST(uintImpl(bits div 2))
|
||||
result = newTree(nnkBracketExpr, ident("UintImpl"), inner)
|
||||
elif bits == 64:
|
||||
result = ident("uint64")
|
||||
|
@ -51,6 +71,25 @@ else:
|
|||
else:
|
||||
error "Fatal: unreachable"
|
||||
|
||||
macro intImpl*(bits: static[int]): untyped =
|
||||
# Release version, StInt[64] = int64.
|
||||
# Note that int of size 128+ are implemented in terms of unsigned ints
|
||||
# Signed operations will be built on top of that.
|
||||
|
||||
if bits >= 128:
|
||||
let inner = getAST(uintImpl(bits div 2))
|
||||
result = newTree(nnkBracketExpr, ident("IntImpl"), inner)
|
||||
elif bits == 64:
|
||||
result = ident("int64")
|
||||
elif bits == 32:
|
||||
result = ident("int32")
|
||||
elif bits == 16:
|
||||
result = ident("int16")
|
||||
elif bits == 8:
|
||||
result = ident("int8")
|
||||
else:
|
||||
error "Fatal: unreachable"
|
||||
|
||||
proc getSize*(x: NimNode): static[int] =
|
||||
|
||||
# Size of doesn't always work at compile-time, pending PR https://github.com/nim-lang/Nim/pull/5664
|
||||
|
@ -59,7 +98,8 @@ proc getSize*(x: NimNode): static[int] =
|
|||
var node = x.getTypeInst
|
||||
|
||||
while node.kind == nnkBracketExpr:
|
||||
assert eqIdent(node[0], "UintImpl")
|
||||
assert eqIdent(node[0], "UintImpl") or eqIdent(node[0], "IntImpl"), (
|
||||
"getSize only supports primitive integers, Stint and Stuint")
|
||||
multiplier *= 2
|
||||
node = node[1]
|
||||
|
||||
|
@ -84,8 +124,6 @@ macro getSize*(x: typed): untyped =
|
|||
|
||||
type
|
||||
# ### Private ### #
|
||||
# If this is not in the same type section
|
||||
# the compiler has trouble
|
||||
BaseUint* = UintImpl or SomeUnsignedInt
|
||||
|
||||
UintImpl*[Baseuint] = object
|
||||
|
@ -93,8 +131,18 @@ type
|
|||
lo*, hi*: BaseUint
|
||||
else:
|
||||
hi*, lo*: BaseUint
|
||||
|
||||
IntImpl*[Baseuint] = object
|
||||
# Ints are implemented in terms of uints
|
||||
when system.cpuEndian == littleEndian:
|
||||
lo*, hi*: BaseUint
|
||||
else:
|
||||
hi*, lo*: BaseUint
|
||||
|
||||
# ### Private ### #
|
||||
|
||||
StUint*[bits: static[int]] = object
|
||||
data*: getUintImpl(bits)
|
||||
# wrapped in object to avoid recursive calls
|
||||
data*: uintImpl(bits)
|
||||
|
||||
StInt*[bits: static[int]] = object
|
||||
data*: intImpl(bits)
|
|
@ -0,0 +1,42 @@
|
|||
# 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, typetraits
|
||||
|
||||
func initUintImpl*[InType, OutType](x: InType, _: typedesc[OutType]): OutType {.inline.} =
|
||||
|
||||
const
|
||||
size_in = getSize(x)
|
||||
size_out = getSize(result)
|
||||
|
||||
static:
|
||||
assert size_out >= size_in, "The result type size (" & $size_out &
|
||||
" for " & $OutType.name &
|
||||
") should be equal or bigger than the input type size (" & $size_in &
|
||||
" for " & $InType.name & ")."
|
||||
|
||||
when OutType is SomeUnsignedInt:
|
||||
result = x.OutType
|
||||
elif size_in == size_out:
|
||||
result = cast[type result](x)
|
||||
else:
|
||||
result.lo = initUintImpl(x, type result.lo)
|
||||
|
||||
func zero*[T: BaseUint](_: typedesc[T]): T {.inline.}=
|
||||
discard
|
||||
|
||||
func one*[T: BaseUint or IntImpl](_: typedesc[T]): T {.inline.}=
|
||||
when T is SomeInteger:
|
||||
result = T(1)
|
||||
else:
|
||||
let r_ptr = cast[ptr array[getSize(result) div 8, byte]](result.addr)
|
||||
when system.cpuEndian == littleEndian:
|
||||
r_ptr[0] = 1
|
||||
else:
|
||||
r_ptr[r_ptr[].len - 1] = 1
|
|
@ -0,0 +1,47 @@
|
|||
# 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, ./conversion, ./as_signed_words
|
||||
|
||||
func `+`*(x, y: IntImpl): IntImpl {.noInit, 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
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(
|
||||
((result.most_significant_word xor x.most_significant_word) >= 0) or
|
||||
((result.most_significant_word xor y.most_significant_word) >= 0)
|
||||
):
|
||||
return
|
||||
raise newException(OverflowError, "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 {.noInit, inline.}=
|
||||
# Substraction for multi-precision signed int.
|
||||
|
||||
type SubTy = type x.lo
|
||||
result.lo = x.lo - y.lo
|
||||
result.hi = x.hi - y.hi - (x.lo < y.lo).toSubtype(SubTy)
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(
|
||||
((result.most_significant_word xor x.most_significant_word) >= 0) or
|
||||
((result.most_significant_word xor (not y).most_significant_word) >= 0)
|
||||
):
|
||||
return
|
||||
raise newException(OverflowError, "Substraction underflow")
|
||||
|
||||
func `-=`*(x: var IntImpl, y: IntImpl) {.inline.}=
|
||||
## In-place substraction for multi-precision signed int.
|
||||
x = x - y
|
|
@ -0,0 +1,31 @@
|
|||
# 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, ./as_words
|
||||
|
||||
|
||||
func `not`*(x: IntImpl): IntImpl {.noInit, inline.}=
|
||||
## Bitwise complement of unsigned integer x
|
||||
m_asWordsZip(result, x, ignoreEndianness = true):
|
||||
result = not x
|
||||
|
||||
func `or`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
## `Bitwise or` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x or y
|
||||
|
||||
func `and`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
## `Bitwise and` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x and y
|
||||
|
||||
func `xor`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
|
||||
## `Bitwise xor` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x xor y
|
|
@ -0,0 +1,45 @@
|
|||
# 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, ./bithacks, ./as_words, ./as_signed_words,
|
||||
./bithacks
|
||||
|
||||
func isZero*(n: SomeSignedInt): bool {.inline.} =
|
||||
n == 0
|
||||
|
||||
func isZero*(n: IntImpl): bool {.inline.} =
|
||||
asWords(n, ignoreEndianness = true):
|
||||
if n != 0:
|
||||
return false
|
||||
return true
|
||||
|
||||
func isNegative*(n: IntImpl): bool {.inline.} =
|
||||
## Returns true if a number is negative:
|
||||
n.msb.bool
|
||||
|
||||
func `<`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Lower comparison for multi-precision integers
|
||||
asSignedWordsZip(x, y):
|
||||
if x != y:
|
||||
return x < y
|
||||
return false # they're equal
|
||||
|
||||
func `==`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Equal comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = true):
|
||||
if x != y:
|
||||
return false
|
||||
return true # they're equal
|
||||
|
||||
func `<=`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Lower or equal comparison for multi-precision integers
|
||||
asSignedWordsZip(x, y):
|
||||
if x != y:
|
||||
return x < y
|
||||
return true # they're equal
|
|
@ -0,0 +1,59 @@
|
|||
# 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_negabs, ./uint_div
|
||||
|
||||
# Here are the expected signs for division/modulo by opposite signs and both negative numbers
|
||||
# in EVM
|
||||
# Parity: https://github.com/paritytech/parity/blob/684322cd6f210684b890055c43d56bb1bc8cae15/ethcore/evm/src/interpreter/mod.rs#L729-L756
|
||||
# - SDIV is sign(a) xor sign(b)
|
||||
# - SMOD is sign(a)
|
||||
# Go-Ethereum: https://github.com/ethereum/go-ethereum/blob/ba1030b6b84f810c04a82221a1b1c0a3dbf499a8/core/vm/instructions.go#L76-L104
|
||||
# - SDIV is "if same sign, div(abs(a), abs(b)), else -div(abs(a), abs(b))
|
||||
# - SMOD is "sign(a)"
|
||||
#
|
||||
# in Nim
|
||||
# echo "10 div 3: " & $(10 div 3) # 3
|
||||
# echo "10 mod 3: " & $(10 mod 3) # 1
|
||||
# echo '\n'
|
||||
# echo "10 div -3: " & $(10 div -3) # -3
|
||||
# echo "10 mod -3: " & $(10 mod -3) # 1
|
||||
# echo '\n'
|
||||
# echo "-10 div 3: " & $(-10 div 3) # -3
|
||||
# echo "-10 mod 3: " & $(-10 mod 3) # -1
|
||||
# echo '\n'
|
||||
# echo "-10 div -3: " & $(-10 div -3) # 3
|
||||
# echo "-10 mod -3: " & $(-10 mod -3) # -1
|
||||
# echo '\n'
|
||||
|
||||
func divmod*(x, y: SomeSignedInt): tuple[quot, rem: SomeSignedInt] {.noInit, 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.}=
|
||||
## Divmod operation for multi-precision signed integer
|
||||
|
||||
result = cast[type result](divmod(
|
||||
cast[UintImpl[T]](x.abs),
|
||||
cast[UintImpl[T]](y.abs)
|
||||
))
|
||||
|
||||
if (x.isNegative xor y.isNegative):
|
||||
# If opposite signs
|
||||
result.quot = -result.quot
|
||||
if x.isNegative:
|
||||
result.rem = -result.rem
|
||||
|
||||
func `div`*(x, y: IntImpl): IntImpl {.inline.} =
|
||||
## Division operation for multi-precision signed integer
|
||||
divmod(x,y).quot
|
||||
|
||||
func `mod`*(x, y: IntImpl): IntImpl {.inline.} =
|
||||
## Division operation for multi-precision signed integer
|
||||
divmod(x,y).rem
|
|
@ -0,0 +1,27 @@
|
|||
# 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, ./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.}=
|
||||
|
||||
# The lowest signed int has representation
|
||||
# 0b0111_1111_1111_1111 ....
|
||||
# so we only have to unset the most significant bit.
|
||||
not low(T)
|
|
@ -0,0 +1,17 @@
|
|||
# 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_mul
|
||||
|
||||
func `*`*[T](x, y: IntImpl[T]): IntImpl[T] {.inline, noInit.}=
|
||||
## 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
|
||||
# TODO: overflow detection.
|
||||
cast[type result](cast[UIntImpl[T]](x) * cast[UIntImpl[T]](y))
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
|
||||
func `-`*[T: IntImpl](x: T): T {.noInit, inline.}=
|
||||
# Negate a multi-precision signed int.
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(x == low(T)):
|
||||
raise newException(OverflowError, "The lowest negative number cannot be negated")
|
||||
|
||||
result = not x
|
||||
result += one(T)
|
||||
|
||||
func abs*[T: IntImpl](x: T): T {.noInit, inline.}=
|
||||
## Returns the absolute value of a signed int.
|
||||
|
||||
result = if x.isNegative: -x
|
||||
else: x
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -8,7 +8,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./bithacks, ./conversion,
|
||||
./uint_type,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
./uint_bitwise_ops
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type, ./as_words
|
||||
import ./datatypes, ./as_words
|
||||
|
||||
|
||||
func `not`*(x: UintImpl): UintImpl {.noInit, inline.}=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type, ./as_words
|
||||
import ./datatypes, ./as_words
|
||||
|
||||
func isZero*(n: SomeUnsignedInt): bool {.inline.} =
|
||||
n == 0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,8 +7,8 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./bithacks, ./conversion,
|
||||
./uint_type,
|
||||
import ./bithacks, ./conversion, ./initialization,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
./uint_bitwise_ops,
|
||||
./uint_addsub,
|
||||
|
@ -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
|
||||
|
||||
proc divmod*(x, y: SomeInteger): tuple[quot, rem: SomeInteger] {.noSideEffect, inline.}=
|
||||
func divmod*(x, y: SomeUnsignedInt): tuple[quot, rem: SomeUnsignedInt] {.noInit, inline.}=
|
||||
# hopefully the compiler fuse that in a single op
|
||||
(x div y, x mod y)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# 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
|
||||
|
||||
func low*(T: typedesc[UintImpl]): T {.inline, noInit.}=
|
||||
zero(T)
|
||||
|
||||
func high*(T: typedesc[UintImpl]): T {.inline, noInit.}=
|
||||
not zero(T)
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -8,7 +8,8 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./conversion,
|
||||
./uint_type,
|
||||
./initialization,
|
||||
./datatypes,
|
||||
./uint_comparison,
|
||||
./uint_addsub
|
||||
|
||||
|
@ -114,7 +115,7 @@ func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) =
|
|||
if result.lo.hi < z1.lo:
|
||||
result.hi += one(UintImpl[T])
|
||||
|
||||
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline.}=
|
||||
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline, noInit.}=
|
||||
## Multiplication for multi-precision unsigned uint
|
||||
#
|
||||
# For our representation, it is similar to school grade multiplication
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,5 +7,5 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_public, ./uint_init
|
||||
export uint_public, uint_init
|
||||
import ./uint_public, ./int_public, ./init
|
||||
export uint_public, int_public, init
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,8 +7,8 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./private/uint_type, macros
|
||||
export StUint, UintImpl, getUintImpl # TODO remove the need to export UintImpl and this macro
|
||||
import ./private/datatypes, macros
|
||||
export StUint, UintImpl, uintImpl # TODO remove the need to export UintImpl and this macro
|
||||
|
||||
type
|
||||
UInt128* = StUint[128]
|
||||
|
|
|
@ -8,7 +8,7 @@ srcDir = "src"
|
|||
### Dependencies
|
||||
|
||||
# TODO remove test only requirements: https://github.com/nim-lang/nimble/issues/482
|
||||
requires "nim >= 0.18", "https://github.com/alehander42/nim-quicktest >= 0.0.9"
|
||||
requires "nim >= 0.18", "https://github.com/alehander42/nim-quicktest >= 0.0.12"
|
||||
|
||||
proc test(name: string, lang: string = "c") =
|
||||
if not dirExists "build":
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,8 +7,12 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import test_endianness,
|
||||
test_comparison,
|
||||
test_bitwise,
|
||||
test_addsub,
|
||||
test_muldiv
|
||||
import test_uint_endianness,
|
||||
test_uint_comparison,
|
||||
test_uint_bitwise,
|
||||
test_uint_addsub,
|
||||
test_uint_muldiv
|
||||
|
||||
import test_int_endianness,
|
||||
test_int_comparison,
|
||||
test_int_addsub
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest, quicktest
|
||||
import ../src/stint, unittest, quicktest
|
||||
|
||||
const itercount = 1000
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
# Requires "https://github.com/status-im/nim-ttmath#master"
|
||||
# Note that currently importing both Stint and TTMath will crash the compiler for unknown reason
|
||||
import ../src/mpint, unittest, quicktest, ttmath
|
||||
import ../src/stint, unittest, quicktest, ttmath
|
||||
|
||||
const itercount = 1000
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# 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
|
||||
|
||||
suite "Testing signed addition implementation":
|
||||
test "In-place addition gives expected result":
|
||||
|
||||
var a = 20182018.stint(64)
|
||||
let b = 20172017.stint(64)
|
||||
|
||||
a += b
|
||||
|
||||
check: cast[int64](a) == 20182018'i64 + 20172017'i64
|
||||
|
||||
test "Addition gives expected result":
|
||||
|
||||
let a = 20182018.stint(64)
|
||||
let b = 20172017.stint(64)
|
||||
|
||||
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)
|
||||
|
||||
check: cast[int16](a+b) == 200
|
||||
|
||||
suite "Testing signed substraction implementation":
|
||||
test "In-place substraction gives expected result":
|
||||
|
||||
var a = 20182018.stint(64)
|
||||
let b = 20172017.stint(64)
|
||||
|
||||
a -= b
|
||||
|
||||
check: cast[int64](a) == 20182018'i64 - 20172017'i64
|
||||
|
||||
test "Substraction gives expected result":
|
||||
|
||||
let a = 20182018.stint(64)
|
||||
let b = 20172017.stint(64)
|
||||
|
||||
check: cast[int64](a-b) == 20182018'i64 - 20172017'i64
|
|
@ -0,0 +1,56 @@
|
|||
# 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
|
||||
|
||||
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)
|
||||
not (a + a + a < b + b)
|
||||
-c < c
|
||||
-c < a
|
||||
-b < -a
|
||||
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)
|
||||
not (b + b > a + a + a)
|
||||
c > -c
|
||||
a > -c
|
||||
b > -c
|
||||
not(-b > -b)
|
||||
|
||||
test ">= operator":
|
||||
check:
|
||||
b >= a
|
||||
not (b >= a + b)
|
||||
b + b >= a + a + a
|
||||
c >= -c
|
||||
a >= -c
|
||||
b >= -c
|
||||
-b >= -b
|
|
@ -0,0 +1,28 @@
|
|||
# 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
|
||||
|
||||
suite "Testing signed int byte representation":
|
||||
test "Byte representation conforms to the platform endianness":
|
||||
block:
|
||||
let a = 20182018.stint(64)
|
||||
let b = 20182018'i64
|
||||
|
||||
type AsBytes = array[8, byte]
|
||||
|
||||
check cast[AsBytes](a) == cast[AsBytes](b)
|
||||
|
||||
block:
|
||||
let a = (-20182018).stint(64)
|
||||
let b = -20182018'i64
|
||||
|
||||
type AsBytes = array[8, byte]
|
||||
|
||||
check cast[AsBytes](a) == cast[AsBytes](b)
|
|
@ -0,0 +1,109 @@
|
|||
# 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
|
||||
|
||||
suite "Testing signed int multiplication implementation":
|
||||
test "Multiplication with result fitting in low half":
|
||||
|
||||
let a = 10000.stint(64)
|
||||
let b = 10000.stint(64)
|
||||
|
||||
check: cast[int64](a*b) == 100_000_000'i64 # need 27-bits
|
||||
|
||||
test "Multiplication with result overflowing low half":
|
||||
|
||||
let a = 1_000_000.stint(64)
|
||||
let b = 1_000_000.stint(64)
|
||||
|
||||
check: cast[int64](a*b) == 1_000_000_000_000'i64 # need 40 bits
|
||||
|
||||
test "Multiplication with result fitting in low half - opposite signs":
|
||||
|
||||
let a = -10000.stint(64)
|
||||
let b = 10000.stint(64)
|
||||
|
||||
check:
|
||||
cast[int64](a*b) == -100_000_000'i64 # need 27-bits
|
||||
cast[int64](b*a) == -100_000_000'i64
|
||||
|
||||
|
||||
test "Multiplication with result overflowing low half - opposite signs":
|
||||
|
||||
let a = -1_000_000.stint(64)
|
||||
let b = 1_000_000.stint(64)
|
||||
|
||||
check:
|
||||
cast[int64](a*b) == -1_000_000_000_000'i64 # need 40 bits
|
||||
cast[int64](b*a) == -1_000_000_000_000'i64
|
||||
|
||||
test "Multiplication with result fitting in low half - both negative":
|
||||
|
||||
let a = -10000.stint(64)
|
||||
let b = -10000.stint(64)
|
||||
|
||||
check: cast[int64](a*b) == 100_000_000'i64 # need 27-bits
|
||||
|
||||
test "Multiplication with result overflowing low half - both negative":
|
||||
|
||||
let a = -1_000_000.stint(64)
|
||||
let b = -1_000_000.stint(64)
|
||||
|
||||
check: cast[int64](a*b) == 1_000_000_000_000'i64 # need 40 bits
|
||||
|
||||
suite "Testing signed int division and modulo implementation":
|
||||
test "Divmod(100, 13) returns the correct result":
|
||||
|
||||
let a = 100.stint(64)
|
||||
let b = 13.stint(64)
|
||||
let qr = divmod(a, b)
|
||||
|
||||
check: cast[int64](qr.quot) == 7'i64
|
||||
check: cast[int64](qr.rem) == 9'i64
|
||||
|
||||
test "Divmod(-100, 13) returns the correct result":
|
||||
|
||||
let a = -100.stint(64)
|
||||
let b = 13.stint(64)
|
||||
let qr = divmod(a, b)
|
||||
|
||||
check: cast[int64](qr.quot) == -100'i64 div 13
|
||||
check: cast[int64](qr.rem) == -100'i64 mod 13
|
||||
|
||||
test "Divmod(100, -13) returns the correct result":
|
||||
|
||||
let a = 100.stint(64)
|
||||
let b = -13.stint(64)
|
||||
let qr = divmod(a, b)
|
||||
|
||||
check: cast[int64](qr.quot) == 100'i64 div -13
|
||||
check: cast[int64](qr.rem) == 100'i64 mod -13
|
||||
|
||||
test "Divmod(-100, -13) returns the correct result":
|
||||
|
||||
let a = -100.stint(64)
|
||||
let b = -13.stint(64)
|
||||
let qr = divmod(a, b)
|
||||
|
||||
check: cast[int64](qr.quot) == -100'i64 div -13
|
||||
check: cast[int64](qr.rem) == -100'i64 mod -13
|
||||
|
||||
# test "Divmod(2^64, 3) returns the correct result":
|
||||
# let a = 1.stint(128) shl 64
|
||||
# let b = 3.stint(128)
|
||||
|
||||
# let qr = divmod(a, b)
|
||||
|
||||
# let q = cast[UintImpl[uint64]](qr.quot)
|
||||
# let r = cast[UintImpl[uint64]](qr.rem)
|
||||
|
||||
# check: q.lo == 6148914691236517205'u64
|
||||
# check: q.hi == 0'u64
|
||||
# check: r.lo == 1'u64
|
||||
# check: r.hi == 0'u64
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest
|
||||
import ../src/stint, unittest
|
||||
|
||||
suite "Testing addition implementation":
|
||||
suite "Testing unsigned int addition implementation":
|
||||
test "In-place addition gives expected result":
|
||||
|
||||
var a = 20182018.stuint(64)
|
||||
|
@ -51,7 +51,7 @@ suite "Testing addition implementation":
|
|||
z += a
|
||||
check: cast[uint16](z) == 164
|
||||
|
||||
suite "Testing substraction implementation":
|
||||
suite "Testing unsigned int substraction implementation":
|
||||
test "In-place substraction gives expected result":
|
||||
|
||||
var a = 20182018.stuint(64)
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest
|
||||
import ../src/stint, unittest
|
||||
|
||||
suite "Testing bitwise operations":
|
||||
suite "Testing unsigned int bitwise operations":
|
||||
let a = 100'i16.stuint(16)
|
||||
|
||||
let b = a * a
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest
|
||||
import ../src/stint, unittest
|
||||
|
||||
suite "Testing comparison operators":
|
||||
suite "Testing unsigned int comparison operators":
|
||||
let
|
||||
a = 10'i16.stuint(16)
|
||||
b = 15'i16.stuint(16)
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest
|
||||
import ../src/stint, unittest
|
||||
|
||||
suite "Testing byte representation":
|
||||
suite "Testing unsigned int byte representation":
|
||||
test "Byte representation conforms to the platform endianness":
|
||||
let a = 20182018.stuint(64)
|
||||
let b = 20182018'u64
|
|
@ -1,4 +1,4 @@
|
|||
# Mpint
|
||||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
|
@ -7,9 +7,9 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ../src/mpint, unittest
|
||||
import ../src/stint, unittest
|
||||
|
||||
suite "Testing multiplication implementation":
|
||||
suite "Testing unsigned int multiplication implementation":
|
||||
test "Multiplication with result fitting in low half":
|
||||
|
||||
let a = 10000.stuint(64)
|
||||
|
@ -33,7 +33,7 @@ suite "Testing multiplication implementation":
|
|||
check: cast[uint64](a*b*c) == 1_000_000_000_000_000_000_000'u64 # need 70-bits
|
||||
|
||||
|
||||
suite "Testing division and modulo implementation":
|
||||
suite "Testing unsigned int division and modulo implementation":
|
||||
test "Divmod(100, 13) returns the correct result":
|
||||
|
||||
let a = 100.stuint(64)
|
Loading…
Reference in New Issue