From 63a32129c857a553b9b0be7d1cbfa9919097240c Mon Sep 17 00:00:00 2001 From: jangko Date: Fri, 9 Jun 2023 15:46:21 +0700 Subject: [PATCH] rebase and try to make it works with clients --- stint.nim | 14 ++-- stint.nimble | 4 +- stint/endians2.nim | 4 +- stint/intops.nim | 16 ++-- stint/io.nim | 132 ++++++++++++++++-------------- stint/literals_stint.nim | 2 +- stint/modular_arithmetic.nim | 2 +- stint/private/datatypes.nim | 25 ++++-- stint/private/uint_bitwise.nim | 4 +- stint/private/uint_div.nim | 142 +-------------------------------- stint/uintops.nim | 2 +- 11 files changed, 116 insertions(+), 231 deletions(-) diff --git a/stint.nim b/stint.nim index b6c02e7..efd52f3 100644 --- a/stint.nim +++ b/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) diff --git a/stint.nimble b/stint.nimble index 7195f13..e19546c 100644 --- a/stint.nimble +++ b/stint.nimble @@ -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" diff --git a/stint/endians2.nim b/stint/endians2.nim index 0730bea..3614940 100644 --- a/stint/endians2.nim +++ b/stint/endians2.nim @@ -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) diff --git a/stint/intops.nim b/stint/intops.nim index fa97d52..90ed0d2 100644 --- a/stint/intops.nim +++ b/stint/intops.nim @@ -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 +]# \ No newline at end of file diff --git a/stint/io.nim b/stint/io.nim index c80fc4f..c7ddd17 100644 --- a/stint/io.nim +++ b/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) diff --git a/stint/literals_stint.nim b/stint/literals_stint.nim index e09e1f8..40a8431 100644 --- a/stint/literals_stint.nim +++ b/stint/literals_stint.nim @@ -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 diff --git a/stint/modular_arithmetic.nim b/stint/modular_arithmetic.nim index 4eff437..d7f2914 100644 --- a/stint/modular_arithmetic.nim +++ b/stint/modular_arithmetic.nim @@ -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 diff --git a/stint/private/datatypes.nim b/stint/private/datatypes.nim index 1792304..5c04439 100644 --- a/stint/private/datatypes.nim +++ b/stint/private/datatypes.nim @@ -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.} \ No newline at end of file +{.pop.} diff --git a/stint/private/uint_bitwise.nim b/stint/private/uint_bitwise.nim index a3ce42b..598d88d 100644 --- a/stint/private/uint_bitwise.nim +++ b/stint/private/uint_bitwise.nim @@ -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) diff --git a/stint/private/uint_div.nim b/stint/private/uint_div.nim index 960d02e..c0a0798 100644 --- a/stint/private/uint_div.nim +++ b/stint/private/uint_div.nim @@ -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 diff --git a/stint/uintops.nim b/stint/uintops.nim index 681616f..3c406ef 100644 --- a/stint/uintops.nim +++ b/stint/uintops.nim @@ -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.} =