rebase and try to make it works with clients
This commit is contained in:
parent
bff69b3f98
commit
63a32129c8
14
stint.nim
14
stint.nim
|
@ -1,5 +1,5 @@
|
|||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Copyright 2018-2023 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)
|
||||
|
@ -10,12 +10,12 @@
|
|||
# import stint/[bitops2, endians2, intops, io, modular_arithmetic, literals_stint]
|
||||
# export bitops2, endians2, intops, io, modular_arithmetic, literals_stint
|
||||
|
||||
import stint/[io, uintops]
|
||||
export io, uintops
|
||||
import stint/[io, uintops, intops, literals_stint, modular_arithmetic]
|
||||
export io, uintops, intops, literals_stint, modular_arithmetic
|
||||
|
||||
type
|
||||
# Int128* = Stint[128]
|
||||
# Int256* = Stint[256]
|
||||
Int128* = Stint[128]
|
||||
Int256* = Stint[256]
|
||||
UInt128* = StUint[128]
|
||||
UInt256* = StUint[256]
|
||||
|
||||
|
@ -25,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*(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*(n: SomeInteger): Int256 {.inline.} = n.stint(256)
|
||||
# func i256*(s: string): Int256 {.inline.} = s.parse(Int256)
|
||||
|
|
|
@ -7,10 +7,10 @@ skipDirs = @["tests", "benchmarks"]
|
|||
### Dependencies
|
||||
|
||||
# TODO test only requirements don't work: https://github.com/nim-lang/nimble/issues/482
|
||||
requires "nim >= 1.6.0",
|
||||
requires "nim >= 1.6.12",
|
||||
"stew"
|
||||
|
||||
proc test(args, path: string) =
|
||||
proc test(name: string, lang: string = "c") =
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import private/datatypes
|
||||
|
||||
{.push raises: [IndexError], noInit, gcsafe.}
|
||||
{.push raises: [IndexDefect], noInit, gcsafe.}
|
||||
|
||||
# Serialization
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
@ -138,7 +138,7 @@ func toBytes*[bits: static int](x: StUint[bits], endian: Endianness = bigEndian)
|
|||
|
||||
func fromBytesBE*[bits: static int](
|
||||
T: typedesc[StUint[bits]],
|
||||
x: openArray[byte]): T =
|
||||
x: openArray[byte]): T {.raises: [], noInit, gcsafe.} =
|
||||
## Read big endian bytes and convert to an integer. At runtime, v must contain
|
||||
## at least sizeof(T) bytes. Native endianess is used which is not
|
||||
## portable! (i.e. use fixed-endian byte array or hex for serialization)
|
||||
|
|
|
@ -7,19 +7,18 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./private/[bitops2_priv, datatypes]
|
||||
import ./private/[datatypes]
|
||||
|
||||
export StInt, StUint
|
||||
export IntImpl, intImpl, UintImpl, uintImpl, bitsof # TODO: remove the need to export those
|
||||
export StInt
|
||||
#export IntImpl, intImpl, UintImpl, uintImpl, bitsof # TODO: remove the need to export those
|
||||
|
||||
type SomeBigInteger = StUint|StInt
|
||||
#import ./private/initialization
|
||||
|
||||
import ./private/initialization
|
||||
|
||||
func zero*[bits: static[int]](T: typedesc[StUint[bits] or StInt[bits]]): T {.inline.} =
|
||||
func zero*[bits: static[int]](T: typedesc[StInt[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.data = one(type result.data)
|
||||
|
@ -159,3 +158,4 @@ func pow*(x: StUint, y: StUint): StUint {.inline.} =
|
|||
result.data = x.data.pow(y.data)
|
||||
else:
|
||||
result.data = x.data ^ y.data
|
||||
]#
|
132
stint/io.nim
132
stint/io.nim
|
@ -1,5 +1,5 @@
|
|||
# Stint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Copyright 2018-2023 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)
|
||||
|
@ -21,6 +21,18 @@ import
|
|||
|
||||
from stew/byteutils import toHex # Why are we exporting readHexChar in byteutils?
|
||||
|
||||
template leastSignificantWord*(a: SomeBigInteger): Word =
|
||||
a.limbs[0]
|
||||
|
||||
template mostSignificantWord*(a: SomeBigInteger): Word =
|
||||
a.limbs[^1]
|
||||
|
||||
template signedWordType*(_: type SomeBigInteger): type =
|
||||
SignedWord
|
||||
|
||||
template wordType*(_: type SomeBigInteger): type =
|
||||
Word
|
||||
|
||||
template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
|
||||
# To avoid a costly runtime check, we refuse storing into StUint types smaller
|
||||
# than the input type.
|
||||
|
@ -62,14 +74,14 @@ func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
|
|||
func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T =
|
||||
stuint(a, result.bits)
|
||||
|
||||
func truncate*(num: StInt or StUint, T: typedesc[SomeInteger]): T {.inline.}=
|
||||
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.
|
||||
result = T(num.leastSignificantWord())
|
||||
|
||||
func toInt*(num: StInt or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}=
|
||||
func toInt*(num: Stint or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}=
|
||||
num.truncate(int)
|
||||
|
||||
func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
|
||||
|
@ -79,7 +91,7 @@ func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
|
|||
for i in 0 ..< result.len:
|
||||
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
|
||||
# ## current behavior is cast-like, copying bit pattern
|
||||
# ## or truncating if input does not fit into destination
|
||||
|
@ -87,12 +99,12 @@ func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
|
|||
# when N < bits:
|
||||
# when N <= 64:
|
||||
# type T = StUint[N]
|
||||
# result = stuint(convert[T](a).data, bits)
|
||||
# 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)
|
||||
# result = StUint(x.truncate(type(result.data)), bits)
|
||||
# else:
|
||||
# bigToSmall(result.data, a.data)
|
||||
# else:
|
||||
|
@ -143,7 +155,7 @@ func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
|
|||
|
||||
# func stint*(a: StUint, bits: static[int]): StInt[bits] {.inline.} =
|
||||
# const N = bitsof(a.data)
|
||||
# const dmax = stuint((type result).high, N)
|
||||
# const dmax = StUint((type result).high, N)
|
||||
# if a > dmax: raise newException(RangeError, "value out of range")
|
||||
# when N < bits:
|
||||
# when N <= 64:
|
||||
|
@ -170,8 +182,6 @@ func readHexChar(c: char): int8 {.inline.}=
|
|||
func skipPrefixes(current_idx: var int, str: string, radix: range[2..16]) {.inline.} =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
# Always called from a context where radix is known at compile-time
|
||||
# and checked within 2..16 and so cannot throw a RangeDefect at runtime
|
||||
|
||||
if str.len < 2:
|
||||
return
|
||||
|
@ -179,20 +189,14 @@ func skipPrefixes(current_idx: var int, str: string, radix: range[2..16]) {.inli
|
|||
doAssert current_idx == 0, "skipPrefixes only works for prefixes (position 0 and 1 of the string)"
|
||||
if str[0] == '0':
|
||||
if str[1] in {'x', 'X'}:
|
||||
if radix == 16:
|
||||
current_idx = 2
|
||||
else:
|
||||
raise newException(ValueError,"Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)")
|
||||
doAssert radix == 16, "Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)"
|
||||
current_idx = 2
|
||||
elif str[1] in {'o', 'O'}:
|
||||
if radix == 8:
|
||||
current_idx = 2
|
||||
else:
|
||||
raise newException(ValueError, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)")
|
||||
doAssert radix == 8, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)"
|
||||
current_idx = 2
|
||||
elif str[1] in {'b', 'B'}:
|
||||
if radix == 2:
|
||||
current_idx = 2
|
||||
elif radix != 16:
|
||||
raise newException(ValueError, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2) or as first byte of a hexadecimal number (base 16)")
|
||||
doAssert radix == 2, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2)"
|
||||
current_idx = 2
|
||||
|
||||
func nextNonBlank(current_idx: var int, s: string) {.inline.} =
|
||||
## Move the current index, skipping white spaces and "_" characters.
|
||||
|
@ -203,15 +207,13 @@ func nextNonBlank(current_idx: var int, s: string) {.inline.} =
|
|||
while current_idx < s.len and s[current_idx] in blanks:
|
||||
inc current_idx
|
||||
|
||||
func readDecChar(c: char): int {.inline.}=
|
||||
func readDecChar(c: range['0'..'9']): int {.inline.}=
|
||||
## Converts a decimal char to an int
|
||||
# specialization without branching for base <= 10.
|
||||
if c notin {'0'..'9'}:
|
||||
raise newException(ValueError, "Character out of '0'..'9' range")
|
||||
ord(c) - ord('0')
|
||||
|
||||
func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix: static[uint8] = 10): T =
|
||||
## Parse a string and store the result in a StInt[bits] or StUint[bits].
|
||||
## 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)
|
||||
|
@ -232,7 +234,7 @@ func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix:
|
|||
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].
|
||||
# ## 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)
|
||||
|
@ -241,12 +243,12 @@ func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix:
|
|||
# # and be much faster
|
||||
|
||||
# # For conversion we require overflowing operations (for example for negative hex numbers)
|
||||
# const base = radix.int8.stuint(bits)
|
||||
# const base = radix.int8.StUint(bits)
|
||||
|
||||
# var
|
||||
# curr = 0 # Current index in the string
|
||||
# isNeg = false
|
||||
# no_overflow: Stuint[bits]
|
||||
# no_overflow: StUint[bits]
|
||||
|
||||
# if input[curr] == '-':
|
||||
# doAssert radix == 10, "Negative numbers are only supported with base 10 input."
|
||||
|
@ -258,9 +260,9 @@ func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix:
|
|||
# while curr < input.len:
|
||||
# # TODO: overflow detection
|
||||
# when radix <= 10:
|
||||
# no_overflow = no_overflow * base + input[curr].readDecChar.stuint(bits)
|
||||
# no_overflow = no_overflow * base + input[curr].readDecChar.StUint(bits)
|
||||
# else:
|
||||
# no_overflow = no_overflow * base + input[curr].readHexChar.stuint(bits)
|
||||
# no_overflow = no_overflow * base + input[curr].readHexChar.StUint(bits)
|
||||
# nextNonBlank(curr, input)
|
||||
|
||||
# # TODO: we can't create the lowest int this way
|
||||
|
@ -269,7 +271,7 @@ func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix:
|
|||
# else:
|
||||
# result = convert[T](no_overflow)
|
||||
|
||||
func fromHex*(T: typedesc[StUint|StInt], s: string): T {.inline.} =
|
||||
func fromHex*(T: typedesc[StUint|Stint], s: string): T {.inline.} =
|
||||
## Convert an hex string to the corresponding unsigned integer
|
||||
parse(s, type result, radix = 16)
|
||||
|
||||
|
@ -277,34 +279,34 @@ 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 bits <= 64:
|
||||
result.add hexChars[r.leastSignificantWord()]
|
||||
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.
|
||||
# ## 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.
|
||||
|
@ -313,11 +315,11 @@ func hexToUint*[bits: static[int]](hexString: string): StUint[bits] {.inline.} =
|
|||
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
|
||||
|
||||
# const hexChars = "0123456789abcdef"
|
||||
# const base = radix.int8.stuint(bits)
|
||||
# const base = radix.int8.StUint(bits)
|
||||
|
||||
# result = ""
|
||||
|
||||
# type T = Stuint[bits]
|
||||
# type T = StUint[bits]
|
||||
# let isNeg = num.isNegative
|
||||
# let num = convert[T](if radix == 10 and isNeg: -num
|
||||
# else: num)
|
||||
|
@ -344,11 +346,11 @@ func hexToUint*[bits: static[int]](hexString: string): StUint[bits] {.inline.} =
|
|||
# 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*(a: Stint or StUint, order: static[Endianness] = bigEndian): string =
|
||||
## Stringify an int to hex.
|
||||
|
@ -365,7 +367,9 @@ func dumpHex*(a: Stint or StUint, order: static[Endianness] = bigEndian): string
|
|||
let bytes = a.toBytes(order)
|
||||
result = bytes.toHex()
|
||||
|
||||
func readUintBE*[bits: static[int]](ba: openarray[byte]): Stuint[bits] {.noInit, inline.}=
|
||||
export fromBytes, toBytes
|
||||
|
||||
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
|
||||
|
@ -386,3 +390,11 @@ template hash*(num: StUint|StInt): Hash =
|
|||
# `hashData` is not particularly efficient.
|
||||
# Explore better hashing solutions in nim-stew.
|
||||
hashData(unsafeAddr num, sizeof num)
|
||||
|
||||
func fromBytesBE*(T: type StUint, ba: openArray[byte], allowPadding: static[bool] = true): T {.noInit, inline.}=
|
||||
result = readUintBE[T.bits](ba)
|
||||
when allowPadding:
|
||||
result = result shl ((sizeof(T) - ba.len) * 8)
|
||||
|
||||
template initFromBytesBE*(x: var StUint, ba: openArray[byte], allowPadding: static[bool] = true) =
|
||||
x = fromBytesBE(type x, ba, allowPadding)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
## This file provides syntactic sugar to work with literals
|
||||
|
||||
import ./intops, macros
|
||||
import ./intops, ./uintops, macros
|
||||
|
||||
type Signedness = enum
|
||||
BothSigned, IntOnly, UintOnly
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./intops, private/datatypes
|
||||
import ./uintops, private/datatypes
|
||||
|
||||
func addmod_internal(a, b, m: StUint): StUint {.inline.}=
|
||||
## Modular addition
|
||||
|
|
|
@ -12,9 +12,13 @@ import
|
|||
stew/bitops2
|
||||
|
||||
when sizeof(int) == 8 and not defined(Stint32):
|
||||
type Word* = uint64
|
||||
type
|
||||
Word* = uint64
|
||||
SignedWord* = int64
|
||||
else:
|
||||
type Word* = uint32
|
||||
type
|
||||
Word* = uint32
|
||||
SignedWord* = int32
|
||||
|
||||
const WordBitWidth* = sizeof(Word) * 8
|
||||
|
||||
|
@ -33,10 +37,19 @@ type
|
|||
limbs*: array[bits.wordsRequired, Word]
|
||||
# Limbs-Endianess is little-endian
|
||||
|
||||
StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits]
|
||||
## Stack-based integer
|
||||
## Signed
|
||||
when (NimMajor, NimMinor) < (1,9):
|
||||
type
|
||||
StInt*[bits: static[int]] = object
|
||||
## Stack-based integer
|
||||
## Signed
|
||||
limbs*: array[bits.wordsRequired, Word]
|
||||
else:
|
||||
type
|
||||
StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits]
|
||||
## Stack-based integer
|
||||
## Signed
|
||||
|
||||
type
|
||||
Carry* = uint8 # distinct range[0'u8 .. 1]
|
||||
Borrow* = uint8 # distinct range[0'u8 .. 1]
|
||||
|
||||
|
@ -136,4 +149,4 @@ func copyWords*(
|
|||
for i in countdown(numWords-1, 0):
|
||||
a[startA+i] = b[startB+i]
|
||||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
|
|
|
@ -20,7 +20,7 @@ import
|
|||
func bitnot*(r: var StUint, a: Stuint) =
|
||||
## Bitwise complement of unsigned integer a
|
||||
## i.e. flips all bits of the input
|
||||
for i in 0 ..< r.len:
|
||||
for i in 0 ..< r.limbs.len:
|
||||
r[i] = not a[i]
|
||||
r.clearExtraBitsOverMSB()
|
||||
|
||||
|
@ -56,7 +56,7 @@ func leadingZeros*(a: Stuint): int =
|
|||
# Adjust when we use only part of the word size
|
||||
var extraBits = WordBitWidth * a.limbs.len - a.bits
|
||||
|
||||
for i in countdown(a.len-1, 0):
|
||||
for i in countdown(a.limbs.len-1, 0):
|
||||
let zeroCount = a.limbs[i].leadingZeros()
|
||||
if extraBits > 0:
|
||||
result += zeroCount - min(extraBits, WordBitWidth)
|
||||
|
|
|
@ -54,146 +54,6 @@ func shlAddMod_multi(a: var openArray[Word], c: Word,
|
|||
## Does a <- a * W + c (mod M)
|
||||
## and returns q = (a * W + c ) / M
|
||||
##
|
||||
<<<<<<< HEAD
|
||||
## For now only LittleEndian is implemented
|
||||
#
|
||||
# Resources at the bottom of the file
|
||||
|
||||
const
|
||||
qLen = q.limbs.len
|
||||
rLen = r.limbs.len
|
||||
uLen = u.limbs.len
|
||||
vLen = v.limbs.len
|
||||
|
||||
template `[]`(a: Stuint, i: int): Word = a.limbs[i]
|
||||
template `[]=`(a: Stuint, i: int, val: Word) = a.limbs[i] = val
|
||||
|
||||
# Find the most significant word with actual set bits
|
||||
# and get the leading zero count there
|
||||
var divisorLen = vLen
|
||||
var clz: int
|
||||
for w in mostToLeastSig(v):
|
||||
if w != 0:
|
||||
clz = leadingZeros(w)
|
||||
break
|
||||
else:
|
||||
divisorLen -= 1
|
||||
|
||||
doAssert divisorLen != 0, "Division by zero. Abandon ship!"
|
||||
|
||||
# Divisor is a single word.
|
||||
if divisorLen == 1:
|
||||
q.copyFrom(u)
|
||||
r.leastSignificantWord() = q.limbs.shortDiv(v.leastSignificantWord())
|
||||
# zero all but the least significant word
|
||||
var lsw = true
|
||||
for w in leastToMostSig(r):
|
||||
if lsw:
|
||||
lsw = false
|
||||
else:
|
||||
w = 0
|
||||
return
|
||||
|
||||
var un {.noInit.}: Limbs[uLen+1]
|
||||
var vn {.noInit.}: Limbs[vLen] # [mswLen .. vLen] range is unused
|
||||
|
||||
# Normalize so that the divisor MSB is set,
|
||||
# vn cannot overflow, un can overflowed by 1 word at most, hence uLen+1
|
||||
un.shlSmallOverflowing(u.limbs, clz)
|
||||
vn.shlSmall(v.limbs, clz)
|
||||
|
||||
static: doAssert cpuEndian == littleEndian, "Currently the division algorithm requires little endian ordering of the limbs"
|
||||
# TODO: is it worth it to have the uint be the exact same extended precision representation
|
||||
# as a wide int (say uint128 or uint256)?
|
||||
# in big-endian, the following loop must go the other way and the -1 must be +1
|
||||
|
||||
let vhi = vn[divisorLen-1]
|
||||
let vlo = vn[divisorLen-2]
|
||||
|
||||
for j in countdown(uLen - divisorLen, 0, 1):
|
||||
# Compute qhat estimate of q[j] (off by 0, 1 and rarely 2)
|
||||
var qhat, rhat: Word
|
||||
let uhi = un[j+divisorLen]
|
||||
let ulo = un[j+divisorLen-1]
|
||||
div2n1n(qhat, rhat, uhi, ulo, vhi)
|
||||
var mhi, mlo: Word
|
||||
var rhi, rlo: Word
|
||||
mul(mhi, mlo, qhat, vlo)
|
||||
rhi = rhat
|
||||
rlo = ulo
|
||||
|
||||
# if r < m, adjust approximation, up to twice
|
||||
while rhi < mhi or (rhi == mhi and rlo < mlo):
|
||||
qhat -= 1
|
||||
rhi += vhi
|
||||
|
||||
# Found the quotient
|
||||
q[j] = qhat
|
||||
|
||||
# un -= qhat * v
|
||||
var borrow = Borrow(0)
|
||||
var qvhi, qvlo: Word
|
||||
for i in 0 ..< divisorLen-1:
|
||||
mul(qvhi, qvlo, qhat, v[i])
|
||||
subB(borrow, un[j+i], un[j+i], qvlo, borrow)
|
||||
subB(borrow, un[j+i+1], un[j+i+1], qvhi, borrow)
|
||||
# Last step
|
||||
mul(qvhi, qvlo, qhat, v[divisorLen-1])
|
||||
subB(borrow, un[j+divisorLen-1], un[j+divisorLen-1], qvlo, borrow)
|
||||
qvhi += Word(borrow)
|
||||
let isNeg = un[j+divisorLen] < qvhi
|
||||
un[j+divisorLen] -= qvhi
|
||||
|
||||
if isNeg:
|
||||
# oops, too big by one, add back
|
||||
q[j] -= 1
|
||||
var carry = Carry(0)
|
||||
for i in 0 ..< divisorLen:
|
||||
addC(carry, un[j+i], un[j+i], v[i], carry)
|
||||
|
||||
# Quotient is found, if remainder is needed we need to un-normalize un
|
||||
if needRemainder:
|
||||
# r.limbs.shrSmall(un, clz) - TODO
|
||||
when cpuEndian == littleEndian:
|
||||
# rLen+1 == un.len
|
||||
for i in 0 ..< rLen:
|
||||
r[i] = (un[i] shr clz) or (un[i+1] shl (WordBitWidth - clz))
|
||||
else:
|
||||
{.error: "Not Implemented for bigEndian".}
|
||||
|
||||
|
||||
const BinaryShiftThreshold = 8 # If the difference in bit-length is below 8
|
||||
# binary shift is probably faster
|
||||
|
||||
func divmod(q, r: var Stuint,
|
||||
x, y: Stuint, needRemainder: bool) =
|
||||
|
||||
let x_clz = x.leadingZeros()
|
||||
let y_clz = y.leadingZeros()
|
||||
|
||||
# We short-circuit division depending on special-cases.
|
||||
if unlikely(y.isZero()):
|
||||
raise newException(DivByZeroError, "You attempted to divide by zero")
|
||||
elif y_clz == (y.bits - 1):
|
||||
# y is one
|
||||
q = x
|
||||
# elif (x.hi or y.hi).isZero:
|
||||
# # If computing just on the low part is enough
|
||||
# (result.quot.lo, result.rem.lo) = divmod(x.lo, y.lo, needRemainder)
|
||||
# elif (y and (y - one(type y))).isZero:
|
||||
# # y is a power of 2. (this also matches 0 but it was eliminated earlier)
|
||||
# # TODO. Would it be faster to use countTrailingZero (ctz) + clz == size(y) - 1?
|
||||
# # Especially because we shift by ctz after.
|
||||
# let y_ctz = bitsof(y) - y_clz - 1
|
||||
# result.quot = x shr y_ctz
|
||||
# if needRemainder:
|
||||
# result.rem = x and (y - one(type y))
|
||||
elif x == y:
|
||||
q.setOne()
|
||||
elif x < y:
|
||||
r = x
|
||||
# elif (y_clz - x_clz) < BinaryShiftThreshold:
|
||||
# binaryShiftDiv(x, y, result.quot, result.rem)
|
||||
## The modulus `M` most-significant bit at `mBits` MUST be set.
|
||||
|
||||
# Assuming 64-bit words
|
||||
|
@ -369,7 +229,7 @@ func divRem*(
|
|||
# - An Efficient Multiple-Precision Division Algorithm,
|
||||
# Liusheng Huang, Hong Zhong, Hong Shen, Yonglong Luo, 2005
|
||||
# https://ieeexplore.ieee.org/document/1579076
|
||||
#
|
||||
#
|
||||
# - Efficient multiple-precision integer division algorithm
|
||||
# Debapriyay Mukhopadhyaya, Subhas C.Nandy, 2014
|
||||
# https://www.sciencedirect.com/science/article/abs/pii/S0020019013002627
|
||||
|
|
|
@ -46,7 +46,7 @@ func one*[bits: static[int]](T: typedesc[Stuint[bits]]): T {.inline.} =
|
|||
result.setOne()
|
||||
|
||||
func high*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||
for i in 0 ..< result.len:
|
||||
for i in 0 ..< result.limbs.len:
|
||||
result[i] = high(Word)
|
||||
|
||||
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =
|
||||
|
|
Loading…
Reference in New Issue