2018-04-25 19:21:58 +00:00
|
|
|
# Stint
|
2018-03-26 09:46:24 +00:00
|
|
|
# Copyright 2018 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)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
|
|
#
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2020-06-12 16:37:02 +00:00
|
|
|
import
|
|
|
|
# Status lib
|
|
|
|
stew/bitops2
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-12 16:37:02 +00:00
|
|
|
when sizeof(int) == 8 and not defined(Stint32):
|
|
|
|
type Word* = uint64
|
2018-03-26 09:46:24 +00:00
|
|
|
else:
|
2020-06-12 16:37:02 +00:00
|
|
|
type Word* = uint32
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-12 17:23:03 +00:00
|
|
|
const WordBitWidth* = sizeof(Word) * 8
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-12 16:37:02 +00:00
|
|
|
func wordsRequired*(bits: int): int {.compileTime.} =
|
|
|
|
## Compute the number of limbs required
|
|
|
|
## from the **announced** bit length
|
|
|
|
(bits + WordBitWidth - 1) div WordBitWidth
|
2018-04-21 10:12:05 +00:00
|
|
|
|
2018-03-26 09:46:24 +00:00
|
|
|
type
|
2020-06-12 17:01:05 +00:00
|
|
|
Limbs*[N: static int] = array[N, Word]
|
2020-06-13 10:29:31 +00:00
|
|
|
## 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]
|
2018-03-26 09:46:24 +00:00
|
|
|
|
2018-04-25 10:52:00 +00:00
|
|
|
StUint*[bits: static[int]] = object
|
2020-06-12 16:37:02 +00:00
|
|
|
## Stack-based integer
|
|
|
|
## Unsigned
|
2020-06-13 10:29:31 +00:00
|
|
|
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]()
|
2018-04-25 12:27:55 +00:00
|
|
|
|
|
|
|
StInt*[bits: static[int]] = object
|
2020-06-12 16:37:02 +00:00
|
|
|
## Stack-based integer
|
|
|
|
## Signed
|
2020-06-13 10:29:31 +00:00
|
|
|
limbs*: array[bits.wordsRequired, Word]
|
2020-06-12 16:37:02 +00:00
|
|
|
|
|
|
|
Carry* = uint8 # distinct range[0'u8 .. 1]
|
|
|
|
Borrow* = uint8 # distinct range[0'u8 .. 1]
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
SomeBigInteger*[bits: static[int]] = Stuint[bits]|Stint[bits]
|
|
|
|
|
2020-06-12 16:37:02 +00:00
|
|
|
const GCC_Compatible* = defined(gcc) or defined(clang) or defined(llvm_gcc)
|
|
|
|
const X86* = defined(amd64) or defined(i386)
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-12 16:37:02 +00:00
|
|
|
when sizeof(int) == 8 and GCC_Compatible:
|
|
|
|
type
|
|
|
|
uint128*{.importc: "unsigned __int128".} = object
|
2022-04-07 07:52:36 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
# Accessors
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
2019-10-08 13:11:08 +00:00
|
|
|
template leastSignificantWord*(num: SomeInteger): auto =
|
|
|
|
num
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-13 10:29:31 +00:00
|
|
|
template leastSignificantWord*(a: SomeBigInteger): auto =
|
2020-06-12 16:37:02 +00:00
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
a.limbs[0]
|
2018-10-25 10:58:40 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
a.limbs[^1]
|
2018-10-25 10:58:40 +00:00
|
|
|
|
2020-06-13 10:29:31 +00:00
|
|
|
template mostSignificantWord*(a: SomeBigInteger): auto =
|
2020-06-12 16:37:02 +00:00
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
a.limbs[^1]
|
2018-10-25 10:58:40 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
a.limbs[0]
|
2020-06-12 17:01:05 +00:00
|
|
|
|
2021-02-21 19:21:56 +00:00
|
|
|
template clearExtraBits*(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
|
|
|
|
## we need to clear the upper 56-bit
|
|
|
|
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
|
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
# Iterations
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
|
|
|
iterator leastToMostSig*(a: SomeBigInteger): Word =
|
2020-06-12 17:01:05 +00:00
|
|
|
## Iterate from least to most significant word
|
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in 0 ..< a.limbs.len:
|
|
|
|
yield a.limbs[i]
|
2020-06-12 17:01:05 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in countdown(a.limbs.len-1, 0):
|
|
|
|
yield a.limbs[i]
|
2020-06-12 17:01:05 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
iterator leastToMostSig*(a: var SomeBigInteger): var Word =
|
2020-06-12 17:01:05 +00:00
|
|
|
## Iterate from least to most significant word
|
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in 0 ..< a.limbs.len:
|
|
|
|
yield a.limbs[i]
|
2020-06-12 17:01:05 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in countdown(a.limbs.len-1, 0):
|
|
|
|
yield a.limbs[i]
|
2020-06-12 17:01:05 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
iterator leastToMostSig*(a, b: SomeBigInteger): (Word, Word) =
|
2020-06-12 17:01:05 +00:00
|
|
|
## Iterate from least to most significant word
|
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in 0 ..< a.limbs.len:
|
|
|
|
yield (a.limbs[i], b.limbs[i])
|
2020-06-12 17:01:05 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in countdown(a.limbs.len-1, 0):
|
|
|
|
yield (a.limbs[i], b.limbs[i])
|
2020-06-12 17:01:05 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
iterator leastToMostSig*[aBits, bBits](a: var SomeBigInteger[aBits], b: SomeBigInteger[bBits]): (var Word, Word) =
|
2020-06-12 17:01:05 +00:00
|
|
|
## Iterate from least to most significant word
|
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in 0 ..< min(a.limbs.len, b.limbs.len):
|
|
|
|
yield (a.limbs[i], b.limbs[i])
|
2020-06-12 17:01:05 +00:00
|
|
|
else:
|
2021-08-06 12:44:25 +00:00
|
|
|
for i in countdown(min(a.limbs.len, b.limbs.len)-1, 0):
|
2020-06-12 21:53:08 +00:00
|
|
|
yield (a.limbs[i], b.limbs[i])
|
2020-06-12 17:23:03 +00:00
|
|
|
|
2020-06-12 21:53:08 +00:00
|
|
|
iterator leastToMostSig*(c: var SomeBigInteger, a, b: SomeBigInteger): (var Word, Word, Word) =
|
2020-06-12 17:23:03 +00:00
|
|
|
## Iterate from least to most significant word
|
|
|
|
when cpuEndian == littleEndian:
|
2020-06-12 21:53:08 +00:00
|
|
|
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]
|
2020-06-12 17:23:03 +00:00
|
|
|
else:
|
2020-06-12 21:53:08 +00:00
|
|
|
for i in countdown(a.limbs.len-1, 0):
|
|
|
|
yield a.limbs[i]
|
2020-06-12 18:05:40 +00:00
|
|
|
|
|
|
|
import std/macros
|
|
|
|
|
|
|
|
proc replaceNodes(ast: NimNode, what: NimNode, by: NimNode): NimNode =
|
|
|
|
# Replace "what" ident node by "by"
|
|
|
|
proc inspect(node: NimNode): NimNode =
|
|
|
|
case node.kind:
|
|
|
|
of {nnkIdent, nnkSym}:
|
|
|
|
if node.eqIdent(what):
|
|
|
|
return by
|
|
|
|
return node
|
|
|
|
of nnkEmpty:
|
|
|
|
return node
|
|
|
|
of nnkLiterals:
|
|
|
|
return node
|
|
|
|
else:
|
|
|
|
var rTree = node.kind.newTree()
|
|
|
|
for child in node:
|
|
|
|
rTree.add inspect(child)
|
|
|
|
return rTree
|
|
|
|
result = inspect(ast)
|
|
|
|
|
|
|
|
macro staticFor*(idx: untyped{nkIdent}, start, stopEx: static int, body: untyped): untyped =
|
|
|
|
## staticFor [min inclusive, max exclusive)
|
|
|
|
result = newStmtList()
|
|
|
|
for i in start ..< stopEx:
|
|
|
|
result.add nnkBlockStmt.newTree(
|
|
|
|
ident("unrolledIter_" & $idx & $i),
|
|
|
|
body.replaceNodes(idx, newLit i)
|
|
|
|
)
|
2021-08-06 12:44:25 +00:00
|
|
|
|
|
|
|
# Copy
|
|
|
|
# --------------------------------------------------------
|
|
|
|
|
2022-01-22 00:42:54 +00:00
|
|
|
func copyFrom*(
|
|
|
|
dst: var SomeBigInteger,
|
|
|
|
src: SomeBigInteger
|
2021-08-06 12:44:25 +00:00
|
|
|
){.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]
|