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
|
||||
- Ease of use:
|
||||
- Use traditional `+`, `-`, `+=`, etc operators like on native types
|
||||
- Representation of numbers in memory is the exact same as native types and endianness aware.
|
||||
- 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 raw byte BigInts (also called octet string in IETF specs)
|
||||
- converting to and from Hex
|
||||
- converting to and from decimal strings
|
||||
|
||||
|
|
|
@ -245,28 +245,3 @@ func fromBytes*[bits: static int](
|
|||
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, 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.}=
|
||||
## Converts an integer to an arbitrary precision integer.
|
||||
when cpuEndian == littleEndian:
|
||||
result.limbs[0] = Word(n)
|
||||
when sizeof(n) > sizeof(Word):
|
||||
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
|
||||
result.limbs[0] = Word(n)
|
||||
when sizeof(n) > sizeof(Word):
|
||||
result.limbs[1] = 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.
|
||||
#
|
||||
|
@ -88,8 +76,8 @@ 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
|
||||
for wr, wa in leastToMostSig(result, a):
|
||||
wr = wa
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = a[i]
|
||||
|
||||
# func stuint*(a: StInt, bits: static[int]): StUint[bits] {.inline.} =
|
||||
# ## 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)
|
||||
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.}=
|
||||
## 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 = endians2.fromBytesBE(Stuint[bits], ba)
|
||||
result = (typeof result).fromBytesBE(ba)
|
||||
|
||||
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
|
||||
|
@ -460,7 +379,7 @@ func toByteArrayBE*[bits: static[int]](n: StUint[bits]): array[bits div 8, byte]
|
|||
## - an unsigned integer
|
||||
## Returns:
|
||||
## - a big-endian array of the same size
|
||||
result = n.toBytes(bigEndian)
|
||||
result = n.toBytesBE()
|
||||
|
||||
template hash*(num: StUint|StInt): Hash =
|
||||
# TODO:
|
||||
|
|
|
@ -20,35 +20,22 @@ const WordBitWidth* = sizeof(Word) * 8
|
|||
|
||||
func wordsRequired*(bits: int): int {.compileTime.} =
|
||||
## Compute the number of limbs required
|
||||
## from the **announced** bit length
|
||||
## for the **announced** bit length
|
||||
(bits + WordBitWidth - 1) div WordBitWidth
|
||||
|
||||
type
|
||||
Limbs*[N: static int] = array[N, Word]
|
||||
## 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
|
||||
## Stack-based integer
|
||||
## Unsigned
|
||||
limbs*: array[bits.wordsRequired, Word]
|
||||
# TODO: using the limbs type here
|
||||
# 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]()
|
||||
# Limbs-Endianess is little-endian
|
||||
|
||||
StInt*[bits: static[int]] = object
|
||||
StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits]
|
||||
## Stack-based integer
|
||||
## Signed
|
||||
limbs*: array[bits.wordsRequired, Word]
|
||||
|
||||
Carry* = 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
|
||||
uint128*{.importc: "unsigned __int128".} = object
|
||||
|
||||
# Accessors
|
||||
# Bithacks
|
||||
# --------------------------------------------------------
|
||||
|
||||
template leastSignificantWord*(num: SomeInteger): auto =
|
||||
num
|
||||
{.push raises: [], inline, noInit, gcsafe.}
|
||||
|
||||
template leastSignificantWord*(a: SomeBigInteger): auto =
|
||||
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) =
|
||||
template clearExtraBitsOverMSB*(a: var StUint) =
|
||||
## A Stuint is stored in an array of 32 of 64-bit word
|
||||
## If we do bit manipulation at the word level,
|
||||
## 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:
|
||||
const posExtraBits = a.bits - (a.limbs.len-1) * WordBitWidth
|
||||
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
|
||||
# --------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
# --------------------------------------------------------
|
||||
{.push raises: [], inline, noInit, gcsafe.}
|
||||
|
||||
func copyFrom*(
|
||||
dst: var SomeBigInteger,
|
||||
src: SomeBigInteger
|
||||
){.inline.} =
|
||||
## Copy a BigInteger, truncated to 2^slen if the source
|
||||
## is larger than the destination
|
||||
when cpuEndian == littleEndian:
|
||||
for i in 0 ..< min(dst.limbs.len, src.limbs.len):
|
||||
dst.limbs[i] = src.limbs[i]
|
||||
for i in src.limbs.len ..< dst.limbs.len:
|
||||
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]
|
||||
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]
|
||||
|
||||
{.pop.}
|
|
@ -19,40 +19,31 @@ import
|
|||
func sum*(r: var Stuint, a, b: Stuint) =
|
||||
## Addition for multi-precision unsigned int
|
||||
var carry = Carry(0)
|
||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
||||
addC(carry, wr, wa, wb, carry)
|
||||
r.clearExtraBits()
|
||||
for i in 0 ..< r.limbs.len:
|
||||
addC(carry, r[i], a[i], b[i], carry)
|
||||
r.clearExtraBitsOverMSB()
|
||||
|
||||
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)
|
||||
a.clearExtraBits()
|
||||
a.sum(a, b)
|
||||
|
||||
func diff*(r: var Stuint, a, b: Stuint) =
|
||||
## Substraction for multi-precision unsigned int
|
||||
var borrow = Borrow(0)
|
||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
||||
subB(borrow, wr, wa, wb, borrow)
|
||||
r.clearExtraBits()
|
||||
for i in 0 ..< r.limbs.len:
|
||||
subB(borrow, r[i], a[i], b[i], borrow)
|
||||
r.clearExtraBitsOverMSB()
|
||||
|
||||
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)
|
||||
a.clearExtraBits()
|
||||
a.diff(a, b)
|
||||
|
||||
func inc*(a: var Stuint, w: Word = 1) =
|
||||
var carry = Carry(0)
|
||||
when cpuEndian == littleEndian:
|
||||
addC(carry, a.limbs[0], a.limbs[0], w, carry)
|
||||
for i in 1 ..< a.limbs.len:
|
||||
addC(carry, a.limbs[i], a.limbs[i], 0, carry)
|
||||
else:
|
||||
{.error: "Not implemented.".}
|
||||
a.clearExtraBits()
|
||||
addC(carry, a.limbs[0], a.limbs[0], w, carry)
|
||||
for i in 1 ..< a.limbs.len:
|
||||
addC(carry, a.limbs[i], a.limbs[i], 0, carry)
|
||||
a.clearExtraBitsOverMSB()
|
||||
|
||||
func sum*(r: var Stuint, a: Stuint, b: SomeUnsignedInt) =
|
||||
## Addition for multi-precision unsigned int
|
||||
|
|
|
@ -20,30 +20,30 @@ import
|
|||
func bitnot*(r: var StUint, a: Stuint) =
|
||||
## Bitwise complement of unsigned integer a
|
||||
## i.e. flips all bits of the input
|
||||
for wr, wa in leastToMostSig(r, a):
|
||||
wr = not wa
|
||||
r.clearExtraBits()
|
||||
for i in 0 ..< r.len:
|
||||
r[i] = not a[i]
|
||||
r.clearExtraBitsOverMSB()
|
||||
|
||||
func bitor*(r: var Stuint, a, b: Stuint) =
|
||||
## `Bitwise or` of numbers a and b
|
||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
||||
wr = wa or wb
|
||||
for i in 0 ..< r.limbs.len:
|
||||
r[i] = a[i] or b[i]
|
||||
|
||||
func bitand*(r: var Stuint, a, b: Stuint) =
|
||||
## `Bitwise and` of numbers a and b
|
||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
||||
wr = wa and wb
|
||||
for i in 0 ..< r.limbs.len:
|
||||
r[i] = a[i] and b[i]
|
||||
|
||||
func bitxor*(r: var Stuint, a, b: Stuint) =
|
||||
## `Bitwise xor` of numbers x and y
|
||||
for wr, wa, wb in leastToMostSig(r, a, b):
|
||||
wr = wa xor wb
|
||||
r.clearExtraBits()
|
||||
for i in 0 ..< r.limbs.len:
|
||||
r[i] = a[i] xor b[i]
|
||||
r.clearExtraBitsOverMSB()
|
||||
|
||||
func countOnes*(a: Stuint): int =
|
||||
result = 0
|
||||
for wa in leastToMostSig(a):
|
||||
result += countOnes(wa)
|
||||
for i in 0 ..< a.limbs.len:
|
||||
result += countOnes(a[i])
|
||||
|
||||
func parity*(a: Stuint): int =
|
||||
result = parity(a.limbs[0])
|
||||
|
@ -56,8 +56,8 @@ func leadingZeros*(a: Stuint): int =
|
|||
# Adjust when we use only part of the word size
|
||||
var extraBits = WordBitWidth * a.limbs.len - a.bits
|
||||
|
||||
for word in mostToLeastSig(a):
|
||||
let zeroCount = word.leadingZeros()
|
||||
for i in countdown(a.len-1, 0):
|
||||
let zeroCount = a.limbs[i].leadingZeros()
|
||||
if extraBits > 0:
|
||||
result += zeroCount - min(extraBits, WordBitWidth)
|
||||
extraBits -= WordBitWidth
|
||||
|
@ -68,8 +68,8 @@ func leadingZeros*(a: Stuint): int =
|
|||
|
||||
func trailingZeros*(a: Stuint): int =
|
||||
result = 0
|
||||
for word in leastToMostSig(a):
|
||||
let zeroCount = word.trailingZeros()
|
||||
for i in 0 ..< a.limbs.len:
|
||||
let zeroCount = a[i].trailingZeros()
|
||||
result += zeroCount
|
||||
if zeroCount != WordBitWidth:
|
||||
break
|
||||
|
|
|
@ -15,31 +15,6 @@ import
|
|||
./uint_bitwise,
|
||||
./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
|
||||
# --------------------------------------------------------
|
||||
|
||||
|
@ -312,7 +287,7 @@ func shlAddMod(a: var openArray[Word], c: Word,
|
|||
else:
|
||||
return shlAddMod_multi(a, c, M, mBits)
|
||||
|
||||
func divRemImpl(
|
||||
func divRem*(
|
||||
q, r: var openArray[Word],
|
||||
a, b: openArray[Word]
|
||||
) =
|
||||
|
@ -350,20 +325,6 @@ func divRemImpl(
|
|||
for i in rLen ..< r.len:
|
||||
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
|
||||
#
|
||||
|
|
|
@ -25,14 +25,9 @@ func shrSmall*(r: var Limbs, a: Limbs, k: SomeInteger) =
|
|||
# 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.len-1:
|
||||
r[i] = (a[i] shr k) or (a[i+1] shl (WordBitWidth - 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
|
||||
for i in 0 ..< a.len-1:
|
||||
r[i] = (a[i] shr k) or (a[i+1] shl (WordBitWidth - k))
|
||||
r[^1] = a[^1] shr k
|
||||
|
||||
func shrLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||
## 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:
|
||||
return
|
||||
|
||||
when cpuEndian == littleEndian:
|
||||
for i in w ..< a.len-1:
|
||||
r[i-w] = (a[i] shr shift) or (a[i+1] shl (WordBitWidth - 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
|
||||
for i in w ..< a.len-1:
|
||||
r[i-w] = (a[i] shr shift) or (a[i+1] shl (WordBitWidth - shift))
|
||||
r[^(1+w)] = a[^1] shr shift
|
||||
|
||||
func shrWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
||||
## Shift right by w word
|
||||
when cpuEndian == littleEndian:
|
||||
for i in 0 ..< Limbs.len-w:
|
||||
r[i] = a[i+w]
|
||||
for i in Limbs.len-w ..< Limbs.len:
|
||||
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]
|
||||
for i in 0 ..< Limbs.len-w:
|
||||
r[i] = a[i+w]
|
||||
for i in Limbs.len-w ..< Limbs.len:
|
||||
r[i] = 0
|
||||
|
||||
func shlSmall*(r: var Limbs, a: Limbs, k: SomeInteger) =
|
||||
## Compute the `shift left` operation of x and k
|
||||
##
|
||||
## k MUST be less than the base word size (2^32 or 2^64)
|
||||
when cpuEndian == littleEndian:
|
||||
r[0] = a[0] shl k
|
||||
for i in 1 ..< a.len:
|
||||
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))
|
||||
r[0] = a[0] shl k
|
||||
for i in 1 ..< a.len:
|
||||
r[i] = (a[i] shl k) or (a[i-1] shr (WordBitWidth - k))
|
||||
|
||||
func shlLarge*(r: var Limbs, a: Limbs, w, shift: SomeInteger) =
|
||||
## 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:
|
||||
return
|
||||
|
||||
when cpuEndian == littleEndian:
|
||||
r[w] = a[0] shl shift
|
||||
for i in 1+w ..< r.len:
|
||||
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))
|
||||
r[w] = a[0] shl shift
|
||||
for i in 1+w ..< r.len:
|
||||
r[i] = (a[i-w] shl shift) or (a[i-w-1] shr (WordBitWidth - shift))
|
||||
|
||||
func shlWords*(r: var Limbs, a: Limbs, w: SomeInteger) =
|
||||
## Shift left by w word
|
||||
when cpuEndian == littleEndian:
|
||||
for i in 0 ..< w:
|
||||
r[i] = 0
|
||||
for i in 0 ..< Limbs.len-w:
|
||||
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]
|
||||
for i in 0 ..< w:
|
||||
r[i] = 0
|
||||
for i in 0 ..< Limbs.len-w:
|
||||
r[i+w] = a[i]
|
||||
|
||||
# Wrappers
|
||||
# --------------------------------------------------------
|
||||
|
@ -133,7 +101,7 @@ func shiftLeft*(r: var Stuint, a: Stuint, k: SomeInteger) =
|
|||
|
||||
if k < WordBitWidth:
|
||||
r.limbs.shlSmall(a.limbs, k)
|
||||
r.clearExtraBits()
|
||||
r.clearExtraBitsOverMSB()
|
||||
return
|
||||
|
||||
# w = k div WordBitWidth, shift = k mod WordBitWidth
|
||||
|
@ -145,4 +113,4 @@ func shiftLeft*(r: var Stuint, a: Stuint, k: SomeInteger) =
|
|||
else:
|
||||
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) =
|
||||
## Set ``a`` to k
|
||||
when cpuEndian == littleEndian:
|
||||
a.limbs[0] = k
|
||||
for i in 1 ..< a.limbs.len:
|
||||
a.limbs[i] = 0
|
||||
else:
|
||||
a.limbs[^1] = k
|
||||
for i in 0 ..< a.limb.len - 1:
|
||||
a.limbs[i] = 0
|
||||
a.limbs[0] = k
|
||||
for i in 1 ..< a.limbs.len:
|
||||
a.limbs[i] = 0
|
||||
|
||||
func setOne*(a: var StUint) =
|
||||
setSmallInt(a, 1)
|
||||
|
@ -51,8 +46,9 @@ func one*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
|||
result.setOne()
|
||||
|
||||
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||
for wr in leastToMostSig(result):
|
||||
wr = high(Word)
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = high(Word)
|
||||
|
||||
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||
discard
|
||||
|
||||
|
@ -62,15 +58,15 @@ func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
|||
{.push raises: [], inline, noInit, gcsafe.}
|
||||
|
||||
func isZero*(a: Stuint): bool =
|
||||
for word in leastToMostSig(a):
|
||||
if word != 0:
|
||||
for i in 0 ..< a.limbs.len:
|
||||
if a[i] != 0:
|
||||
return false
|
||||
return true
|
||||
|
||||
func `==`*(a, b: Stuint): bool {.inline.} =
|
||||
## Unsigned `equal` comparison
|
||||
for wa, wb in leastToMostSig(a, b):
|
||||
if wa != wb:
|
||||
for i in 0 ..< a.limbs.len:
|
||||
if a[i] != b[i]:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
@ -78,8 +74,8 @@ 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)
|
||||
for i in 0 ..< a.limbs.len:
|
||||
subB(borrow, diff, a[i], b[i], borrow)
|
||||
return bool(borrow)
|
||||
|
||||
func `<=`*(a, b: Stuint): bool {.inline.} =
|
||||
|
@ -89,12 +85,12 @@ func `<=`*(a, b: Stuint): bool {.inline.} =
|
|||
func isOdd*(a: Stuint): bool {.inline.} =
|
||||
## Returns true if input is off
|
||||
## false otherwise
|
||||
bool(a.leastSignificantWord and 1)
|
||||
bool(a[0] and 1)
|
||||
|
||||
func isEven*(a: Stuint): bool {.inline.} =
|
||||
## Returns true if input is zero
|
||||
## false otherwise
|
||||
not a.isOdd
|
||||
not a.isOdd()
|
||||
|
||||
{.pop.}
|
||||
# Bitwise operations
|
||||
|
@ -178,7 +174,7 @@ export `+=`
|
|||
func `*`*(a, b: Stuint): Stuint =
|
||||
## Integer multiplication
|
||||
result.limbs.prod(a.limbs, b.limbs)
|
||||
result.clearExtraBits()
|
||||
result.clearExtraBitsOverMSB()
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
@ -228,5 +224,20 @@ func pow*[aBits, eBits](a: Stuint[aBits], e: Stuint[eBits]): Stuint[aBits] =
|
|||
|
||||
# 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