rebase and try to make it works with clients

This commit is contained in:
jangko 2023-06-09 15:46:21 +07:00
parent bff69b3f98
commit 63a32129c8
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
11 changed files with 116 additions and 231 deletions

View File

@ -1,5 +1,5 @@
# Stint # Stint
# Copyright 2018 Status Research & Development GmbH # Copyright 2018-2023 Status Research & Development GmbH
# Licensed under either of # Licensed under either of
# #
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) # * 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] # import stint/[bitops2, endians2, intops, io, modular_arithmetic, literals_stint]
# export bitops2, endians2, intops, io, modular_arithmetic, literals_stint # export bitops2, endians2, intops, io, modular_arithmetic, literals_stint
import stint/[io, uintops] import stint/[io, uintops, intops, literals_stint, modular_arithmetic]
export io, uintops export io, uintops, intops, literals_stint, modular_arithmetic
type type
# Int128* = Stint[128] Int128* = Stint[128]
# Int256* = Stint[256] Int256* = Stint[256]
UInt128* = StUint[128] UInt128* = StUint[128]
UInt256* = StUint[256] 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*(n: SomeInteger): UInt256 {.inline.} = n.stuint(256)
func u256*(s: string): UInt256 {.inline.} = s.parse(UInt256) 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 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) # func i256*(s: string): Int256 {.inline.} = s.parse(Int256)

View File

@ -7,10 +7,10 @@ skipDirs = @["tests", "benchmarks"]
### Dependencies ### Dependencies
# TODO test only requirements don't work: https://github.com/nim-lang/nimble/issues/482 # 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" "stew"
proc test(args, path: string) = proc test(name: string, lang: string = "c") =
if not dirExists "build": if not dirExists "build":
mkDir "build" mkDir "build"

View File

@ -9,7 +9,7 @@
import private/datatypes import private/datatypes
{.push raises: [IndexError], noInit, gcsafe.} {.push raises: [IndexDefect], noInit, gcsafe.}
# Serialization # Serialization
# ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------
@ -138,7 +138,7 @@ func toBytes*[bits: static int](x: StUint[bits], endian: Endianness = bigEndian)
func fromBytesBE*[bits: static int]( func fromBytesBE*[bits: static int](
T: typedesc[StUint[bits]], 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 ## 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 ## at least sizeof(T) bytes. Native endianess is used which is not
## portable! (i.e. use fixed-endian byte array or hex for serialization) ## portable! (i.e. use fixed-endian byte array or hex for serialization)

View File

@ -7,19 +7,18 @@
# #
# at your option. This file may not be copied, modified, or distributed except according to those terms. # 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 StInt
export IntImpl, intImpl, UintImpl, uintImpl, bitsof # TODO: remove the need to export those #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[StInt[bits]]): T {.inline.} =
func zero*[bits: static[int]](T: typedesc[StUint[bits] or StInt[bits]]): T {.inline.} =
## Returns the zero of the input type ## Returns the zero of the input type
discard discard
#[
func one*[bits: static[int]](T: typedesc[StUint[bits]]): T {.inline.} = func one*[bits: static[int]](T: typedesc[StUint[bits]]): T {.inline.} =
## Returns the one of the input type ## Returns the one of the input type
result.data = one(type result.data) 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) result.data = x.data.pow(y.data)
else: else:
result.data = x.data ^ y.data result.data = x.data ^ y.data
]#

View File

@ -1,5 +1,5 @@
# Stint # Stint
# Copyright 2018 Status Research & Development GmbH # Copyright 2018-2023 Status Research & Development GmbH
# Licensed under either of # Licensed under either of
# #
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) # * 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? 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]) = template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
# To avoid a costly runtime check, we refuse storing into StUint types smaller # To avoid a costly runtime check, we refuse storing into StUint types smaller
# than the input type. # 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 = func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T =
stuint(a, result.bits) 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. ## 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. ## 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 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. ## For signed result type, result is undefined if input does not fit in the target type.
result = T(num.leastSignificantWord()) 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) num.truncate(int)
func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} = 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: for i in 0 ..< result.len:
result[i] = a[i] 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
# ## current behavior is cast-like, copying bit pattern # ## current behavior is cast-like, copying bit pattern
# ## or truncating if input does not fit into destination # ## 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 < bits:
# when N <= 64: # when N <= 64:
# type T = StUint[N] # type T = StUint[N]
# result = stuint(convert[T](a).data, bits) # result = StUint(convert[T](a).data, bits)
# else: # else:
# smallToBig(result.data, a.data) # smallToBig(result.data, a.data)
# elif N > bits: # elif N > bits:
# when bits <= 64: # when bits <= 64:
# result = stuint(x.truncate(type(result.data)), bits) # result = StUint(x.truncate(type(result.data)), bits)
# else: # else:
# bigToSmall(result.data, a.data) # bigToSmall(result.data, a.data)
# else: # 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.} = # func stint*(a: StUint, bits: static[int]): StInt[bits] {.inline.} =
# const N = bitsof(a.data) # 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") # if a > dmax: raise newException(RangeError, "value out of range")
# when N < bits: # when N < bits:
# when N <= 64: # 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.} = 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 ## Returns the index of the first meaningful char in `hexStr` by skipping
## "0x" prefix ## "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: if str.len < 2:
return 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)" doAssert current_idx == 0, "skipPrefixes only works for prefixes (position 0 and 1 of the string)"
if str[0] == '0': if str[0] == '0':
if str[1] in {'x', 'X'}: if str[1] in {'x', 'X'}:
if radix == 16: doAssert radix == 16, "Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)"
current_idx = 2 current_idx = 2
else:
raise newException(ValueError,"Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)")
elif str[1] in {'o', 'O'}: elif str[1] in {'o', 'O'}:
if radix == 8: doAssert radix == 8, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)"
current_idx = 2 current_idx = 2
else:
raise newException(ValueError, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)")
elif str[1] in {'b', 'B'}: elif str[1] in {'b', 'B'}:
if radix == 2: doAssert radix == 2, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2)"
current_idx = 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)")
func nextNonBlank(current_idx: var int, s: string) {.inline.} = func nextNonBlank(current_idx: var int, s: string) {.inline.} =
## Move the current index, skipping white spaces and "_" characters. ## 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: while current_idx < s.len and s[current_idx] in blanks:
inc current_idx inc current_idx
func readDecChar(c: char): int {.inline.}= func readDecChar(c: range['0'..'9']): int {.inline.}=
## Converts a decimal char to an int ## Converts a decimal char to an int
# specialization without branching for base <= 10. # specialization without branching for base <= 10.
if c notin {'0'..'9'}:
raise newException(ValueError, "Character out of '0'..'9' range")
ord(c) - ord('0') ord(c) - ord('0')
func parse*[bits: static[int]](input: string, T: typedesc[StUint[bits]], radix: static[uint8] = 10): T = 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" static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26) # TODO: 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) nextNonBlank(curr, input)
# func parse*[bits: static[int]](input: string, T: typedesc[Stint[bits]], radix: static[int8] = 10): T = # 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" # static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26) # # TODO: 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 # # and be much faster
# # For conversion we require overflowing operations (for example for negative hex numbers) # # 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 # var
# curr = 0 # Current index in the string # curr = 0 # Current index in the string
# isNeg = false # isNeg = false
# no_overflow: Stuint[bits] # no_overflow: StUint[bits]
# if input[curr] == '-': # if input[curr] == '-':
# doAssert radix == 10, "Negative numbers are only supported with base 10 input." # 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: # while curr < input.len:
# # TODO: overflow detection # # TODO: overflow detection
# when radix <= 10: # when radix <= 10:
# no_overflow = no_overflow * base + input[curr].readDecChar.stuint(bits) # no_overflow = no_overflow * base + input[curr].readDecChar.StUint(bits)
# else: # else:
# no_overflow = no_overflow * base + input[curr].readHexChar.stuint(bits) # no_overflow = no_overflow * base + input[curr].readHexChar.StUint(bits)
# nextNonBlank(curr, input) # nextNonBlank(curr, input)
# # TODO: we can't create the lowest int this way # # 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: # else:
# result = convert[T](no_overflow) # 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 ## Convert an hex string to the corresponding unsigned integer
parse(s, type result, radix = 16) 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 ## Convert an hex string to the corresponding unsigned integer
parse(hexString, type result, radix = 16) parse(hexString, type result, radix = 16)
# func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10): string = func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10): string =
# ## Convert a Stint or Stuint to string. ## Convert a Stint or StUint to string.
# ## In case of negative numbers: ## In case of negative numbers:
# ## - they are prefixed with "-" for base 10. ## - they are prefixed with "-" for base 10.
# ## - if not base 10, they are returned raw in two-complement form. ## - 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" static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported"
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26) # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# const hexChars = "0123456789abcdef" const hexChars = "0123456789abcdef"
# const base = radix.uint8.stuint(bits) const base = radix.uint8.stuint(bits)
# result = "" result = ""
# var (q, r) = divmod(num, base) var (q, r) = divmod(num, base)
# while true: while true:
# when bitsof(r.data) <= 64: when bits <= 64:
# result.add hexChars[r.data.int] result.add hexChars[r.leastSignificantWord()]
# else: else:
# result.add hexChars[r.truncate(int)] result.add hexChars[r.truncate(int)]
# if q.isZero: if q.isZero:
# break break
# (q, r) = divmod(q, base) (q, r) = divmod(q, base)
# reverse(result) reverse(result)
# func toString*[bits: static[int]](num: Stint[bits], radix: static[int8] = 10): string = # 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: # ## In case of negative numbers:
# ## - they are prefixed with "-" for base 10. # ## - they are prefixed with "-" for base 10.
# ## - if not base 10, they are returned raw in two-complement form. # ## - 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) # # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26)
# const hexChars = "0123456789abcdef" # const hexChars = "0123456789abcdef"
# const base = radix.int8.stuint(bits) # const base = radix.int8.StUint(bits)
# result = "" # result = ""
# type T = Stuint[bits] # type T = StUint[bits]
# let isNeg = num.isNegative # let isNeg = num.isNegative
# let num = convert[T](if radix == 10 and isNeg: -num # let num = convert[T](if radix == 10 and isNeg: -num
# else: num) # else: num)
@ -344,11 +346,11 @@ func hexToUint*[bits: static[int]](hexString: string): StUint[bits] {.inline.} =
# else: # else:
# toString(num, 10) # toString(num, 10)
# func toHex*[bits: static[int]](num: Stint[bits] or StUint[bits]): string {.inline.}= func toHex*[bits: static[int]](num: Stint[bits] or StUint[bits]): string {.inline.}=
# ## Convert to a hex string. ## Convert to a hex string.
# ## Output is considered a big-endian base 16 string. ## Output is considered a big-endian base 16 string.
# ## Leading zeros are stripped. Use dumpHex instead if you need the in-memory representation ## Leading zeros are stripped. Use dumpHex instead if you need the in-memory representation
# toString(num, 16) toString(num, 16)
func dumpHex*(a: Stint or StUint, order: static[Endianness] = bigEndian): string = func dumpHex*(a: Stint or StUint, order: static[Endianness] = bigEndian): string =
## Stringify an int to hex. ## 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) let bytes = a.toBytes(order)
result = bytes.toHex() 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) ## 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
@ -386,3 +390,11 @@ template hash*(num: StUint|StInt): Hash =
# `hashData` is not particularly efficient. # `hashData` is not particularly efficient.
# Explore better hashing solutions in nim-stew. # Explore better hashing solutions in nim-stew.
hashData(unsafeAddr num, sizeof num) 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)

View File

@ -9,7 +9,7 @@
## This file provides syntactic sugar to work with literals ## This file provides syntactic sugar to work with literals
import ./intops, macros import ./intops, ./uintops, macros
type Signedness = enum type Signedness = enum
BothSigned, IntOnly, UintOnly BothSigned, IntOnly, UintOnly

View File

@ -7,7 +7,7 @@
# #
# at your option. This file may not be copied, modified, or distributed except according to those terms. # 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.}= func addmod_internal(a, b, m: StUint): StUint {.inline.}=
## Modular addition ## Modular addition

View File

@ -12,9 +12,13 @@ import
stew/bitops2 stew/bitops2
when sizeof(int) == 8 and not defined(Stint32): when sizeof(int) == 8 and not defined(Stint32):
type Word* = uint64 type
Word* = uint64
SignedWord* = int64
else: else:
type Word* = uint32 type
Word* = uint32
SignedWord* = int32
const WordBitWidth* = sizeof(Word) * 8 const WordBitWidth* = sizeof(Word) * 8
@ -33,10 +37,19 @@ type
limbs*: array[bits.wordsRequired, Word] limbs*: array[bits.wordsRequired, Word]
# Limbs-Endianess is little-endian # Limbs-Endianess is little-endian
StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits] when (NimMajor, NimMinor) < (1,9):
## Stack-based integer type
## Signed 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] Carry* = uint8 # distinct range[0'u8 .. 1]
Borrow* = uint8 # distinct range[0'u8 .. 1] Borrow* = uint8 # distinct range[0'u8 .. 1]

View File

@ -20,7 +20,7 @@ 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 i in 0 ..< r.len: for i in 0 ..< r.limbs.len:
r[i] = not a[i] r[i] = not a[i]
r.clearExtraBitsOverMSB() r.clearExtraBitsOverMSB()
@ -56,7 +56,7 @@ 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 i in countdown(a.len-1, 0): for i in countdown(a.limbs.len-1, 0):
let zeroCount = a.limbs[i].leadingZeros() let zeroCount = a.limbs[i].leadingZeros()
if extraBits > 0: if extraBits > 0:
result += zeroCount - min(extraBits, WordBitWidth) result += zeroCount - min(extraBits, WordBitWidth)

View File

@ -54,146 +54,6 @@ func shlAddMod_multi(a: var openArray[Word], c: Word,
## Does a <- a * W + c (mod M) ## Does a <- a * W + c (mod M)
## and returns q = (a * W + c ) / 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. ## The modulus `M` most-significant bit at `mBits` MUST be set.
# Assuming 64-bit words # Assuming 64-bit words

View File

@ -46,7 +46,7 @@ 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 i in 0 ..< result.len: for i in 0 ..< result.limbs.len:
result[i] = high(Word) result[i] = high(Word)
func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} = func low*[bits](_: typedesc[Stuint[bits]]): Stuint[bits] {.inline.} =