Use littleEndian for limb-endianness: bigEndian arch are very rare, untestable in CI, a pain to maintain and an intermediate serialization step instead of casting is cheap
This commit is contained in:
parent
7efa2483e4
commit
4660dfe4a4
|
@ -20,8 +20,7 @@ Main focus:
|
||||||
- Uint2048 for Ethereum Bloom filters
|
- Uint2048 for Ethereum Bloom filters
|
||||||
- Ease of use:
|
- Ease of use:
|
||||||
- Use traditional `+`, `-`, `+=`, etc operators like on native types
|
- Use traditional `+`, `-`, `+=`, etc operators like on native types
|
||||||
- Representation of numbers in memory is the exact same as native types and endianness aware.
|
- converting to and from raw byte BigInts (also called octet string in IETF specs)
|
||||||
- In practice that means that interfacing with binary blobs representing numbers from cryptographic libraries can be done with a `cast` if it represents a Uint256, Uint512, Uint1024, Uint2048.
|
|
||||||
- converting to and from Hex
|
- converting to and from Hex
|
||||||
- converting to and from decimal strings
|
- converting to and from decimal strings
|
||||||
|
|
||||||
|
|
|
@ -245,28 +245,3 @@ func fromBytes*[bits: static int](
|
||||||
result = fromBytesLE(T, x)
|
result = fromBytesLE(T, x)
|
||||||
else:
|
else:
|
||||||
result = fromBytesBE(T, x)
|
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, 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 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, 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)
|
|
||||||
|
|
95
stint/io.nim
95
stint/io.nim
|
@ -33,22 +33,10 @@ template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
|
||||||
|
|
||||||
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
||||||
## Converts an integer to an arbitrary precision integer.
|
## Converts an integer to an arbitrary precision integer.
|
||||||
when cpuEndian == littleEndian:
|
result.limbs[0] = Word(n)
|
||||||
result.limbs[0] = Word(n)
|
when sizeof(n) > sizeof(Word):
|
||||||
when sizeof(n) > sizeof(Word):
|
result.limbs[1] = Word(n) shr WordBitWidth
|
||||||
result.limbs[1] = Word(n) shr WordBitWidth
|
|
||||||
else:
|
|
||||||
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.}=
|
# func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
|
||||||
# ## Converts an integer to an arbitrary precision signed integer.
|
# ## Converts an integer to an arbitrary precision signed integer.
|
||||||
#
|
#
|
||||||
|
@ -88,8 +76,8 @@ func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
|
||||||
## unsigned int to unsigned int conversion
|
## unsigned int to unsigned int conversion
|
||||||
## smaller to bigger bits conversion will have the same value
|
## smaller to bigger bits conversion will have the same value
|
||||||
## bigger to smaller bits conversion, the result is truncated
|
## bigger to smaller bits conversion, the result is truncated
|
||||||
for wr, wa in leastToMostSig(result, a):
|
for i in 0 ..< result.len:
|
||||||
wr = wa
|
result[i] = a[i]
|
||||||
|
|
||||||
# func stuint*(a: StInt, bits: static[int]): StUint[bits] {.inline.} =
|
# func stuint*(a: StInt, bits: static[int]): StUint[bits] {.inline.} =
|
||||||
# ## signed int to unsigned int conversion
|
# ## signed int to unsigned int conversion
|
||||||
|
@ -377,82 +365,13 @@ func dumpHex*(a: Stint or StUint, order: static[Endianness] = bigEndian): string
|
||||||
let bytes = a.toBytes(order)
|
let bytes = a.toBytes(order)
|
||||||
result = bytes.toHex()
|
result = bytes.toHex()
|
||||||
|
|
||||||
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.
|
|
||||||
##
|
|
||||||
## If `allowPadding` is set to false, the input array must be exactly
|
|
||||||
## (bits div 8) bytes long. Otherwise, it may be shorter and the remaining
|
|
||||||
## bytes will be assumed to be zero.
|
|
||||||
|
|
||||||
const N = bits div 8
|
|
||||||
|
|
||||||
when not allowPadding:
|
|
||||||
doAssert(ba.len == N)
|
|
||||||
else:
|
|
||||||
doAssert ba.len <= N
|
|
||||||
when system.cpuEndian == bigEndian:
|
|
||||||
let baseIdx = N - val.len
|
|
||||||
else:
|
|
||||||
let baseIdx = ba.len - 1
|
|
||||||
|
|
||||||
when nimvm:
|
|
||||||
when system.cpuEndian == bigEndian:
|
|
||||||
when allowPadding:
|
|
||||||
for i, b in ba: val.data.setByte(baseIdx + i, b)
|
|
||||||
else:
|
|
||||||
for i, b in ba: val.data.setByte(i, b)
|
|
||||||
else:
|
|
||||||
when allowPadding:
|
|
||||||
for i, b in ba: val.data.setByte(baseIdx - i, b)
|
|
||||||
else:
|
|
||||||
for i, b in ba: val.data.setByte(N-1 - i, b)
|
|
||||||
else:
|
|
||||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
|
||||||
let r_ptr {.restrict.} = cast[ptr array[N, byte]](val.addr)
|
|
||||||
|
|
||||||
when system.cpuEndian == bigEndian:
|
|
||||||
# TODO: due to https://github.com/status-im/nim-stint/issues/38
|
|
||||||
# We can't cast a stack byte array to stuint with a convenient proc signature.
|
|
||||||
when allowPadding:
|
|
||||||
for i, b in ba: r_ptr[baseIdx + i] = b
|
|
||||||
else:
|
|
||||||
for i, b in ba: r_ptr[i] = b
|
|
||||||
else:
|
|
||||||
when allowPadding:
|
|
||||||
for i, b in ba: r_ptr[baseIdx - i] = b
|
|
||||||
else:
|
|
||||||
for i, b in ba: r_ptr[N-1 - i] = b
|
|
||||||
|
|
||||||
func significantBytesBE*(val: openArray[byte]): int {.deprecated.}=
|
|
||||||
## Returns the number of significant trailing bytes in a big endian
|
|
||||||
## representation of a number.
|
|
||||||
# TODO: move that in https://github.com/status-im/nim-byteutils
|
|
||||||
for i in 0 ..< val.len:
|
|
||||||
if val[i] != 0:
|
|
||||||
return val.len - i
|
|
||||||
return 1
|
|
||||||
|
|
||||||
func fromBytesBE*(T: type Stuint, ba: openarray[byte],
|
|
||||||
allowPadding: static[bool] = true): T {.noInit, inline.} =
|
|
||||||
## This function provides a convenience wrapper around `initFromBytesBE`.
|
|
||||||
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] {.noInit, inline.}=
|
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)
|
## Convert a big-endian array of (bits div 8) Bytes to an UInt[bits] (in native host endianness)
|
||||||
## Input:
|
## Input:
|
||||||
## - a big-endian openArray of size (bits div 8) at least
|
## - a big-endian openArray of size (bits div 8) at least
|
||||||
## Returns:
|
## Returns:
|
||||||
## - A unsigned integer of the same size with `bits` bits
|
## - A unsigned integer of the same size with `bits` bits
|
||||||
##
|
result = (typeof result).fromBytesBE(ba)
|
||||||
## ⚠ 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] {.noInit, inline.}=
|
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
|
## Convert a uint[bits] to to a big-endian array of bits div 8 bytes
|
||||||
|
@ -460,7 +379,7 @@ func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte]
|
||||||
## - an unsigned integer
|
## - an unsigned integer
|
||||||
## Returns:
|
## Returns:
|
||||||
## - a big-endian array of the same size
|
## - a big-endian array of the same size
|
||||||
result = n.toBytes(bigEndian)
|
result = n.toBytesBE()
|
||||||
|
|
||||||
template hash*(num: StUint|StInt): Hash =
|
template hash*(num: StUint|StInt): Hash =
|
||||||
# TODO:
|
# TODO:
|
||||||
|
|
|
@ -20,35 +20,22 @@ const WordBitWidth* = sizeof(Word) * 8
|
||||||
|
|
||||||
func wordsRequired*(bits: int): int {.compileTime.} =
|
func wordsRequired*(bits: int): int {.compileTime.} =
|
||||||
## Compute the number of limbs required
|
## Compute the number of limbs required
|
||||||
## from the **announced** bit length
|
## for the **announced** bit length
|
||||||
(bits + WordBitWidth - 1) div WordBitWidth
|
(bits + WordBitWidth - 1) div WordBitWidth
|
||||||
|
|
||||||
type
|
type
|
||||||
Limbs*[N: static int] = array[N, Word]
|
Limbs*[N: static int] = array[N, Word]
|
||||||
## Limbs type
|
## Limbs type
|
||||||
## Large proc like multiplication and division
|
|
||||||
## should operate at the limb-level
|
|
||||||
## to avoid duplicate codepaths
|
|
||||||
## For example for Stuint[16] and Stuint[32]
|
|
||||||
## or if allowed in the future
|
|
||||||
## Stuint[254] and Stuint[256]
|
|
||||||
|
|
||||||
StUint*[bits: static[int]] = object
|
StUint*[bits: static[int]] = object
|
||||||
## Stack-based integer
|
## Stack-based integer
|
||||||
## Unsigned
|
## Unsigned
|
||||||
limbs*: array[bits.wordsRequired, Word]
|
limbs*: array[bits.wordsRequired, Word]
|
||||||
# TODO: using the limbs type here
|
# Limbs-Endianess is little-endian
|
||||||
# can using StUint[8] of length 2, instead of 1
|
|
||||||
# in test_uint_bitwise (in the VM)
|
|
||||||
# unless you put the following instantiation
|
|
||||||
# at the bottom of this file
|
|
||||||
# static:
|
|
||||||
# echo StUint[8]()
|
|
||||||
|
|
||||||
StInt*[bits: static[int]] = object
|
StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits]
|
||||||
## Stack-based integer
|
## Stack-based integer
|
||||||
## Signed
|
## Signed
|
||||||
limbs*: array[bits.wordsRequired, Word]
|
|
||||||
|
|
||||||
Carry* = uint8 # distinct range[0'u8 .. 1]
|
Carry* = uint8 # distinct range[0'u8 .. 1]
|
||||||
Borrow* = uint8 # distinct range[0'u8 .. 1]
|
Borrow* = uint8 # distinct range[0'u8 .. 1]
|
||||||
|
@ -62,25 +49,12 @@ when sizeof(int) == 8 and GCC_Compatible:
|
||||||
type
|
type
|
||||||
uint128*{.importc: "unsigned __int128".} = object
|
uint128*{.importc: "unsigned __int128".} = object
|
||||||
|
|
||||||
# Accessors
|
# Bithacks
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
template leastSignificantWord*(num: SomeInteger): auto =
|
{.push raises: [], inline, noInit, gcsafe.}
|
||||||
num
|
|
||||||
|
|
||||||
template leastSignificantWord*(a: SomeBigInteger): auto =
|
template clearExtraBitsOverMSB*(a: var StUint) =
|
||||||
when cpuEndian == littleEndian:
|
|
||||||
a.limbs[0]
|
|
||||||
else:
|
|
||||||
a.limbs[^1]
|
|
||||||
|
|
||||||
template mostSignificantWord*(a: SomeBigInteger): auto =
|
|
||||||
when cpuEndian == littleEndian:
|
|
||||||
a.limbs[^1]
|
|
||||||
else:
|
|
||||||
a.limbs[0]
|
|
||||||
|
|
||||||
template clearExtraBits*(a: var StUint) =
|
|
||||||
## A Stuint is stored in an array of 32 of 64-bit word
|
## A Stuint is stored in an array of 32 of 64-bit word
|
||||||
## If we do bit manipulation at the word level,
|
## If we do bit manipulation at the word level,
|
||||||
## for example a 8-bit stuint stored in a 64-bit word
|
## for example a 8-bit stuint stored in a 64-bit word
|
||||||
|
@ -88,65 +62,35 @@ template clearExtraBits*(a: var StUint) =
|
||||||
when a.bits != a.limbs.len * WordBitWidth:
|
when a.bits != a.limbs.len * WordBitWidth:
|
||||||
const posExtraBits = a.bits - (a.limbs.len-1) * WordBitWidth
|
const posExtraBits = a.bits - (a.limbs.len-1) * WordBitWidth
|
||||||
const mask = (Word(1) shl posExtraBits) - 1
|
const mask = (Word(1) shl posExtraBits) - 1
|
||||||
mostSignificantWord(a) = mostSignificantWord(a) and mask
|
a[^1] = a[^1] and mask
|
||||||
|
|
||||||
|
func usedBitsAndWords*(a: openArray[Word]): tuple[bits, words: int] =
|
||||||
|
## Returns the number of used words and bits in a bigInt
|
||||||
|
var clz = 0
|
||||||
|
# Count Leading Zeros
|
||||||
|
for i in countdown(a.len-1, 0):
|
||||||
|
let count = log2trunc(a[i])
|
||||||
|
# debugEcho "count: ", count, ", a[", i, "]: ", a[i].toBin(64)
|
||||||
|
if count == -1:
|
||||||
|
clz += WordBitWidth
|
||||||
|
else:
|
||||||
|
clz += WordBitWidth - count - 1
|
||||||
|
return (a.len*WordBitWidth - clz, i+1)
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
# Accessors
|
||||||
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
template `[]`*(a: SomeBigInteger, i: SomeInteger or BackwardsIndex): Word =
|
||||||
|
a.limbs[i]
|
||||||
|
|
||||||
|
template `[]=`*(a: var SomeBigInteger, i: SomeInteger or BackwardsIndex, val: Word) =
|
||||||
|
a.limbs[i] = val
|
||||||
|
|
||||||
# Iterations
|
# Iterations
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
iterator leastToMostSig*(a: SomeBigInteger): Word =
|
|
||||||
## Iterate from least to most significant word
|
|
||||||
when cpuEndian == littleEndian:
|
|
||||||
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]
|
|
||||||
|
|
||||||
iterator leastToMostSig*(a: var SomeBigInteger): var Word =
|
|
||||||
## Iterate from least to most significant word
|
|
||||||
when cpuEndian == littleEndian:
|
|
||||||
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]
|
|
||||||
|
|
||||||
iterator leastToMostSig*(a, b: SomeBigInteger): (Word, Word) =
|
|
||||||
## Iterate from least to most significant word
|
|
||||||
when cpuEndian == littleEndian:
|
|
||||||
for i in 0 ..< a.limbs.len:
|
|
||||||
yield (a.limbs[i], b.limbs[i])
|
|
||||||
else:
|
|
||||||
for i in countdown(a.limbs.len-1, 0):
|
|
||||||
yield (a.limbs[i], b.limbs[i])
|
|
||||||
|
|
||||||
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 ..< min(a.limbs.len, b.limbs.len):
|
|
||||||
yield (a.limbs[i], b.limbs[i])
|
|
||||||
else:
|
|
||||||
for i in countdown(min(a.limbs.len, b.limbs.len)-1, 0):
|
|
||||||
yield (a.limbs[i], b.limbs[i])
|
|
||||||
|
|
||||||
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 ..< a.limbs.len:
|
|
||||||
yield (c.limbs[i], a.limbs[i], b.limbs[i])
|
|
||||||
else:
|
|
||||||
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
|
import std/macros
|
||||||
|
|
||||||
proc replaceNodes(ast: NimNode, what: NimNode, by: NimNode): NimNode =
|
proc replaceNodes(ast: NimNode, what: NimNode, by: NimNode): NimNode =
|
||||||
|
@ -179,20 +123,15 @@ macro staticFor*(idx: untyped{nkIdent}, start, stopEx: static int, body: untyped
|
||||||
|
|
||||||
# Copy
|
# Copy
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
{.push raises: [], inline, noInit, gcsafe.}
|
||||||
|
|
||||||
func copyFrom*(
|
func copyWords*(
|
||||||
dst: var SomeBigInteger,
|
a: var openArray[Word], startA: int,
|
||||||
src: SomeBigInteger
|
b: openArray[Word], startB: int,
|
||||||
){.inline.} =
|
numWords: int) =
|
||||||
## Copy a BigInteger, truncated to 2^slen if the source
|
## Copy a slice of B into A. This properly deals
|
||||||
## is larger than the destination
|
## with overlaps when A and B are slices of the same buffer
|
||||||
when cpuEndian == littleEndian:
|
for i in countdown(numWords-1, 0):
|
||||||
for i in 0 ..< min(dst.limbs.len, src.limbs.len):
|
a[startA+i] = b[startB+i]
|
||||||
dst.limbs[i] = src.limbs[i]
|
|
||||||
for i in src.limbs.len ..< dst.limbs.len:
|
{.pop.}
|
||||||
dst.limbs[i] = 0
|
|
||||||
else:
|
|
||||||
for i in countdown(dst.limbs.len-1, src.limbs.len):
|
|
||||||
dst.limbs[i] = 0
|
|
||||||
for i in countdown(src.limbs.len-1, 0):
|
|
||||||
dst.limbs[i] = src.limbs[i]
|
|
|
@ -19,40 +19,31 @@ import
|
||||||
func sum*(r: var Stuint, a, b: Stuint) =
|
func sum*(r: var Stuint, a, b: Stuint) =
|
||||||
## Addition for multi-precision unsigned int
|
## Addition for multi-precision unsigned int
|
||||||
var carry = Carry(0)
|
var carry = Carry(0)
|
||||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
for i in 0 ..< r.limbs.len:
|
||||||
addC(carry, wr, wa, wb, carry)
|
addC(carry, r[i], a[i], b[i], carry)
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
|
|
||||||
func `+=`*(a: var Stuint, b: Stuint) =
|
func `+=`*(a: var Stuint, b: Stuint) =
|
||||||
## In-place addition for multi-precision unsigned int
|
## In-place addition for multi-precision unsigned int
|
||||||
var carry = Carry(0)
|
a.sum(a, b)
|
||||||
for wa, wb in leastToMostSig(a, b):
|
|
||||||
addC(carry, wa, wa, wb, carry)
|
|
||||||
a.clearExtraBits()
|
|
||||||
|
|
||||||
func diff*(r: var Stuint, a, b: Stuint) =
|
func diff*(r: var Stuint, a, b: Stuint) =
|
||||||
## Substraction for multi-precision unsigned int
|
## Substraction for multi-precision unsigned int
|
||||||
var borrow = Borrow(0)
|
var borrow = Borrow(0)
|
||||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
for i in 0 ..< r.limbs.len:
|
||||||
subB(borrow, wr, wa, wb, borrow)
|
subB(borrow, r[i], a[i], b[i], borrow)
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
|
|
||||||
func `-=`*(a: var Stuint, b: Stuint) =
|
func `-=`*(a: var Stuint, b: Stuint) =
|
||||||
## In-place substraction for multi-precision unsigned int
|
## In-place substraction for multi-precision unsigned int
|
||||||
var borrow = Borrow(0)
|
a.diff(a, b)
|
||||||
for wa, wb in leastToMostSig(a, b):
|
|
||||||
subB(borrow, wa, wa, wb, borrow)
|
|
||||||
a.clearExtraBits()
|
|
||||||
|
|
||||||
func inc*(a: var Stuint, w: Word = 1) =
|
func inc*(a: var Stuint, w: Word = 1) =
|
||||||
var carry = Carry(0)
|
var carry = Carry(0)
|
||||||
when cpuEndian == littleEndian:
|
addC(carry, a.limbs[0], a.limbs[0], w, carry)
|
||||||
addC(carry, a.limbs[0], a.limbs[0], w, carry)
|
for i in 1 ..< a.limbs.len:
|
||||||
for i in 1 ..< a.limbs.len:
|
addC(carry, a.limbs[i], a.limbs[i], 0, carry)
|
||||||
addC(carry, a.limbs[i], a.limbs[i], 0, carry)
|
a.clearExtraBitsOverMSB()
|
||||||
else:
|
|
||||||
{.error: "Not implemented.".}
|
|
||||||
a.clearExtraBits()
|
|
||||||
|
|
||||||
func sum*(r: var Stuint, a: Stuint, b: SomeUnsignedInt) =
|
func sum*(r: var Stuint, a: Stuint, b: SomeUnsignedInt) =
|
||||||
## Addition for multi-precision unsigned int
|
## Addition for multi-precision unsigned int
|
||||||
|
|
|
@ -20,30 +20,30 @@ import
|
||||||
func bitnot*(r: var StUint, a: Stuint) =
|
func bitnot*(r: var StUint, a: Stuint) =
|
||||||
## Bitwise complement of unsigned integer a
|
## Bitwise complement of unsigned integer a
|
||||||
## i.e. flips all bits of the input
|
## i.e. flips all bits of the input
|
||||||
for wr, wa in leastToMostSig(r, a):
|
for i in 0 ..< r.len:
|
||||||
wr = not wa
|
r[i] = not a[i]
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
|
|
||||||
func bitor*(r: var Stuint, a, b: Stuint) =
|
func bitor*(r: var Stuint, a, b: Stuint) =
|
||||||
## `Bitwise or` of numbers a and b
|
## `Bitwise or` of numbers a and b
|
||||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
for i in 0 ..< r.limbs.len:
|
||||||
wr = wa or wb
|
r[i] = a[i] or b[i]
|
||||||
|
|
||||||
func bitand*(r: var Stuint, a, b: Stuint) =
|
func bitand*(r: var Stuint, a, b: Stuint) =
|
||||||
## `Bitwise and` of numbers a and b
|
## `Bitwise and` of numbers a and b
|
||||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
for i in 0 ..< r.limbs.len:
|
||||||
wr = wa and wb
|
r[i] = a[i] and b[i]
|
||||||
|
|
||||||
func bitxor*(r: var Stuint, a, b: Stuint) =
|
func bitxor*(r: var Stuint, a, b: Stuint) =
|
||||||
## `Bitwise xor` of numbers x and y
|
## `Bitwise xor` of numbers x and y
|
||||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
for i in 0 ..< r.limbs.len:
|
||||||
wr = wa xor wb
|
r[i] = a[i] xor b[i]
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
|
|
||||||
func countOnes*(a: Stuint): int =
|
func countOnes*(a: Stuint): int =
|
||||||
result = 0
|
result = 0
|
||||||
for wa in leastToMostSig(a):
|
for i in 0 ..< a.limbs.len:
|
||||||
result += countOnes(wa)
|
result += countOnes(a[i])
|
||||||
|
|
||||||
func parity*(a: Stuint): int =
|
func parity*(a: Stuint): int =
|
||||||
result = parity(a.limbs[0])
|
result = parity(a.limbs[0])
|
||||||
|
@ -56,8 +56,8 @@ func leadingZeros*(a: Stuint): int =
|
||||||
# Adjust when we use only part of the word size
|
# Adjust when we use only part of the word size
|
||||||
var extraBits = WordBitWidth * a.limbs.len - a.bits
|
var extraBits = WordBitWidth * a.limbs.len - a.bits
|
||||||
|
|
||||||
for word in mostToLeastSig(a):
|
for i in countdown(a.len-1, 0):
|
||||||
let zeroCount = word.leadingZeros()
|
let zeroCount = a.limbs[i].leadingZeros()
|
||||||
if extraBits > 0:
|
if extraBits > 0:
|
||||||
result += zeroCount - min(extraBits, WordBitWidth)
|
result += zeroCount - min(extraBits, WordBitWidth)
|
||||||
extraBits -= WordBitWidth
|
extraBits -= WordBitWidth
|
||||||
|
@ -68,8 +68,8 @@ func leadingZeros*(a: Stuint): int =
|
||||||
|
|
||||||
func trailingZeros*(a: Stuint): int =
|
func trailingZeros*(a: Stuint): int =
|
||||||
result = 0
|
result = 0
|
||||||
for word in leastToMostSig(a):
|
for i in 0 ..< a.limbs.len:
|
||||||
let zeroCount = word.trailingZeros()
|
let zeroCount = a[i].trailingZeros()
|
||||||
result += zeroCount
|
result += zeroCount
|
||||||
if zeroCount != WordBitWidth:
|
if zeroCount != WordBitWidth:
|
||||||
break
|
break
|
||||||
|
|
|
@ -15,31 +15,6 @@ import
|
||||||
./uint_bitwise,
|
./uint_bitwise,
|
||||||
./primitives/[addcarry_subborrow, extended_precision]
|
./primitives/[addcarry_subborrow, extended_precision]
|
||||||
|
|
||||||
# Helpers
|
|
||||||
# --------------------------------------------------------
|
|
||||||
|
|
||||||
func usedBitsAndWords(a: openArray[Word]): tuple[bits, words: int] {.inline.} =
|
|
||||||
## Returns the number of used words and bits in a bigInt
|
|
||||||
var clz = 0
|
|
||||||
# Count Leading Zeros
|
|
||||||
for i in countdown(a.len-1, 0):
|
|
||||||
let count = log2trunc(a[i])
|
|
||||||
# debugEcho "count: ", count, ", a[", i, "]: ", a[i].toBin(64)
|
|
||||||
if count == -1:
|
|
||||||
clz += WordBitWidth
|
|
||||||
else:
|
|
||||||
clz += WordBitWidth - count - 1
|
|
||||||
return (a.len*WordBitWidth - clz, i+1)
|
|
||||||
|
|
||||||
func copyWords(
|
|
||||||
a: var openArray[Word], startA: int,
|
|
||||||
b: openArray[Word], startB: int,
|
|
||||||
numWords: int) =
|
|
||||||
## Copy a slice of B into A. This properly deals
|
|
||||||
## with overlaps when A and B are slices of the same buffer
|
|
||||||
for i in countdown(numWords-1, 0):
|
|
||||||
a[startA+i] = b[startB+i]
|
|
||||||
|
|
||||||
# Division
|
# Division
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
|
@ -312,7 +287,7 @@ func shlAddMod(a: var openArray[Word], c: Word,
|
||||||
else:
|
else:
|
||||||
return shlAddMod_multi(a, c, M, mBits)
|
return shlAddMod_multi(a, c, M, mBits)
|
||||||
|
|
||||||
func divRemImpl(
|
func divRem*(
|
||||||
q, r: var openArray[Word],
|
q, r: var openArray[Word],
|
||||||
a, b: openArray[Word]
|
a, b: openArray[Word]
|
||||||
) =
|
) =
|
||||||
|
@ -350,20 +325,6 @@ func divRemImpl(
|
||||||
for i in rLen ..< r.len:
|
for i in rLen ..< r.len:
|
||||||
r[i] = 0
|
r[i] = 0
|
||||||
|
|
||||||
func `div`*(x, y: Stuint): Stuint {.inline.} =
|
|
||||||
## Division operation for multi-precision unsigned uint
|
|
||||||
var tmp{.noInit.}: Stuint
|
|
||||||
divRemImpl(result.limbs, tmp.limbs, x.limbs, y.limbs)
|
|
||||||
|
|
||||||
func `mod`*(x, y: Stuint): Stuint {.inline.} =
|
|
||||||
## Remainder operation for multi-precision unsigned uint
|
|
||||||
var tmp{.noInit.}: Stuint
|
|
||||||
divRemImpl(tmp.limbs, result.limbs, x.limbs, y.limbs)
|
|
||||||
|
|
||||||
func divmod*(x, y: Stuint): tuple[quot, rem: Stuint] =
|
|
||||||
## Division and remainder operations for multi-precision unsigned uint
|
|
||||||
divRemImpl(result.quot.limbs, result.rem.limbs, x.limbs, y.limbs)
|
|
||||||
|
|
||||||
# ######################################################################
|
# ######################################################################
|
||||||
# Division implementations
|
# Division implementations
|
||||||
#
|
#
|
||||||
|
|
|
@ -25,14 +25,9 @@ func shrSmall*(r: var Limbs, a: Limbs, k: SomeInteger) =
|
||||||
# instead of a[i-1] and a[i]
|
# instead of a[i-1] and a[i]
|
||||||
# is probably easier to parallelize for the compiler
|
# is probably easier to parallelize for the compiler
|
||||||
# (antidependence WAR vs loop-carried dependence RAW)
|
# (antidependence WAR vs loop-carried dependence RAW)
|
||||||
when cpuEndian == littleEndian:
|
for i in 0 ..< a.len-1:
|
||||||
for i in 0 ..< a.len-1:
|
r[i] = (a[i] shr k) or (a[i+1] shl (WordBitWidth - k))
|
||||||
r[i] = (a[i] shr k) or (a[i+1] shl (WordBitWidth - k))
|
r[^1] = a[^1] shr k
|
||||||
r[^1] = a[^1] shr k
|
|
||||||
else:
|
|
||||||
for i in countdown(a.len-1, 1):
|
|
||||||
r[i] = (a[i] shr k) or (a[i-1] shl (WordBitWidth - k))
|
|
||||||
r[0] = a[0] shr k
|
|
||||||
|
|
||||||
func shrLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
func shrLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||||
## Shift right by `w` words + `shift` bits
|
## Shift right by `w` words + `shift` bits
|
||||||
|
@ -40,40 +35,24 @@ func shrLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||||
if w > Limbs.len:
|
if w > Limbs.len:
|
||||||
return
|
return
|
||||||
|
|
||||||
when cpuEndian == littleEndian:
|
for i in w ..< a.len-1:
|
||||||
for i in w ..< a.len-1:
|
r[i-w] = (a[i] shr shift) or (a[i+1] shl (WordBitWidth - shift))
|
||||||
r[i-w] = (a[i] shr shift) or (a[i+1] shl (WordBitWidth - shift))
|
r[^(1+w)] = a[^1] shr shift
|
||||||
r[^(1+w)] = a[^1] shr shift
|
|
||||||
else:
|
|
||||||
for i in countdown(a.len-1, 1+w):
|
|
||||||
r[i-w] = (a[i] shr shift) or (a[i-1] shl (WordBitWidth - k))
|
|
||||||
r[0] = a[w] shr shift
|
|
||||||
|
|
||||||
func shrWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
func shrWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
||||||
## Shift right by w word
|
## Shift right by w word
|
||||||
when cpuEndian == littleEndian:
|
for i in 0 ..< Limbs.len-w:
|
||||||
for i in 0 ..< Limbs.len-w:
|
r[i] = a[i+w]
|
||||||
r[i] = a[i+w]
|
for i in Limbs.len-w ..< Limbs.len:
|
||||||
for i in Limbs.len-w ..< Limbs.len:
|
r[i] = 0
|
||||||
r[i] = 0
|
|
||||||
else:
|
|
||||||
for i in countdown(Limbs.len-1, Limbs.len-w):
|
|
||||||
r[i] = 0
|
|
||||||
for i in countdown(Limbs.len-w, 0):
|
|
||||||
r[i] = a[i+w]
|
|
||||||
|
|
||||||
func shlSmall*(r: var Limbs, a: Limbs, k: SomeInteger) =
|
func shlSmall*(r: var Limbs, a: Limbs, k: SomeInteger) =
|
||||||
## Compute the `shift left` operation of x and k
|
## Compute the `shift left` operation of x and k
|
||||||
##
|
##
|
||||||
## k MUST be less than the base word size (2^32 or 2^64)
|
## k MUST be less than the base word size (2^32 or 2^64)
|
||||||
when cpuEndian == littleEndian:
|
r[0] = a[0] shl k
|
||||||
r[0] = a[0] shl k
|
for i in 1 ..< a.len:
|
||||||
for i in 1 ..< a.len:
|
r[i] = (a[i] shl k) or (a[i-1] shr (WordBitWidth - k))
|
||||||
r[i] = (a[i] shl k) or (a[i-1] shr (WordBitWidth - k))
|
|
||||||
else:
|
|
||||||
r[^1] = a[^1] shl k
|
|
||||||
for i in countdown(a.len-2, 0):
|
|
||||||
r[i] = (a[i] shl k) or (a[i+1] shr (WordBitWidth - k))
|
|
||||||
|
|
||||||
func shlLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
func shlLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||||
## Shift left by `w` words + `shift` bits
|
## Shift left by `w` words + `shift` bits
|
||||||
|
@ -81,27 +60,16 @@ func shlLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||||
if w > Limbs.len:
|
if w > Limbs.len:
|
||||||
return
|
return
|
||||||
|
|
||||||
when cpuEndian == littleEndian:
|
r[w] = a[0] shl shift
|
||||||
r[w] = a[0] shl shift
|
for i in 1+w ..< r.len:
|
||||||
for i in 1+w ..< r.len:
|
r[i] = (a[i-w] shl shift) or (a[i-w-1] shr (WordBitWidth - shift))
|
||||||
r[i] = (a[i-w] shl shift) or (a[i-w-1] shr (WordBitWidth - shift))
|
|
||||||
else:
|
|
||||||
r[^1] = a[^w] shl shift
|
|
||||||
for i in countdown(a.len-2-w, 0):
|
|
||||||
r[i+w] = (a[i] shl shift) or (a[i+1] shr (WordBitWidth - shift))
|
|
||||||
|
|
||||||
func shlWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
func shlWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
||||||
## Shift left by w word
|
## Shift left by w word
|
||||||
when cpuEndian == littleEndian:
|
for i in 0 ..< w:
|
||||||
for i in 0 ..< w:
|
r[i] = 0
|
||||||
r[i] = 0
|
for i in 0 ..< Limbs.len-w:
|
||||||
for i in 0 ..< Limbs.len-w:
|
r[i+w] = a[i]
|
||||||
r[i+w] = a[i]
|
|
||||||
else:
|
|
||||||
for i in countdown(Limbs.len-1, Limbs.len-w):
|
|
||||||
r[i] = 0
|
|
||||||
for i in countdown(Limbs.len-w-1, 0):
|
|
||||||
r[i] = a[i-w]
|
|
||||||
|
|
||||||
# Wrappers
|
# Wrappers
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
@ -133,7 +101,7 @@ func shiftLeft*(r: var Stuint, a: Stuint, k: SomeInteger) =
|
||||||
|
|
||||||
if k < WordBitWidth:
|
if k < WordBitWidth:
|
||||||
r.limbs.shlSmall(a.limbs, k)
|
r.limbs.shlSmall(a.limbs, k)
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
return
|
return
|
||||||
|
|
||||||
# w = k div WordBitWidth, shift = k mod WordBitWidth
|
# w = k div WordBitWidth, shift = k mod WordBitWidth
|
||||||
|
@ -145,4 +113,4 @@ func shiftLeft*(r: var Stuint, a: Stuint, k: SomeInteger) =
|
||||||
else:
|
else:
|
||||||
r.limbs.shlLarge(a.limbs, w, shift)
|
r.limbs.shlLarge(a.limbs, w, shift)
|
||||||
|
|
||||||
r.clearExtraBits()
|
r.clearExtraBitsOverMSB()
|
||||||
|
|
|
@ -30,14 +30,9 @@ func setZero*(a: var StUint) =
|
||||||
|
|
||||||
func setSmallInt(a: var StUint, k: Word) =
|
func setSmallInt(a: var StUint, k: Word) =
|
||||||
## Set ``a`` to k
|
## Set ``a`` to k
|
||||||
when cpuEndian == littleEndian:
|
a.limbs[0] = k
|
||||||
a.limbs[0] = k
|
for i in 1 ..< a.limbs.len:
|
||||||
for i in 1 ..< a.limbs.len:
|
a.limbs[i] = 0
|
||||||
a.limbs[i] = 0
|
|
||||||
else:
|
|
||||||
a.limbs[^1] = k
|
|
||||||
for i in 0 ..< a.limb.len - 1:
|
|
||||||
a.limbs[i] = 0
|
|
||||||
|
|
||||||
func setOne*(a: var StUint) =
|
func setOne*(a: var StUint) =
|
||||||
setSmallInt(a, 1)
|
setSmallInt(a, 1)
|
||||||
|
@ -51,8 +46,9 @@ func one*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
||||||
result.setOne()
|
result.setOne()
|
||||||
|
|
||||||
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||||
for wr in leastToMostSig(result):
|
for i in 0 ..< result.len:
|
||||||
wr = high(Word)
|
result[i] = high(Word)
|
||||||
|
|
||||||
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -62,15 +58,15 @@ func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||||
{.push raises: [], inline, noInit, gcsafe.}
|
{.push raises: [], inline, noInit, gcsafe.}
|
||||||
|
|
||||||
func isZero*(a: Stuint): bool =
|
func isZero*(a: Stuint): bool =
|
||||||
for word in leastToMostSig(a):
|
for i in 0 ..< a.limbs.len:
|
||||||
if word != 0:
|
if a[i] != 0:
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
func `==`*(a, b: Stuint): bool {.inline.} =
|
func `==`*(a, b: Stuint): bool {.inline.} =
|
||||||
## Unsigned `equal` comparison
|
## Unsigned `equal` comparison
|
||||||
for wa, wb in leastToMostSig(a, b):
|
for i in 0 ..< a.limbs.len:
|
||||||
if wa != wb:
|
if a[i] != b[i]:
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
@ -78,8 +74,8 @@ func `<`*(a, b: Stuint): bool {.inline.} =
|
||||||
## Unsigned `less than` comparison
|
## Unsigned `less than` comparison
|
||||||
var diff: Word
|
var diff: Word
|
||||||
var borrow: Borrow
|
var borrow: Borrow
|
||||||
for wa, wb in leastToMostSig(a, b):
|
for i in 0 ..< a.limbs.len:
|
||||||
subB(borrow, diff, wa, wb, borrow)
|
subB(borrow, diff, a[i], b[i], borrow)
|
||||||
return bool(borrow)
|
return bool(borrow)
|
||||||
|
|
||||||
func `<=`*(a, b: Stuint): bool {.inline.} =
|
func `<=`*(a, b: Stuint): bool {.inline.} =
|
||||||
|
@ -89,12 +85,12 @@ func `<=`*(a, b: Stuint): bool {.inline.} =
|
||||||
func isOdd*(a: Stuint): bool {.inline.} =
|
func isOdd*(a: Stuint): bool {.inline.} =
|
||||||
## Returns true if input is off
|
## Returns true if input is off
|
||||||
## false otherwise
|
## false otherwise
|
||||||
bool(a.leastSignificantWord and 1)
|
bool(a[0] and 1)
|
||||||
|
|
||||||
func isEven*(a: Stuint): bool {.inline.} =
|
func isEven*(a: Stuint): bool {.inline.} =
|
||||||
## Returns true if input is zero
|
## Returns true if input is zero
|
||||||
## false otherwise
|
## false otherwise
|
||||||
not a.isOdd
|
not a.isOdd()
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
# Bitwise operations
|
# Bitwise operations
|
||||||
|
@ -178,7 +174,7 @@ export `+=`
|
||||||
func `*`*(a, b: Stuint): Stuint =
|
func `*`*(a, b: Stuint): Stuint =
|
||||||
## Integer multiplication
|
## Integer multiplication
|
||||||
result.limbs.prod(a.limbs, b.limbs)
|
result.limbs.prod(a.limbs, b.limbs)
|
||||||
result.clearExtraBits()
|
result.clearExtraBitsOverMSB()
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
@ -228,5 +224,20 @@ func pow*[aBits, eBits](a: Stuint[aBits], e: Stuint[eBits]): Stuint[aBits] =
|
||||||
|
|
||||||
# Division & Modulo
|
# Division & Modulo
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
{.push raises: [], inline, noInit, gcsafe.}
|
||||||
|
|
||||||
export uint_div
|
func `div`*(x, y: Stuint): Stuint =
|
||||||
|
## Division operation for multi-precision unsigned uint
|
||||||
|
var tmp{.noInit.}: Stuint
|
||||||
|
divRem(result.limbs, tmp.limbs, x.limbs, y.limbs)
|
||||||
|
|
||||||
|
func `mod`*(x, y: Stuint): Stuint =
|
||||||
|
## Remainder operation for multi-precision unsigned uint
|
||||||
|
var tmp{.noInit.}: Stuint
|
||||||
|
divRem(tmp.limbs, result.limbs, x.limbs, y.limbs)
|
||||||
|
|
||||||
|
func divmod*(x, y: Stuint): tuple[quot, rem: Stuint] =
|
||||||
|
## Division and remainder operations for multi-precision unsigned uint
|
||||||
|
divRem(result.quot.limbs, result.rem.limbs, x.limbs, y.limbs)
|
||||||
|
|
||||||
|
{.pop.}
|
Loading…
Reference in New Issue