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:
Mamy Ratsimbazafy 2022-01-23 22:45:47 +01:00 committed by jangko
parent 7efa2483e4
commit 4660dfe4a4
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
9 changed files with 132 additions and 369 deletions

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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.}

View File

@ -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

View File

@ -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

View File

@ -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
#

View File

@ -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()

View File

@ -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.}