Passing addition tests (however simple bitwise ops crash the int128 VM ... during compilation)

This commit is contained in:
Mamy André-Ratsimbazafy 2020-06-12 23:53:08 +02:00 committed by jangko
parent a0dec54c12
commit 7f6c588ce3
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
17 changed files with 829 additions and 815 deletions

View File

@ -7,12 +7,15 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import stint/[bitops2, endians2, intops, io, modular_arithmetic, literals_stint]
export bitops2, endians2, intops, io, modular_arithmetic, literals_stint
# import stint/[bitops2, endians2, intops, io, modular_arithmetic, literals_stint]
# export bitops2, endians2, intops, io, modular_arithmetic, literals_stint
import stint/[io, uintops, bitops2]
export io, uintops, bitops2
type
Int128* = StInt[128]
Int256* = StInt[256]
# Int128* = Stint[128]
# Int256* = Stint[256]
UInt128* = StUint[128]
UInt256* = StUint[256]
@ -22,8 +25,8 @@ func u128*(s: string): UInt128 {.inline.} = s.parse(UInt128)
func u256*(n: SomeInteger): UInt256 {.inline.} = n.stuint(256)
func u256*(s: string): UInt256 {.inline.} = s.parse(UInt256)
func i128*(n: SomeInteger): Int128 {.inline.} = n.stint(128)
func i128*(s: string): Int128 {.inline.} = s.parse(Int128)
# func i128*(n: SomeInteger): Int128 {.inline.} = n.stint(128)
# func i128*(s: string): Int128 {.inline.} = s.parse(Int128)
func i256*(n: SomeInteger): Int256 {.inline.} = n.stint(256)
func i256*(s: string): Int256 {.inline.} = s.parse(Int256)
# func i256*(n: SomeInteger): Int256 {.inline.} = n.stint(256)
# func i256*(s: string): Int256 {.inline.} = s.parse(Int256)

View File

@ -1,16 +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/[bitops2_priv, datatypes]
func countOnes*(x: StUint): int {.inline.} = countOnes(x.data)
func parity*(x: StUint): int {.inline.} = parity(x.data)
func firstOne*(x: StUint): int {.inline.} = firstOne(x.data)
func leadingZeros*(x: StUint): int {.inline.} = leadingZeros(x.data)
func trailingZeros*(x: StUint): int {.inline.} = trailingZeros(x.data)

View File

@ -7,16 +7,21 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import private/[bitops2_priv, endians2_priv, datatypes, compiletime_helpers]
import private/datatypes
import stew/endians2
export endians2
func swapBytes*(x: StUint): StUint {.inline.} = StUint(data: swapBytes(x.data))
{.push raises: [IndexError], noInit, gcsafe.}
func toBytes*[bits: static int](x: StUint[bits], endian: Endianness = system.cpuEndian):
array[bits div 8, byte] {.inline.} =
toBytes(x.data, endian)
when endian == system.cpuEndian:
for i in 0 ..< x.limbs.len:
result[i * sizeof(Word)] = x.limbs[i].toBytes()
else:
for i in 0 ..< x.limbs.len:
result[i * sizeof(Word)] = x.limbs[^i].toBytes()
func toBytesLE*[bits: static int](x: StUint[bits]):
array[bits div 8, byte] {.inline.} =
@ -26,83 +31,138 @@ func toBytesBE*[bits: static int](x: StUint[bits]):
array[bits div 8, byte] {.inline.} =
toBytes(x, bigEndian)
func fromBytes*[bits: static int](
T: typedesc[StUint[bits]],
x: array[bits div 8, byte],
endian: Endianness = system.cpuEndian): T {.inline, noinit.} =
when nimvm:
copyFromArray(result.data, x)
else:
copyMem(addr result, unsafeAddr x[0], bits div 8)
if endian != system.cpuEndian:
result = swapBytes(result)
func fromBytes*[bits: static int](
T: typedesc[StUint[bits]],
x: openArray[byte],
endian: Endianness = system.cpuEndian): T {.inline.} =
# TODO fromBytesBE in io.nim handles this better, merge the two!
var tmp: array[bits div 8, byte]
if x.len < tmp.len:
let offset = if endian == bigEndian: tmp.len - x.len else: 0
for i in 0..<x.len: # Loop since vm can't copymem
tmp[i + offset] = x[i]
else:
for i in 0..<tmp.len: # Loop since vm can't copymem
tmp[i] = x[i]
fromBytes(T, tmp, endian)
func fromBytesBE*[bits: static int](
T: typedesc[StUint[bits]],
x: array[bits div 8, byte]): T {.inline.} =
## Read big endian bytes and convert to an integer. By default, native
## endianess is used which is not
## portable!
fromBytes(T, x, bigEndian)
func fromBytesBE*[bits: static int](
T: typedesc[StUint[bits]],
x: openArray[byte]): T {.inline.} =
x: openArray[byte]): T =
## Read big endian bytes and convert to an integer. At runtime, v must contain
## at least sizeof(T) bytes. By default, native endianess is used which is not
## portable!
fromBytes(T, x, bigEndian)
## at least sizeof(T) bytes. Native endianess is used which is not
## portable! (i.e. use fixed-endian byte array or hex for serialization)
func toBE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline.} =
var accum: Word
var accumBits: int
var dstIdx: int
when cpuEndian == littleEndian: # src is bigEndian, CPU is little-endian
dstIdx = 0
for srcIdx in countdown(x.len-1, 0):
let srcByte = x[srcIdx]
accum = accum or (srcByte shl accumBits)
accumBits += 8
if accumBits >= WordBitWidth:
result.limbs[dstIdx] = accum
inc dstIdx
accumBits -= WordBitWidth
accum = srcByte shr (8 - accumBits)
if dstIdx < result.limbs.len:
result.limbs[dstIdx] = accum
for fillIdx in dstIdx+1 ..< result.limbs.len:
result.limbs[fillIdx] = 0
else: # src and CPU are bigEndian
dstIdx = result.limbs.len-1
for srcIdx in countdown(x.len-1, 0):
let srcByte = x[srcIdx]
accum = accum or (srcByte shl accumBits)
accumBits += 8
if accumBits >= WordBitWidth:
result.limbs[dstIdx] = accum
dec dstIdx
accumBits -= WordBitWidth
accum = srcByte shr (8 - accumBits)
if dstIdx > 0:
result.limbs[dstIdx] = accum
for fillIdx in 0 ..< dstIdx:
result.limbs[fillIdx] = 0
func fromBytesLE*[bits: static int](
T: typedesc[StUint[bits]],
x: openArray[byte]): T =
## Read little endian bytes and convert to an integer. At runtime, v must
## contain at least sizeof(T) bytes. By default, native endianess is used
## which is not portable! (i.e. use fixed-endian byte array or hex for serialization)
var accum: Word
var accumBits: int
var dstIdx: int
when cpuEndian == littleEndian: # src and CPU are little-endian
dstIdx = 0
for srcIdx in 0 ..< x.len:
let srcByte = x[srcIdx]
accum = accum or (srcByte shl accumBits)
accumBits += 8
if accumBits >= WordBitWidth:
result.limbs[dstIdx] = accum
inc dstIdx
accumBits -= WordBitWidth
accum = srcByte shr (8 - accumBits)
if dstIdx < result.limbs.len:
result.limbs[dstIdx] = accum
for fillIdx in dstIdx+1 ..< result.limbs.len:
result.limbs[fillIdx] = 0
else: # src is little endian, CPU is bigEndian
dstIdx = result.limbs.len-1
for srcIdx in 0 ..< x.len:
let srcByte = x[srcIdx]
accum = accum or (srcByte shl accumBits)
accumBits += 8
if accumBits >= WordBitWidth:
result.limbs[dstIdx] = accum
dec dstIdx
accumBits -= WordBitWidth
accum = srcByte shr (8 - accumBits)
if dstIdx > 0:
result.limbs[dstIdx] = accum
for fillIdx in 0 ..< dstIdx:
result.limbs[fillIdx] = 0
func fromBytes*[bits: static int](
T: typedesc[StUint[bits]],
x: openarray[byte],
srcEndian: Endianness = system.cpuEndian): T {.inline.} =
## Read an source bytearray with the specified endianness and
## convert it to an integer
when srcEndian == littleEndian:
result = fromBytesLE(T, x)
else:
result = fromBytesBE(T, x)
# TODO: What is the use-case for all the procs below?
# ------------------------------------------------------------------------------------------
func toBE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline, deprecated: "Use toByteArrayBE instead".} =
## Convert a native endian value to big endian. Consider toBytesBE instead
## which may prevent some confusion.
if cpuEndian == bigEndian: x
else: x.swapBytes
func fromBE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline.} =
func fromBE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline, deprecated: "Use fromBytesBE instead".} =
## Read a big endian value and return the corresponding native endian
# there's no difference between this and toBE, except when reading the code
toBE(x)
func fromBytesLE*[bits: static int](
T: typedesc[StUint[bits]],
x: array[bits div 8, byte]): StUint[bits] {.inline.} =
## Read little endian bytes and convert to an integer. By default, native
## endianess is used which is not portable!
fromBytes(T, x, littleEndian)
func fromBytesLE*[bits: static int](
T: typedesc[StUint[bits]],
x: openArray[byte]): StUint[bits] {.inline.} =
## Read little endian bytes and convert to an integer. At runtime, v must
## contain at least sizeof(T) bytes. By default, native endianess is used
## which is not portable!
fromBytes(T, x, littleEndian)
func toLE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline.} =
func toLE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline, deprecated.} =
## Convert a native endian value to little endian. Consider toBytesLE instead
## which may prevent some confusion.
if cpuEndian == littleEndian: x
else: x.swapBytes
func fromLE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline.} =
func fromLE*[bits: static int](x: StUint[bits]): StUint[bits] {.inline, deprecated: "Use fromBytesLE instead".} =
## Read a little endian value and return the corresponding native endian
# there's no difference between this and toLE, except when reading the code
toLE(x)

View File

@ -9,9 +9,10 @@
import
./private/datatypes,
./private/int_negabs,
./private/compiletime_helpers,
./intops,
# ./private/int_negabs,
# ./private/compiletime_helpers,
# ./intops,
./uintops, ./endians2,
typetraits, algorithm, hashes
template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
@ -24,178 +25,144 @@ template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
"\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 assignLo(result: var (UintImpl | IntImpl), n: SomeInteger) {.inline.} =
when result.lo is UintImpl:
assignLo(result.lo, n)
else:
result.lo = (type result.lo)(n)
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
## Converts an integer to an arbitrary precision integer.
doAssert n >= 0.T
when result.data is UintImpl:
static_check_size(T, bits)
assignLo(result.data, n)
when cpuEndian == littleEndian:
result.limbs[0] = Word(n)
when sizeof(n) > sizeof(Word):
result.limbs[1] = Word(n) shr WordBitWidth
else:
result.data = (type result.data)(n)
func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
## Converts an integer to an arbitrary precision signed integer.
when result.data is IntImpl:
static_check_size(T, bits)
when T is SomeSignedInt:
if n < 0:
# TODO: when bits >= 128, cannot create from
# low(int8-64)
# see: status-im/nim-stint/issues/92
assignLo(result.data, -n)
result = -result
else:
assignLo(result.data, n)
else:
assignLo(result.data, n)
else:
result.data = (type result.data)(n)
result.limbs[^1] = Word(n)
when sizeof(n) > sizeof(Word):
result.limbs[^2] = Word(n) shr WordBitWidth
<<<<<<< HEAD
func to*(x: SomeInteger, T: typedesc[StInt]): T =
stint(x, result.bits)
func to*(x: SomeUnsignedInt, T: typedesc[StUint]): T =
stuint(x, result.bits)
=======
# func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
# ## Converts an integer to an arbitrary precision signed integer.
#
# when result.data is IntImpl:
# static_check_size(T, bits)
# when T is SomeSignedInt:
# if n < 0:
# # TODO: when bits >= 128, cannot create from
# # low(int8-64)
# # see: status-im/nim-stint/issues/92
# assignLo(result.data, -n)
# result = -result
# else:
# assignLo(result.data, n)
# else:
# assignLo(result.data, n)
# else:
# result.data = (type result.data)(n)
# func to*(a: SomeInteger, T: typedesc[Stint]): T =
# stint(a, result.bits)
func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T =
stuint(a, result.bits)
func truncate*(num: StInt or StUint, T: typedesc[SomeInteger]): T {.inline.}=
## Extract the int, uint, int8-int64 or uint8-uint64 portion of a multi-precision integer.
## Note that int and uint are 32-bit on 32-bit platform.
## For unsigned result type, result is modulo 2^(sizeof T in bit)
## For signed result type, result is undefined if input does not fit in the target type.
static:
doAssert bitsof(T) <= bitsof(num.data.leastSignificantWord)
when nimvm:
let data = num.data.leastSignificantWord
vmIntCast[T](data)
else:
cast[T](num.data.leastSignificantWord)
result = T(num.leastSignificantWord())
func toInt*(num: StInt or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}=
num.truncate(int)
func bigToSmall(result: var (UintImpl | IntImpl), x: auto) {.inline.} =
when bitsof(x) == bitsof(result):
when type(result) is type(x):
result = x
else:
result = convert[type(result)](x)
else:
bigToSmall(result, x.lo)
func smallToBig(result: var (UintImpl | IntImpl), x: auto) {.inline.} =
when bitsof(x) == bitsof(result):
when type(result) is type(x):
result = x
else:
result = convert[type(result)](x)
else:
smallToBig(result.lo, x)
func stuint*(x: StUint, bits: static[int]): StUint[bits] {.inline.} =
func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
## unsigned int to unsigned int conversion
## smaller to bigger bits conversion will have the same value
## bigger to smaller bits conversion, the result is truncated
const N = bitsof(x.data)
when N < bits:
when N <= 64:
result = stuint(x.data, bits)
else:
smallToBig(result.data, x.data)
elif N > bits:
when bits <= 64:
result = stuint(x.truncate(type(result.data)), bits)
else:
bigToSmall(result.data, x.data)
else:
result = x
for wr, wa in leastToMostSig(result, a):
wr = wa
func stuint*(x: StInt, bits: static[int]): StUint[bits] {.inline.} =
## signed int to unsigned int conversion
## current behavior is cast-like, copying bit pattern
## or truncating if input does not fit into destination
const N = bitsof(x.data)
when N < bits:
when N <= 64:
type T = StUint[N]
result = stuint(convert[T](x).data, bits)
else:
smallToBig(result.data, x.data)
elif N > bits:
when bits <= 64:
result = stuint(x.truncate(type(result.data)), bits)
else:
bigToSmall(result.data, x.data)
else:
result = convert[type(result)](x)
# func stuint*(a: StInt, bits: static[int]): StUint[bits] {.inline.} =
# ## signed int to unsigned int conversion
# ## current behavior is cast-like, copying bit pattern
# ## or truncating if input does not fit into destination
# const N = bitsof(x.data)
# when N < bits:
# when N <= 64:
# type T = StUint[N]
# result = stuint(convert[T](a).data, bits)
# else:
# smallToBig(result.data, a.data)
# elif N > bits:
# when bits <= 64:
# result = stuint(x.truncate(type(result.data)), bits)
# else:
# bigToSmall(result.data, a.data)
# else:
# result = convert[type(result)](a)
func stint*(x: StInt, bits: static[int]): StInt[bits] {.inline.} =
## signed int to signed int conversion
## will raise exception if input does not fit into destination
const N = bitsof(x.data)
when N < bits:
when N <= 64:
result = stint(x.data, bits)
else:
if x.isNegative:
smallToBig(result.data, (-x).data)
result = -result
else:
smallToBig(result.data, x.data)
elif N > bits:
template checkNegativeRange() =
# due to bug #92, we skip negative range check
when false:
const dmin = stint((type result).low, N)
if x < dmin: raise newException(ValueError, "value out of range")
# func stint*(a: StInt, bits: static[int]): StInt[bits] {.inline.} =
# ## signed int to signed int conversion
# ## will raise exception if input does not fit into destination
# const N = bitsof(a.data)
# when N < bits:
# when N <= 64:
# result = stint(a.data, bits)
# else:
# if a.isNegative:
# smallToBig(result.data, (-a).data)
# result = -result
# else:
# smallToBig(result.data, a.data)
# elif N > bits:
# template checkNegativeRange() =
# # due to bug #92, we skip negative range check
# when false:
# const dmin = stint((type result).low, N)
# if a < dmin: raise newException(RangeError, "value out of range")
template checkPositiveRange() =
const dmax = stint((type result).high, N)
if x > dmax: raise newException(ValueError, "value out of range")
# template checkPositiveRange() =
# const dmax = stint((type result).high, N)
# if a > dmax: raise newException(RangeError, "value out of range")
when bits <= 64:
if x.isNegative:
checkNegativeRange()
result = stint((-x).truncate(type(result.data)), bits)
result = -result
else:
checkPositiveRange()
result = stint(x.truncate(type(result.data)), bits)
else:
if x.isNegative:
checkNegativeRange()
bigToSmall(result.data, (-x).data)
result = -result
else:
checkPositiveRange()
bigToSmall(result.data, x.data)
else:
result = x
# when bits <= 64:
# if a.isNegative:
# checkNegativeRange()
# result = stint((-a).truncate(type(result.data)), bits)
# result = -result
# else:
# checkPositiveRange()
# result = stint(a.truncate(type(result.data)), bits)
# else:
# if a.isNegative:
# checkNegativeRange()
# bigToSmall(result.data, (-a).data)
# result = -result
# else:
# checkPositiveRange()
# bigToSmall(result.data, a.data)
# else:
# result = a
func stint*(x: StUint, bits: static[int]): StInt[bits] {.inline.} =
const N = bitsof(x.data)
const dmax = stuint((type result).high, N)
if x > dmax: raise newException(ValueError, "value out of range")
when N < bits:
when N <= 64:
result = stint(x.data, bits)
else:
smallToBig(result.data, x.data)
elif N > bits:
when bits <= 64:
result = stint(x.truncate(type(result.data)), bits)
else:
bigToSmall(result.data, x.data)
else:
result = convert[type(result)](x)
# func stint*(a: StUint, bits: static[int]): StInt[bits] {.inline.} =
# const N = bitsof(a.data)
# const dmax = stuint((type result).high, N)
# if a > dmax: raise newException(RangeError, "value out of range")
# when N < bits:
# when N <= 64:
# result = stint(a.data, bits)
# else:
# smallToBig(result.data, a.data)
# elif N > bits:
# when bits <= 64:
# result = stint(a.truncate(type(result.data)), bits)
# else:
# bigToSmall(result.data, a.data)
# else:
# result = convert[type(result)](a)
func readHexChar(c: char): int8 {.inline.}=
## Converts an hex char to an int
@ -270,43 +237,43 @@ func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix:
result = result * base + input[curr].readHexChar.stuint(bits)
nextNonBlank(curr, input)
func parse*[bits: static[int]](input: string, T: typedesc[StInt[bits]], radix: static[int8] = 10): T =
## Parse a string and store the result in a StInt[bits] or StUint[bits].
# func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]], radix: static[int8] = 10): T =
# ## Parse a string and store the result in a Stint[bits] or Stuint[bits].
static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# TODO: we can special case hex result/input as an array of bytes
# and be much faster
# # 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)
const base = radix.int8.stuint(bits)
# # For conversion we require overflowing operations (for example for negative hex numbers)
# const base = radix.int8.stuint(bits)
var
curr = 0 # Current index in the string
isNeg = false
no_overflow: StUint[bits]
# var
# curr = 0 # Current index in the string
# isNeg = false
# no_overflow: Stuint[bits]
if input[curr] == '-':
doAssert radix == 10, "Negative numbers are only supported with base 10 input."
isNeg = true
inc curr
else:
skipPrefixes(curr, input, radix)
# if input[curr] == '-':
# doAssert radix == 10, "Negative numbers are only supported with base 10 input."
# isNeg = true
# inc curr
# else:
# skipPrefixes(curr, input, radix)
while curr < input.len:
# TODO: overflow detection
when radix <= 10:
no_overflow = no_overflow * base + input[curr].readDecChar.stuint(bits)
else:
no_overflow = no_overflow * base + input[curr].readHexChar.stuint(bits)
nextNonBlank(curr, input)
# while curr < input.len:
# # TODO: overflow detection
# when radix <= 10:
# no_overflow = no_overflow * base + input[curr].readDecChar.stuint(bits)
# else:
# no_overflow = no_overflow * base + input[curr].readHexChar.stuint(bits)
# nextNonBlank(curr, input)
# TODO: we can't create the lowest int this way
if isNeg:
result = -convert[T](no_overflow)
else:
result = convert[T](no_overflow)
# # TODO: we can't create the lowest int this way
# if isNeg:
# result = -convert[T](no_overflow)
# else:
# result = convert[T](no_overflow)
func fromHex*(T: typedesc[StUint|StInt], s: string): T {.inline.} =
## Convert an hex string to the corresponding unsigned integer
@ -316,119 +283,121 @@ func hexToUint*[bits: static[int]](hexString: string): StUint[bits] {.inline.} =
## Convert an hex string to the corresponding unsigned integer
parse(hexString, type result, radix = 16)
func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10): string =
## Convert a StInt or StUint to string.
## In case of negative numbers:
## - they are prefixed with "-" for base 10.
## - if not base 10, they are returned raw in two-complement form.
# func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10): string =
# ## Convert a Stint or Stuint to string.
# ## In case of negative numbers:
# ## - they are prefixed with "-" for base 10.
# ## - if not base 10, they are returned raw in two-complement form.
static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
const hexChars = "0123456789abcdef"
const base = radix.uint8.stuint(bits)
# const hexChars = "0123456789abcdef"
# const base = radix.uint8.stuint(bits)
result = ""
var (q, r) = divmod(num, base)
# result = ""
# var (q, r) = divmod(num, base)
while true:
when bitsof(r.data) <= 64:
result.add hexChars[r.data.int]
else:
result.add hexChars[r.truncate(int)]
if q.isZero:
break
(q, r) = divmod(q, base)
# while true:
# when bitsof(r.data) <= 64:
# result.add hexChars[r.data.int]
# else:
# result.add hexChars[r.truncate(int)]
# if q.isZero:
# break
# (q, r) = divmod(q, base)
reverse(result)
# reverse(result)
func toString*[bits: static[int]](num: StInt[bits], radix: static[int8] = 10): string =
## Convert a StInt or StUint to string.
## In case of negative numbers:
## - they are prefixed with "-" for base 10.
## - if not base 10, they are returned raw in two-complement form.
# func toString*[bits: static[int]](num: Stint[bits], radix: static[int8] = 10): string =
# ## Convert a Stint or Stuint to string.
# ## In case of negative numbers:
# ## - they are prefixed with "-" for base 10.
# ## - if not base 10, they are returned raw in two-complement form.
static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
const hexChars = "0123456789abcdef"
const base = radix.int8.stuint(bits)
# const hexChars = "0123456789abcdef"
# const base = radix.int8.stuint(bits)
result = ""
# result = ""
type T = StUint[bits]
let isNeg = num.isNegative
let num = convert[T](if radix == 10 and isNeg: -num
else: num)
# type T = Stuint[bits]
# let isNeg = num.isNegative
# let num = convert[T](if radix == 10 and isNeg: -num
# else: num)
var (q, r) = divmod(num, base)
# var (q, r) = divmod(num, base)
while true:
when bitsof(r.data) <= 64:
result.add hexChars[r.data.int]
else:
result.add hexChars[r.truncate(int)]
if q.isZero:
break
(q, r) = divmod(q, base)
# while true:
# when bitsof(r.data) <= 64:
# result.add hexChars[r.data.int]
# else:
# result.add hexChars[r.truncate(int)]
# if q.isZero:
# break
# (q, r) = divmod(q, base)
if isNeg and radix == 10:
result.add '-'
# if isNeg and radix == 10:
# result.add '-'
reverse(result)
# reverse(result)
func `$`*(num: StInt or StUint): string {.inline.}=
when num.data is SomeInteger:
$num.data
else:
toString(num, 10)
# func `$`*(num: Stint or StUint): string {.inline.}=
# when num.data is SomeInteger:
# $num.data
# else:
# toString(num, 10)
func toHex*[bits: static[int]](num: StInt[bits] or StUint[bits]): string {.inline.}=
## Convert to a hex string.
## Output is considered a big-endian base 16 string.
## Leading zeros are stripped. Use dumpHex instead if you need the in-memory representation
toString(num, 16)
# func toHex*[bits: static[int]](num: Stint[bits] or StUint[bits]): string {.inline.}=
# ## Convert to a hex string.
# ## Output is considered a big-endian base 16 string.
# ## Leading zeros are stripped. Use dumpHex instead if you need the in-memory representation
# toString(num, 16)
func dumpHex*(x: StInt or StUint, order: static[Endianness] = bigEndian): string =
## Stringify an int to hex.
## Note. Leading zeros are not removed. Use toString(n, base = 16)/toHex 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
# func dumpHex*(x: Stint or StUint, order: static[Endianness] = bigEndian): string =
# ## Stringify an int to hex.
# ## Note. Leading zeros are not removed. Use toString(n, base = 16)/toHex 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 = bitsof(x.data) div 8
# const
# hexChars = "0123456789abcdef"
# size = bitsof(x.data) div 8
result = newString(2*size)
# result = newString(2*size)
when nimvm:
for i in 0 ..< size:
when order == system.cpuEndian:
let byte = x.data.getByte(i)
else:
let byte = x.data.getByte(size - 1 - i)
result[2*i] = hexChars[int byte shr 4 and 0xF]
result[2*i+1] = hexChars[int byte and 0xF]
else:
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let bytes {.restrict.}= cast[ptr array[size, byte]](x.unsafeAddr)
# when nimvm:
# for i in 0 ..< size:
# when order == system.cpuEndian:
# let byte = x.data.getByte(i)
# else:
# let byte = x.data.getByte(size - 1 - i)
# result[2*i] = hexChars[int byte shr 4 and 0xF]
# result[2*i+1] = hexChars[int byte and 0xF]
# else:
# {.pragma: restrict, codegenDecl: "$# __restrict $#".}
# let bytes {.restrict.}= cast[ptr array[size, byte]](x.unsafeaddr)
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]
# 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]
proc initFromBytesBE*[bits: static[int]](val: var StUint[bits], ba: openArray[byte], allowPadding: static[bool] = true) =
proc initFromBytesBE*[bits: static[int]](val: var Stuint[bits],
ba: openarray[byte],
allowPadding: static[bool] = true) {.deprecated:"Use fromBytesBE instead".}=
## Initializes a UInt[bits] value from a byte buffer storing a big-endian
## representation of a number.
##
@ -484,48 +453,35 @@ func significantBytesBE*(val: openArray[byte]): int {.deprecated.}=
return val.len - i
return 1
func fromBytesBE*(T: type StUint, ba: openArray[byte],
allowPadding: static[bool] = true): T =
func fromBytesBE*(T: type Stuint, ba: openarray[byte],
allowPadding: static[bool] = true): T {.noInit, inline.} =
## This function provides a convenience wrapper around `initFromBytesBE`.
result.initFromBytesBE(ba, allowPadding)
when not allowPadding:
{.deprecated: "fromBytesBE without padding is deprecated".}
result.initFromBytesBE(ba, allowPadding)
else:
result = endians2.fromBytesBE(T, ba)
func readUintBE*[bits: static[int]](ba: openArray[byte]): StUint[bits] =
func readUintBE*[bits: static[int]](ba: openarray[byte]): Stuint[bits] {.noInit, inline.}=
## Convert a big-endian array of (bits div 8) Bytes to an UInt[bits] (in native host endianness)
## Input:
## - a big-endian openArray of size (bits div 8) at least
## Returns:
## - A unsigned integer of the same size with `bits` bits
##
## ⚠ If the openArray length is bigger than bits div 8, part converted is undefined behaviour.
result.initFromBytesBE(ba, false)
## ⚠ If the openarray length is bigger than bits div 8, part converted is undefined behaviour.
result = endians2.fromBytesBE(Stuint[bits], ba)
func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte] =
func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte] {.noInit, inline.}=
## Convert a uint[bits] to to a big-endian array of bits div 8 bytes
## Input:
## - an unsigned integer
## Returns:
## - a big-endian array of the same size
const N = bits div 8
when nimvm:
for i in 0 ..< N:
when system.cpuEndian == bigEndian:
result[i] = n.data.getByte(i)
else:
result[i] = n.data.getByte(N - 1 - i)
else:
when system.cpuEndian == bigEndian:
result = cast[type result](n)
else:
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
let n_ptr {.restrict.} = cast[ptr array[N, byte]](n.unsafeAddr)
for i in 0 ..< N:
result[N-1 - i] = n_ptr[i]
result = n.toBytes(bigEndian)
template hash*(num: StUint|StInt): Hash =
# TODO:
# `hashData` is not particularly efficient.
# Explore better hashing solutions in nim-stew.
hashData(unsafeAddr num, sizeof num)

View File

@ -1,58 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./conversion, stew/bitops2
export bitops2
# Bitops from support library
template bitsof*(x: UintImpl): int =
# XXX: https://github.com/nim-lang/Nim/issues/9494
mixin bitsof
bitsof(x.lo) * 2
template bitsof*(x: IntImpl): int =
# XXX: https://github.com/nim-lang/Nim/issues/9494
mixin bitsof
bitsof(x.lo) * 2
template bitsof*(x: typedesc[UintImpl]): int =
# XXX: https://github.com/nim-lang/Nim/issues/9494
mixin bitsof
bitsof(x.lo) * 2
func countOnes*(x: UintImpl): int {.inline.} =
countOnes(x.lo) + countOnes(x.hi)
func countZeros*(x: UintImpl): int {.inline.} =
countZeros(x.lo) + countOnes(x.hi)
func parity*(x: UintImpl): int {.inline.} =
parity(x.lo) xor parity(x.hi)
func leadingZeros*(x: UintImpl): int {.inline.} =
let tmp = x.hi.leadingZeros()
if tmp == bitsof(x.hi):
x.lo.leadingZeros() + bitsof(x.hi)
else:
tmp
func trailingZeros*(x: UintImpl): int {.inline.} =
let tmp = x.lo.trailingZeros()
if tmp == bitsof(x.lo):
tmp + x.hi.trailingZeros()
else:
tmp
func firstOne*(x: UintImpl): int {.inline.} =
let tmp = trailingZeros(x)
if tmp == bitsof(x):
0
else:
1 + tmp

View File

@ -1,82 +0,0 @@
import
./datatypes, ./uint_bitwise_ops, ./bitops2_priv, ./int_bitwise_ops,
./compiletime_cast
export compiletime_cast
func getByte*(x: SomeInteger, pos: int): byte {.compileTime.} =
type DT = type x
when bitsof(DT) == 8:
cast[byte](x)
else:
byte((x shr (pos * 8)) and 0xFF.DT)
func getByte*(x: UintImpl | IntImpl, pos: int): byte {.compileTime.} =
type DT = type x.leastSignificantWord
when bitsof(DT) == 8:
cast[byte](x.leastSignificantWord)
else:
byte((x shr (pos * 8)).leastSignificantWord and 0xFF.DT)
proc setByte*(x: var SomeInteger, pos: int, b: byte) {.compileTime.} =
type DT = type x
x = x or (DT(b) shl (pos*8))
type SomeIntImpl = UintImpl | IntImpl
func setByte*(x: var SomeIntImpl, pos: int, b: byte) {.compileTime.} =
proc putFirstByte(x: var SomeInteger, b: byte) =
type DT = type x
x = x or b.DT
proc putFirstByte(x: var UintImpl, b: byte) =
putFirstByte(x.lo, b)
var cx: type x
cx.putFirstByte(b)
x = x or (cx shl (pos*8))
func copyToArray*(ret: var openArray[byte], x: UintImpl) {.compileTime.} =
const size = bitsof(x) div 8
doAssert ret.len >= size
for i in 0 ..< size:
ret[i] = x.getByte(i)
func copyFromArray*(x: var UintImpl, data: openArray[byte]) {.compileTime.} =
const size = bitsof(x) div 8
doAssert data.len >= size
for i in 0 ..< size:
x.setByte(i, data[i])
func copyFromArray*(x: var SomeInteger, data: openArray[byte]) {.compileTime.} =
const size = bitsof(x) div 8
doAssert data.len >= size
for i in 0 ..< size:
x.setByte(i, data[i])
template vmIntCast*[T](data: SomeInteger): T =
type DT = type data
const
bits = bitsof(T)
DTbits = bitsof(DT)
# we use esoteric type juggling here to trick the Nim VM
when bits == 64:
when DTbits == 64:
cast[T](data)
else:
cast[T](uint64(data and DT(0xFFFFFFFF_FFFFFFFF)))
elif bits == 32:
when DTbits == 32:
cast[T](data)
else:
cast[T](uint32(data and DT(0xFFFFFFFF)))
elif bits == 16:
when DTbits == 16:
cast[T](data)
else:
cast[T](uint16(data and DT(0xFFFF)))
else:
when DTBits == 8:
cast[T](data)
else:
cast[T](uint8(data and DT(0xFF)))

View File

@ -39,6 +39,8 @@ type
Carry* = uint8 # distinct range[0'u8 .. 1]
Borrow* = uint8 # distinct range[0'u8 .. 1]
SomeBigInteger*[bits: static[int]] = Stuint[bits]|Stint[bits]
const GCC_Compatible* = defined(gcc) or defined(clang) or defined(llvm_gcc)
const X86* = defined(amd64) or defined(i386)
@ -46,65 +48,80 @@ when sizeof(int) == 8 and GCC_Compatible:
type
uint128*{.importc: "unsigned __int128".} = object
# Accessors
# --------------------------------------------------------
template leastSignificantWord*(num: SomeInteger): auto =
num
func leastSignificantWord*(limbs: Limbs): auto {.inline.} =
func leastSignificantWord*(a: SomeBigInteger): auto {.inline.} =
when cpuEndian == littleEndian:
limbs[0]
a.limbs[0]
else:
limbs[^1]
a.limbs[^1]
func mostSignificantWord*(limbs: Limbs): auto {.inline.} =
func mostSignificantWord*(a: SomeBigInteger): auto {.inline.} =
when cpuEndian == littleEndian:
limbs[^1]
a.limbs[^1]
else:
limbs[0]
a.limbs[0]
iterator leastToMostSig*(limbs: Limbs): Word =
# Iterations
# --------------------------------------------------------
iterator leastToMostSig*(a: SomeBigInteger): Word =
## Iterate from least to most significant word
when cpuEndian == littleEndian:
for i in 0 ..< limbs.len:
yield limbs[i]
for i in 0 ..< a.limbs.len:
yield a.limbs[i]
else:
for i in countdown(limbs.len-1, 0):
yield limbs[i]
for i in countdown(a.limbs.len-1, 0):
yield a.limbs[i]
iterator leastToMostSig*(limbs: var Limbs): var Word =
iterator leastToMostSig*(a: var SomeBigInteger): var Word =
## Iterate from least to most significant word
when cpuEndian == littleEndian:
for i in 0 ..< limbs.len:
yield limbs[i]
for i in 0 ..< a.limbs.len:
yield a.limbs[i]
else:
for i in countdown(limbs.len-1, 0):
yield limbs[i]
for i in countdown(a.limbs.len-1, 0):
yield a.limbs[i]
iterator leastToMostSig*(aLimbs, bLimbs: Limbs): (Word, Word) =
iterator leastToMostSig*(a, b: SomeBigInteger): (Word, Word) =
## Iterate from least to most significant word
when cpuEndian == littleEndian:
for i in 0 ..< aLimbs.len:
yield (aLimbs[i], bLimbs[i])
for i in 0 ..< a.limbs.len:
yield (a.limbs[i], b.limbs[i])
else:
for i in countdown(aLimbs.len-1, 0):
yield (aLimbs[i], bLimbs[i])
for i in countdown(a.limbs.len-1, 0):
yield (a.limbs[i], b.limbs[i])
iterator leastToMostSig*(aLimbs: var Limbs, bLimbs: Limbs): (var Word, Word) =
iterator leastToMostSig*[aBits, bBits](a: var SomeBigInteger[aBits], b: SomeBigInteger[bBits]): (var Word, Word) =
## Iterate from least to most significant word
when cpuEndian == littleEndian:
for i in 0 ..< aLimbs.len:
yield (aLimbs[i], bLimbs[i])
for i in 0 ..< min(a.limbs.len, b.limbs.len):
yield (a.limbs[i], b.limbs[i])
else:
for i in countdown(aLimbs.len-1, 0):
yield (aLimbs[i], bLimbs[i])
for i in countdown(min(aLimbs.len, b.limbs.len)-1, 0):
yield (a.limbs[i], b.limbs[i])
iterator leastToMostSig*(cLimbs: var Limbs, aLimbs: Limbs, bLimbs: Limbs): (var Word, Word, Word) =
iterator leastToMostSig*(c: var SomeBigInteger, a, b: SomeBigInteger): (var Word, Word, Word) =
## Iterate from least to most significant word
when cpuEndian == littleEndian:
for i in 0 ..< aLimbs.len:
yield (cLimbs[i], aLimbs[i], bLimbs[i])
for i in 0 ..< a.limbs.len:
yield (c.limbs[i], a.limbs[i], b.limbs[i])
else:
for i in countdown(aLimbs.len-1, 0):
yield (cLimbs[i], aLimbs[i], bLimbs[i])
for i in countdown(a.limbs.len-1, 0):
yield (c.limbs[i], a.limbs[i], b.limbs[i])
iterator mostToLeastSig*(a: SomeBigInteger): Word =
## Iterate from most to least significant word
when cpuEndian == bigEndian:
for i in 0 ..< a.limbs.len:
yield a.limbs[i]
else:
for i in countdown(a.limbs.len-1, 0):
yield a.limbs[i]
import std/macros

View File

@ -1,26 +0,0 @@
import ./bitops2_priv, ./datatypes, ./compiletime_helpers
import stew/endians2
export endians2
func swapBytes*(x: UintImpl): UintImpl {.inline.} =
let lo = swapBytes(x.hi)
let hi = swapBytes(x.lo)
UintImpl(hi: hi, lo: lo)
func toBytes*(x: UintImpl, endian: Endianness = system.cpuEndian): auto {.inline.} =
# TODO can't use bitsof in return type (compiler bug?), hence return auto
var ret: array[bitsof(x) div 8, byte]
when nimvm:
if endian == system.cpuEndian:
copyToArray(ret, x)
else:
let v = swapBytes(x)
copyToArray(ret, v)
else:
if endian == system.cpuEndian:
copyMem(addr ret[0], unsafeAddr x, ret.len)
else:
let v = swapBytes(x)
copyMem(addr ret[0], unsafeAddr v, ret.len)
ret

View File

@ -1,19 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes
func zero*(T: typedesc): T {.inline.} =
discard
func one*(T: typedesc[SomeInteger]): T {.inline.} =
1
func one*(T: typedesc[UintImpl or IntImpl]): T {.inline.} =
result.lo = one(type result.lo)

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../datatypes
import ../datatypes, ./compiletime_fallback
# ############################################################
#
@ -105,65 +105,82 @@ when X86:
func addC*(cOut: var Carry, sum: var uint32, a, b: uint32, cIn: Carry) {.inline.} =
## Addition with carry
## (CarryOut, Sum) <- a + b + CarryIn
when X86:
cOut = addcarry_u32(cIn, a, b, sum)
else:
when nimvm:
let dblPrec = uint64(cIn) + uint64(a) + uint64(b)
sum = (uint32)(dblPrec)
cOut = Carry(dblPrec shr 32)
else:
when X86:
cOut = addcarry_u32(cIn, a, b, sum)
else:
let dblPrec = uint64(cIn) + uint64(a) + uint64(b)
sum = (uint32)(dblPrec)
cOut = Carry(dblPrec shr 32)
func subB*(bOut: var Borrow, diff: var uint32, a, b: uint32, bIn: Borrow) {.inline.} =
## Substraction with borrow
## (BorrowOut, Diff) <- a - b - borrowIn
when X86:
bOut = subborrow_u32(bIn, a, b, diff)
else:
when nimvm:
let dblPrec = uint64(a) - uint64(b) - uint64(bIn)
diff = (uint32)(dblPrec)
# On borrow the high word will be 0b1111...1111 and needs to be masked
bOut = Borrow((dblPrec shr 32) and 1)
else:
when X86:
bOut = subborrow_u32(bIn, a, b, diff)
else:
let dblPrec = uint64(a) - uint64(b) - uint64(bIn)
diff = (uint32)(dblPrec)
# On borrow the high word will be 0b1111...1111 and needs to be masked
bOut = Borrow((dblPrec shr 32) and 1)
func addC*(cOut: var Carry, sum: var uint64, a, b: uint64, cIn: Carry) {.inline.} =
## Addition with carry
## (CarryOut, Sum) <- a + b + CarryIn
when X86:
cOut = addcarry_u64(cIn, a, b, sum)
when nimvm:
addC_nim(cOut, sum, a, b, cIn)
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
when X86:
cOut = addcarry_u64(cIn, a, b, sum)
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," + (unsigned __int128)", b, " + (unsigned __int128)",cIn,";"].}
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," + (unsigned __int128)", b, " + (unsigned __int128)",cIn,";"].}
# Don't forget to dereference the var param in C mode
when defined(cpp):
{.emit:[cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:[sum, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:["*",sum, " = (NU64)", dblPrec,";"].}
# Don't forget to dereference the var param in C mode
when defined(cpp):
{.emit:[cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:[sum, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",cOut, " = (NU64)(", dblPrec," >> ", 64'u64, ");"].}
{.emit:["*",sum, " = (NU64)", dblPrec,";"].}
func subB*(bOut: var Borrow, diff: var uint64, a, b: uint64, bIn: Borrow) {.inline.} =
## Substraction with borrow
## (BorrowOut, Diff) <- a - b - borrowIn
when X86:
bOut = subborrow_u64(bIn, a, b, diff)
when nimvm:
subB_nim(bOut, diff, a, b, bIn)
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
when X86:
bOut = subborrow_u64(bIn, a, b, diff)
else:
block:
static:
doAssert GCC_Compatible
doAssert sizeof(int) == 8
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," - (unsigned __int128)", b, " - (unsigned __int128)",bIn,";"].}
var dblPrec {.noInit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", a," - (unsigned __int128)", b, " - (unsigned __int128)",bIn,";"].}
# Don't forget to dereference the var param in C mode
# On borrow the high word will be 0b1111...1111 and needs to be masked
when defined(cpp):
{.emit:[bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:[diff, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:["*",diff, " = (NU64)", dblPrec,";"].}
# Don't forget to dereference the var param in C mode
# On borrow the high word will be 0b1111...1111 and needs to be masked
when defined(cpp):
{.emit:[bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:[diff, " = (NU64)", dblPrec,";"].}
else:
{.emit:["*",bOut, " = (NU64)(", dblPrec," >> ", 64'u64, ") & 1;"].}
{.emit:["*",diff, " = (NU64)", dblPrec,";"].}

View File

@ -0,0 +1,108 @@
# 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
# ############################################################
#
# VM fallback
#
# ############################################################
const
HalfWidth = WordBitWidth shr 1
HalfBase = (Word(1) shl HalfWidth)
HalfMask = HalfBase - 1
func hi(n: Word): Word =
result = n shr HalfWidth
func lo(n: Word): Word =
result = n and HalfMask
func split(n: Word): tuple[hi, lo: Word] =
result.hi = n.hi
result.lo = n.lo
func merge(hi, lo: Word): Word =
(hi shl HalfWidth) or lo
func addC_nim*(cOut: var Carry, sum: var Word, a, b: Word, cIn: Carry) =
# Add with carry, fallback for the Compile-Time VM
# (CarryOut, Sum) <- a + b + CarryIn
let (aHi, aLo) = split(a)
let (bHi, bLo) = split(b)
let tLo = aLo + bLo + cIn
let (cLo, rLo) = split(tLo)
let tHi = aHi + bHi + cLo
let (cHi, rHi) = split(tHi)
cOut = Carry(cHi)
sum = merge(rHi, rLo)
func subB_nim*(bOut: var Borrow, diff: var Word, a, b: Word, bIn: Borrow) =
# Substract with borrow, fallback for the Compile-Time VM
# (BorrowOut, Sum) <- a - b - BorrowIn
let (aHi, aLo) = split(a)
let (bHi, bLo) = split(b)
let tLo = HalfBase + aLo - bLo - bIn
let (noBorrowLo, rLo) = split(tLo)
let tHi = HalfBase + aHi - bHi - Word(noBorrowLo == 0)
let (noBorrowHi, rHi) = split(tHi)
bOut = Borrow(noBorrowHi == 0)
diff = merge(rHi, rLo)
func mul_nim*(hi, lo: var Word, u, v: Word) =
## Extended precision multiplication
## (hi, lo) <- u * v
var x0, x1, x2, x3: Word
let
(uh, ul) = u.split()
(vh, vl) = v.split()
x0 = ul * vl
x1 = ul * vh
x2 = uh * vl
x3 = uh * vh
x1 += hi(x0) # This can't carry
x1 += x2 # but this can
if x1 < x2: # if carry, add it to x3
x3 += HalfBase
hi = x3 + hi(x1)
lo = merge(x1, lo(x0))
func muladd1_nim*(hi, lo: var Word, a, b, c: Word) {.inline.} =
## Extended precision multiplication + addition
## (hi, lo) <- a*b + c
##
## Note: 0xFFFFFFFF_FFFFFFFF² -> (hi: 0xFFFFFFFFFFFFFFFE, lo: 0x0000000000000001)
## so adding any c cannot overflow
var carry: Carry
mul_nim(hi, lo, a, b)
addC_nim(carry, lo, lo, c, 0)
addC_nim(carry, hi, hi, 0, carry)
func muladd2_nim*(hi, lo: var Word, a, b, c1, c2: Word) {.inline.}=
## Extended precision multiplication + addition + addition
## (hi, lo) <- a*b + c1 + c2
##
## Note: 0xFFFFFFFF_FFFFFFFF² -> (hi: 0xFFFFFFFFFFFFFFFE, lo: 0x0000000000000001)
## so adding 0xFFFFFFFFFFFFFFFF leads to (hi: 0xFFFFFFFFFFFFFFFF, lo: 0x0000000000000000)
## and we have enough space to add again 0xFFFFFFFFFFFFFFFF without overflowing
var carry1, carry2: Carry
mul_nim(hi, lo, a, b)
# Carry chain 1
addC_nim(carry1, lo, lo, c1, 0)
addC_nim(carry1, hi, hi, 0, carry1)
# Carry chain 2
addC_nim(carry2, lo, lo, c2, 0)
addC_nim(carry2, hi, hi, 0, carry2)

View File

@ -1,46 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
./datatypes,
./primitives/addcarry_subborrow
# ############ Addition & Substraction ############ #
{.push raises: [], inline, noInit, gcsafe.}
func `+`*(x, y: Limbs): Limbs =
# Addition for multi-precision unsigned int
var carry = Carry(0)
for wr, wx, wy in leastToMostSig(result, x, y):
addC(carry, wr, wx, wy, carry)
func `+=`*(x: var Limbs, y: Limbs) =
## In-place addition for multi-precision unsigned int
var carry = Carry(0)
for wx, wy in leastToMostSig(x, y):
addC(carry, wx, wx, wy, carry)
func `-`*(x, y: Limbs): Limbs =
# Substraction for multi-precision unsigned int
var borrow = Borrow(0)
for wr, wx, wy in leastToMostSig(result, x, y):
subB(borrow, wr, wx, wy, borrow)
func `-=`*(x: var Limbs, y: Limbs) =
## In-place substraction for multi-precision unsigned int
var borrow = Borrow(0)
for wx, wy in leastToMostSig(x, y):
subB(borrow, wx, wx, wy, borrow)
func inc*(x: var Limbs, w: SomeUnsignedInt = 1) =
var carry = Carry(0)
when cpuEndian == littleEndian:
addC(carry, x[0], x[0], w, carry)
for i in 1 ..< x.len:
addC(carry, x[i], x[i], 0, carry)

View File

@ -1,60 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes
{.push raises: [], inline, noInit, gcsafe.}
func `not`*(x: Limbs): Limbs =
## Bitwise complement of unsigned integer x
for wr, wx in leastToMostSig(result, x):
wr = not wx
func `or`*(x, y: Limbs): Limbs =
## `Bitwise or` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx or wy
func `and`*(x, y: Limbs): Limbs =
## `Bitwise and` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx and wy
func `xor`*(x, y: Limbs): Limbs =
## `Bitwise xor` of numbers x and y
for wr, wx, wy in leastToMostSig(result, x, y):
wr = wx xor wy
func `shr`*(x: Limbs, k: SomeInteger): Limbs =
## Shift right by k.
##
## k MUST be less than the base word size (2^32 or 2^64)
# Note: for speed, loading a[i] and a[i+1]
# instead of a[i-1] and a[i]
# is probably easier to parallelize for the compiler
# (antidependence WAR vs loop-carried dependence RAW)
when cpuEndian == littleEndian:
for i in 0 ..< x.len-1:
result[i] = (x[i] shr k) or (x[i+1] shl (WordBitWidth - k))
result[^1] = x[^1] shr k
else:
for i in countdown(x.len-1, 1):
result[i] = (x[i] shr k) or (x[i-1] shl (WordBitWidth - k))
result[0] = x[0] shr k
func `shl`*(x: Limbs, k: SomeInteger): Limbs =
## Compute the `shift left` operation of x and k
when cpuEndian == littleEndian:
result[0] = x[0] shl k
for i in 1 ..< x.len:
result[i] = (x[i] shl k) or (x[i-1] shr (WordBitWidth - k))
else:
result[^1] = x[^1] shl k
for i in countdown(x.len-2, 0):
result[i] = (x[i] shl k) or (x[i+1] shr (WordBitWidth - k))

View File

@ -1,54 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
./datatypes,
./primitives/addcarry_subborrow
{.push raises: [], inline, noInit, gcsafe.}
func isZero*(n: SomeUnsignedInt): bool =
n == 0
func isZero*(limbs: Limbs): bool =
for word in limbs:
if not word.isZero():
return false
return true
func `<`*(x, y: Limbs): bool =
# Lower comparison for multi-precision integers
var diff: Word
var borrow: Borrow
for wx, wy in leastToMostSig(x, y):
subB(borrow, diff, wx, wy, borrow)
return bool(borrow)
func `==`*(x, y: Limbs): bool =
# Equal comparison for multi-precision integers
for wx, wy in leastToMostSig(x, y):
if wx != wy:
return false
return true
func `<=`*(x, y: Limbs): bool =
# Lower or equal comparison for multi-precision integers
not(y < x)
func isEven*(x: SomeUnsignedInt): bool =
(x and 1) == 0
func isEven*(x: Limbs): bool =
x.leastSignificantWord.isEven
func isOdd*(x: SomeUnsignedInt): bool =
not x.isEven
func isOdd*(x: Limbs): bool =
not x.isEven

View File

@ -1,16 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./initialization, ./uint_bitwise_ops
func low*(T: typedesc[UintImpl]): T {.inline.}=
zero(T)
func high*(T: typedesc[UintImpl]): T {.inline.}=
not zero(T)

View File

@ -1,5 +1,5 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Copyright 2018-Present 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)
@ -14,7 +14,7 @@ import
# ################### Multiplication ################### #
{.push raises: [], gcsafe.}
func prod*[rLen, aLen, bLen](r: var Limbs[rLen], a: Limbs[aLen], b: Limbs[bLen]) =
func prod*[rLen, aLen, bLen: static int](r: var Limbs[rLen], a: Limbs[aLen], b: Limbs[bLen]) =
## Multi-precision multiplication
## r <- a*b
##
@ -25,7 +25,7 @@ func prod*[rLen, aLen, bLen](r: var Limbs[rLen], a: Limbs[aLen], b: Limbs[bLen])
# We use Product Scanning / Comba multiplication
var t, u, v = Word(0)
var z: Limbs[rLen] # zero-init, ensure on stack and removes in-place problems
var z: typeof(r) # zero-init, ensure on stack and removes in-place problems
staticFor i, 0, min(a.len+b.len, r.len):
const ib = min(b.len-1, i)
@ -36,11 +36,11 @@ func prod*[rLen, aLen, bLen](r: var Limbs[rLen], a: Limbs[aLen], b: Limbs[bLen])
z[i] = v
v = u
u = t
t = Word(0)
t = 0
r = z
func prod_high_words*[rLen, aLen, bLen](
func prod_high_words*[rLen, aLen, bLen: static int](
r: var Limbs[rLen],
a: Limbs[aLen], b: Limbs[bLen],
lowestWordIndex: static int) =

230
stint/uintops.nim Normal file
View File

@ -0,0 +1,230 @@
# Stint
# Copyright 2018-2020 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
# Status lib
stew/bitops2,
# Internal
./private/datatypes,
./private/primitives/addcarry_subborrow
export StUint
# Initialization
# --------------------------------------------------------
{.push raises: [], inline, noInit, gcsafe.}
func setZero*(a: var StUint) =
## Set ``a`` to 0
zeroMem(a[0].addr, sizeof(a))
func setOne*(a: var StUint) =
## Set ``a`` to 1
when cpuEndian == littleEndian:
a.limbs[0] = 1
when a.limbs.len > 1:
zeroMem(a.limbs[1].addr, (a.limbs.len - 1) * sizeof(SecretWord))
else:
a.limbs[^1] = 1
when a.limbs.len > 1:
zeroMem(a.limbs[0].addr, (a.len - 1) * sizeof(SecretWord))
func zero*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
## Returns the zero of the input type
discard
func one*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
## Returns the one of the input type
result.limbs.setOne()
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
for wr in leastToMostSig(result):
wr = high(Word)
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
discard
{.pop.}
# Comparisons
# --------------------------------------------------------
{.push raises: [], inline, noInit, gcsafe.}
func isZero*(a: Stuint): bool =
for word in leastToMostSig(a):
if word != 0:
return false
return true
func `==`*(a, b: Stuint): bool {.inline.} =
## Unsigned `equal` comparison
for wa, wb in leastToMostSig(a, b):
if wa != wb:
return false
return true
func `<`*(a, b: Stuint): bool {.inline.} =
## Unsigned `less than` comparison
var diff: Word
var borrow: Borrow
for wa, wb in leastToMostSig(a, b):
subB(borrow, diff, wa, wb, borrow)
return bool(borrow)
func `<=`*(a, b: Stuint): bool {.inline.} =
## Unsigned `less or equal` comparison
not(a < b)
func isOdd*(a: Stuint): bool {.inline.} =
## Returns true if input is off
## false otherwise
bool(a.leastSignificantWord and 1)
func isEven*(a: Stuint): bool {.inline.} =
## Returns true if input is zero
## false otherwise
not a.isOdd
{.pop.}
# Bitwise operations
# --------------------------------------------------------
{.push raises: [], inline, noInit, gcsafe.}
func `not`*(a: Stuint): Stuint =
## Bitwise complement of unsigned integer a
## i.e. flips all bits of the input
for wr, wa in leastToMostSig(result, a):
wr = not wa
func `or`*(a, b: Stuint): Stuint =
## `Bitwise or` of numbers a and b
for wr, wa, wb in leastToMostSig(result, a, b):
wr = wa or wb
func `and`*(a, b: Stuint): Stuint =
## `Bitwise and` of numbers a and b
for wr, wa, wb in leastToMostSig(result, a, b):
wr = wa and wb
func `xor`*(a, b: Stuint): Stuint =
## `Bitwise xor` of numbers x and y
for wr, wa, wb in leastToMostSig(result, a, b):
wr = wa xor wb
func `shr`*(a: Stuint, k: SomeInteger): Stuint =
## Shift right by k.
##
## k MUST be less than the base word size (2^32 or 2^64)
# Note: for speed, loading a[i] and a[i+1]
# instead of a[i-1] and a[i]
# is probably easier to parallelize for the compiler
# (antidependence WAR vs loop-carried dependence RAW)
when cpuEndian == littleEndian:
for i in 0 ..< a.limbs.len-1:
result.limbs[i] = (a.limbs[i] shr k) or (a.limbs[i+1] shl (WordBitWidth - k))
result.limbs[^1] = a.limbs[^1] shr k
else:
for i in countdown(a.limbs.len-1, 1):
result.limbs[i] = (a.limbs[i] shr k) or (a.limbs[i-1] shl (WordBitWidth - k))
result.limbs[0] = a.limbs[0] shr k
func `shl`*(a: Stuint, k: SomeInteger): Stuint =
## Compute the `shift left` operation of x and k
when cpuEndian == littleEndian:
result.limbs[0] = a.limbs[0] shl k
for i in 1 ..< a.limbs.len:
result.limbs[i] = (a.limbs[i] shl k) or (a.limbs[i-1] shr (WordBitWidth - k))
else:
result.limbs[^1] = a.limbs[^1] shl k
for i in countdown(a.limbs.len-2, 0):
result.limbs[i] = (a.limbs[i] shl k) or (a.limbs[i+1] shr (WordBitWidth - k))
func countOnes*(x: Stuint): int {.inline.} =
result = 0
for wx in leastToMostSig(x):
result += countOnes(wx)
func parity*(x: Stuint): int {.inline.} =
result = parity(x.limbs[0])
for i in 1 ..< x.limbs.len:
result = result xor parity(x.limbs[i])
func leadingZeros*(x: Stuint): int {.inline.} =
result = 0
for word in mostToLeastSig(x):
let zeroCount = word.leadingZeros()
result += zeroCount
if zeroCount != WordBitWidth:
return
func trailingZeros*(x: Stuint): int {.inline.} =
result = 0
for word in leastToMostSig(x):
let zeroCount = word.leadingZeros()
result += zeroCount
if zeroCount != WordBitWidth:
return
func firstOne*(x: Stuint): int {.inline.} =
result = trailingZeros(x)
if result == x.limbs.len * WordBitWidth:
result = 0
else:
result += 1
{.pop.}
# Addsub
# --------------------------------------------------------
{.push raises: [], inline, noInit, gcsafe.}
func `+`*(a, b: Stuint): Stuint =
# Addition for multi-precision unsigned int
var carry = Carry(0)
for wr, wa, wb in leastToMostSig(result, a, b):
addC(carry, wr, wa, wb, carry)
func `+=`*(a: var Stuint, b: Stuint) =
## In-place addition for multi-precision unsigned int
var carry = Carry(0)
for wa, wb in leastToMostSig(a, b):
addC(carry, wa, wa, wb, carry)
func `-`*(a, b: Stuint): Stuint =
# Substraction for multi-precision unsigned int
var borrow = Borrow(0)
for wr, wa, wb in leastToMostSig(result, a, b):
subB(borrow, wr, wa, wb, borrow)
func `-=`*(a: var Stuint, b: Stuint) =
## In-place substraction for multi-precision unsigned int
var borrow = Borrow(0)
for wa, wb in leastToMostSig(a, b):
subB(borrow, wa, wa, wb, borrow)
func inc*(a: var Stuint, w: Word = 1) =
var carry = Carry(0)
when cpuEndian == littleEndian:
addC(carry, x.limbs[0], x.limbs[0], w, carry)
for i in 1 ..< x.len:
addC(carry, x.limbs[i], x.limbs[i], 0, carry)
{.pop.}
# Multiplication
# --------------------------------------------------------
import ./private/uint_mul
{.push raises: [], inline, noInit, gcsafe.}
func `*`*(a, b: Stuint): Stuint {.inline.} =
## Integer multiplication
result.limbs.prod(a.limbs, b.limbs)
{.pop.}
# Division & Modulo
# --------------------------------------------------------
# Exponentiation
# --------------------------------------------------------