mirror of
https://github.com/status-im/nim-stint.git
synced 2025-02-19 18:38:13 +00:00
Uint - allow compile-time evaluation for all procs (#54)
* Initial commit - allow all ops at compile-time on uint. * Update benchmark * Delete commented ideal version of asWordsiterate, it is obvious from the workaround (and is tracked in the PR)
This commit is contained in:
parent
184e22b659
commit
a46c62bc83
@ -1,4 +1,4 @@
|
||||
import ../stint/mpint, times
|
||||
import ../stint, times
|
||||
|
||||
|
||||
# Warmup on normal int
|
||||
@ -18,10 +18,10 @@ echo "Warmup: " & $(stop - start) & "s"
|
||||
|
||||
start = cpuTime()
|
||||
block:
|
||||
var foo = 123.u(256)
|
||||
var foo = 123.u256
|
||||
for i in 0 ..< 10_000_000:
|
||||
foo += i.u(256) * i.u(256) mod 456.u(256)
|
||||
foo = foo mod 789.u(256)
|
||||
foo += i.u256 * i.u256 mod 456.u256
|
||||
foo = foo mod 789.u256
|
||||
|
||||
stop = cpuTime()
|
||||
echo "Library: " & $(stop - start) & "s"
|
||||
@ -47,3 +47,9 @@ when defined(bench_ttmath):
|
||||
# Warmup: 0.04060799999999999s
|
||||
# Library: 0.9576759999999999s
|
||||
# TTMath: 0.758443s
|
||||
|
||||
|
||||
# After PR #54 for compile-time evaluation
|
||||
# which includes loop unrolling but may bloat the code
|
||||
# Warmup: 0.03993500000000001s
|
||||
# Library: 0.848464s
|
||||
|
@ -39,7 +39,7 @@ proc isInt*(x: NimNode): static[bool] =
|
||||
elif eqIdent(x, "int8"): true
|
||||
else: false
|
||||
|
||||
macro most_significant_word*(x: IntImpl): untyped =
|
||||
macro most_significant_word_signed*(x: IntImpl): untyped =
|
||||
|
||||
let optim_type = optimInt(x)
|
||||
if optim_type.isInt:
|
||||
@ -87,8 +87,8 @@ macro asSignedWordsZip*[T](
|
||||
first_y = quote do:
|
||||
cast[`optim_type`](`y`)
|
||||
else:
|
||||
first_x = getAST(most_significant_word(x))
|
||||
first_y = getAST(most_significant_word(y))
|
||||
first_x = getAST(most_significant_word_signed(x))
|
||||
first_y = getAST(most_significant_word_signed(y))
|
||||
|
||||
replacing.add first_x
|
||||
replacing.add first_y
|
||||
|
@ -9,33 +9,52 @@
|
||||
|
||||
import ./datatypes, macros
|
||||
|
||||
proc optimUint(x: NimNode): NimNode =
|
||||
let size = getSize(x)
|
||||
# #########################################################################
|
||||
# Multi-precision ints to compile-time array of words
|
||||
|
||||
if size > 64:
|
||||
result = quote do:
|
||||
array[`size` div 64, uint64]
|
||||
elif size == 64:
|
||||
result = quote do:
|
||||
uint64
|
||||
elif size == 32:
|
||||
result = quote do:
|
||||
uint32
|
||||
elif size == 16:
|
||||
result = quote do:
|
||||
uint16
|
||||
elif size == 8:
|
||||
result = quote do:
|
||||
uint8
|
||||
proc asWordsImpl(x: NimNode, current_path: NimNode, result: var NimNode) =
|
||||
## Transforms an UintImpl/IntImpl into an array of words
|
||||
## at compile-time. Recursive implementation.
|
||||
## Result is from most significant word to least significant
|
||||
|
||||
let node = x.getTypeInst
|
||||
|
||||
if node.kind == nnkBracketExpr:
|
||||
assert eqIdent(node[0], "UintImpl") or eqIdent(node[0], "IntImpl")
|
||||
|
||||
let hi = nnkDotExpr.newTree(current_path, newIdentNode("hi"))
|
||||
let lo = nnkDotExpr.newTree(current_path, newIdentNode("lo"))
|
||||
asWordsImpl(node[1], hi, result)
|
||||
asWordsImpl(node[1], lo, result)
|
||||
else:
|
||||
error "Unreachable path reached"
|
||||
result.add current_path
|
||||
|
||||
proc isUint(x: NimNode): static[bool] =
|
||||
if eqIdent(x, "uint64"): true
|
||||
elif eqIdent(x, "uint32"): true
|
||||
elif eqIdent(x, "uint16"): true
|
||||
elif eqIdent(x, "uint8"): true
|
||||
else: false
|
||||
# #########################################################################
|
||||
# Accessors
|
||||
|
||||
macro asWords(x: UintImpl or IntImpl, idx: static[int]): untyped =
|
||||
## Access a single element from a multiprecision ints
|
||||
## as if if was stored as an array
|
||||
## x.asWords[0] is the most significant word
|
||||
var words = nnkBracket.newTree()
|
||||
asWordsImpl(x, x, words)
|
||||
result = words[idx]
|
||||
|
||||
macro most_significant_word*(x: UintImpl or IntImpl): untyped =
|
||||
result = getAST(asWords(x, 0))
|
||||
|
||||
macro least_significant_word*(x: UintImpl or IntImpl): untyped =
|
||||
var words = nnkBracket.newTree()
|
||||
asWordsImpl(x, x, words)
|
||||
result = words[words.len - 1]
|
||||
|
||||
macro second_least_significant_word*(x: UintImpl or IntImpl): untyped =
|
||||
var words = nnkBracket.newTree()
|
||||
asWordsImpl(x, x, words)
|
||||
result = words[words.len - 2]
|
||||
|
||||
# #########################################################################
|
||||
# Iteration macros
|
||||
|
||||
proc replaceNodes*(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNode =
|
||||
# Args:
|
||||
@ -60,263 +79,85 @@ proc replaceNodes*(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNo
|
||||
return rTree
|
||||
result = inspect(ast)
|
||||
|
||||
proc least_significant_two_words*(x: NimNode): tuple[lo, hi: NimNode] =
|
||||
var node = x.getTypeInst
|
||||
var result_lo = x
|
||||
macro asWordsIterate(wordsIdents: untyped, sid0, sid1, sid2: typed, loopBody: untyped): untyped =
|
||||
# TODO: We can't use varargs[typed] without losing type info - https://github.com/nim-lang/Nim/issues/7737
|
||||
# So we need a workaround we accept fixed 3 args sid0, sid1, sid2 and will just ignore what is not used
|
||||
result = newStmtList()
|
||||
let NbStints = wordsIdents.len
|
||||
|
||||
while node.kind == nnkBracketExpr:
|
||||
assert eqIdent(node[0], "UintImpl") or eqIdent(node[0], "IntImpl"), (
|
||||
"least_significant_word only supports primitive integers, Stint and Stuint")
|
||||
result_lo = quote do: `result_lo`.lo
|
||||
node = node[1]
|
||||
# 1. Get the words of each stint
|
||||
# + Workaround varargs[typed] losing type info https://github.com/nim-lang/Nim/issues/7737
|
||||
var words = nnkBracket.newTree
|
||||
block:
|
||||
var wordList = nnkBracket.newTree
|
||||
asWordsImpl(sid0, sid0, wordList)
|
||||
words.add wordList
|
||||
if NbStints > 1:
|
||||
var wordList = nnkBracket.newTree
|
||||
asWordsImpl(sid1, sid1, wordList)
|
||||
words.add wordList
|
||||
if NbStints > 2:
|
||||
var wordList = nnkBracket.newTree
|
||||
asWordsImpl(sid2, sid2, wordList)
|
||||
words.add wordList
|
||||
|
||||
var result_hi = result_lo.copyNimTree # ⚠ Aliasing: NimNodes are ref objects
|
||||
result_hi[1] = newIdentNode("hi") # replace the last lo by hi
|
||||
result = (result_lo, result_hi)
|
||||
# 2. Construct an unrolled loop
|
||||
# We replace each occurence of each words
|
||||
# in the original loop by how to access it.
|
||||
let NbWords = words[0].len
|
||||
|
||||
macro second_least_significant_word*(x: UintImpl or IntImpl): untyped =
|
||||
result = least_significant_two_words(x).hi
|
||||
for currDepth in 0 ..< NbWords:
|
||||
var replacing = nnkBracket.newTree
|
||||
for currStint in 0 ..< NbStints:
|
||||
replacing.add words[currStint][currDepth]
|
||||
|
||||
macro least_significant_word*(x: UintImpl or IntImpl): untyped =
|
||||
result = least_significant_two_words(x).lo
|
||||
let body = replaceNodes(loopBody, replacing, to_replace = wordsIdents)
|
||||
result.add quote do:
|
||||
block: `body`
|
||||
|
||||
macro asWords*(n: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over n, as an array of words.
|
||||
## Input:
|
||||
## - n: The Multiprecision int
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optimUint(n)
|
||||
var
|
||||
inner_n: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
macro asWords*(x: ForLoopStmt): untyped =
|
||||
## This unrolls the body of the for loop
|
||||
## and applies it for each word.
|
||||
##
|
||||
## TODO: allow an ignoreEndianness and signed parameter
|
||||
|
||||
if optim_type.isUint:
|
||||
# We directly cast n
|
||||
inner_n = quote do:
|
||||
cast[`optim_type`](`n`)
|
||||
else:
|
||||
# If we have an array of words, inner_n is a loop intermediate variable
|
||||
inner_n = ident("n_asWordsRaw")
|
||||
# ##### Tree representation
|
||||
# for word_a, word_b in asWords(a, b):
|
||||
# discard
|
||||
|
||||
to_replace.add n
|
||||
replacing.add inner_n
|
||||
# ForStmt
|
||||
# Ident "word_a"
|
||||
# Ident "word_b"
|
||||
# Call
|
||||
# Ident "asWords"
|
||||
# Ident "a"
|
||||
# Ident "b"
|
||||
# StmtList
|
||||
# DiscardStmt
|
||||
# Empty
|
||||
|
||||
let replacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
# 1. Get the words variable idents
|
||||
var wordsIdents = nnkBracket.newTree
|
||||
var idx = 0
|
||||
while x[idx].kind == nnkIdent:
|
||||
wordsIdents.add x[idx]
|
||||
inc idx
|
||||
|
||||
if optim_type.isUint:
|
||||
result = replacedAST
|
||||
else:
|
||||
if ignoreEndianness or system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
for `inner_n` in cast[`optim_type`](`n`):
|
||||
`replacedAST`
|
||||
else:
|
||||
assert false, "Not implemented"
|
||||
# 2. Get the multiprecision ints idents
|
||||
var stintsIdents = nnkArgList.newTree # nnkArgList allows to keep the type when passing to varargs[typed]
|
||||
# but varargs[typed] has further issues ¯\_(ツ)_/¯
|
||||
idx = 1
|
||||
while idx < x[wordsIdents.len].len and x[wordsIdents.len][idx].kind == nnkIdent:
|
||||
stintsIdents.add x[wordsIdents.len][idx]
|
||||
inc idx
|
||||
|
||||
macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over x and y, as an array of words.
|
||||
## Input:
|
||||
## - x, y: The multiprecision ints
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_x, inner_y: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
assert wordsIdents.len == stintsIdents.len, "The number of loop variables and multiprecision integers t iterate on must be the same"
|
||||
|
||||
to_replace.add x
|
||||
to_replace.add y
|
||||
# 3. Get the body and pass the bucket to a typed macro
|
||||
# + unroll varargs[typed] manually as workaround for https://github.com/nim-lang/Nim/issues/7737
|
||||
var body = x[x.len - 1]
|
||||
let sid0 = stintsIdents[0]
|
||||
let sid1 = if stintsIdents.len > 1: stintsIdents[1] else: newEmptyNode()
|
||||
let sid2 = if stintsIdents.len > 2: stintsIdents[2] else: newEmptyNode()
|
||||
|
||||
if optim_type.isUint:
|
||||
# We directly castx and y
|
||||
inner_x = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
inner_y = quote do:
|
||||
cast[`optim_type`](`y`)
|
||||
|
||||
replacing.add inner_x
|
||||
replacing.add inner_y
|
||||
else:
|
||||
# If we have an array of words, inner_x and inner_y is are loop intermediate variable
|
||||
inner_x = ident("x_asWordsRawZip")
|
||||
inner_y = ident("y_asWordsRawZip")
|
||||
|
||||
# We replace the inner loop with the inner_x[idx]
|
||||
replacing.add quote do:
|
||||
`inner_x`[`idx`]
|
||||
replacing.add quote do:
|
||||
`inner_y`[`idx`]
|
||||
|
||||
let replacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
if optim_type.isUint:
|
||||
result = replacedAST
|
||||
else:
|
||||
if ignoreEndianness or system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in 0 ..< `inner_x`[].len:
|
||||
`replacedAST`
|
||||
else:
|
||||
# Little-Endian, iteration in reverse
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in countdown(`inner_x`[].len - 1, 0):
|
||||
`replacedAST`
|
||||
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
to_replace.add m
|
||||
to_replace.add x
|
||||
|
||||
if optim_type.isUint:
|
||||
# We directly cast m and x
|
||||
inner_m = quote do:
|
||||
cast[var `optim_type`](`m`.addr)
|
||||
inner_x = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
|
||||
replacing.add inner_m
|
||||
replacing.add inner_x
|
||||
else:
|
||||
# If we have an array of words, inner_x and inner_y is are loop intermediate variable
|
||||
inner_m = ident("m_asWordsRawZip")
|
||||
inner_x = ident("x_asWordsRawZip")
|
||||
|
||||
# We replace the inner loop with the inner_x[idx]
|
||||
replacing.add quote do:
|
||||
`inner_m`[`idx`]
|
||||
replacing.add quote do:
|
||||
`inner_x`[`idx`]
|
||||
|
||||
let replacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
if optim_type.isUint:
|
||||
result = replacedAST
|
||||
else:
|
||||
if ignoreEndianness or system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
for `idx` in 0 ..< `inner_x`[].len:
|
||||
`replacedAST`
|
||||
else:
|
||||
# Little-Endian, iteration in reverse
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
for `idx` in countdown(`inner_x`[].len - 1, 0):
|
||||
`replacedAST`
|
||||
|
||||
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x, inner_y: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
to_replace.add m
|
||||
to_replace.add x
|
||||
to_replace.add y
|
||||
|
||||
if optim_type.isUint:
|
||||
# We directly cast m, x and y
|
||||
inner_m = quote do:
|
||||
cast[var `optim_type`](`m`.addr)
|
||||
inner_x = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
inner_y = quote do:
|
||||
cast[`optim_type`](`y`)
|
||||
|
||||
replacing.add inner_m
|
||||
replacing.add inner_x
|
||||
replacing.add inner_y
|
||||
else:
|
||||
# If we have an array of words, inner_x and inner_y is are loop intermediate variable
|
||||
inner_m = ident("m_asWordsRawZip")
|
||||
inner_x = ident("x_asWordsRawZip")
|
||||
inner_y = ident("y_asWordsRawZip")
|
||||
|
||||
# We replace the inner loop with the inner_x[idx]
|
||||
replacing.add quote do:
|
||||
`inner_m`[`idx`]
|
||||
replacing.add quote do:
|
||||
`inner_x`[`idx`]
|
||||
replacing.add quote do:
|
||||
`inner_y`[`idx`]
|
||||
|
||||
let replacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
# Arrays are in the form (`[]`, array, type)
|
||||
if optim_type.isUint:
|
||||
result = replacedAST
|
||||
else:
|
||||
if ignoreEndianness or system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in 0 ..< `inner_x`[].len:
|
||||
`replacedAST`
|
||||
else:
|
||||
# Little-Endian, iteration in reverse
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict $#".}
|
||||
let
|
||||
`inner_m`{.restrict.} = cast[ptr `optim_type`](`m`.addr)
|
||||
`inner_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`inner_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in countdown(`inner_x`[].len - 1, 0):
|
||||
`replacedAST`
|
||||
result = quote do: asWordsIterate(`wordsIdents`, `sid0`, `sid1`, `sid2`, `body`)
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes, stdlib_bitops, as_signed_words
|
||||
import ./datatypes, stdlib_bitops, as_words
|
||||
export stdlib_bitops
|
||||
|
||||
# We reuse bitops from Nim standard lib, and expand it for multi-precision int.
|
||||
@ -25,7 +25,7 @@ func countLeadingZeroBits*(n: UintImpl): int {.inline.} =
|
||||
n.lo.countLeadingZeroBits + maxHalfRepr
|
||||
else: hi_clz
|
||||
|
||||
func msb*[T: SomeInteger](n: T): T {.inline.}=
|
||||
func isMsbSet*[T: SomeInteger](n: T): bool {.inline.}=
|
||||
## Returns the most significant bit of an integer.
|
||||
|
||||
when T is int64 or (T is int and sizeof(int) == 8):
|
||||
@ -40,9 +40,8 @@ func msb*[T: SomeInteger](n: T): T {.inline.}=
|
||||
type Uint = T
|
||||
|
||||
const msb_pos = sizeof(T) * 8 - 1
|
||||
result = T(cast[Uint](n) shr msb_pos)
|
||||
result = bool(cast[Uint](n) shr msb_pos)
|
||||
|
||||
func msb*(n: IntImpl): auto {.inline.}=
|
||||
func isMsbSet*(n: UintImpl or IntImpl): bool {.inline.}=
|
||||
## Returns the most significant bit of an arbitrary precision integer.
|
||||
|
||||
result = msb most_significant_word(n)
|
||||
result = isMsbSet most_significant_word(n)
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes, ./conversion, ./as_signed_words
|
||||
import ./datatypes, ./conversion, ./int_comparison
|
||||
|
||||
func `+`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
# Addition for multi-precision signed int.
|
||||
@ -17,8 +17,8 @@ func `+`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(
|
||||
((result.most_significant_word xor x.most_significant_word) >= 0) or
|
||||
((result.most_significant_word xor y.most_significant_word) >= 0)
|
||||
not(result.isNegative xor x.isNegative) or
|
||||
not(result.isNegative xor y.isNegative)
|
||||
):
|
||||
return
|
||||
raise newException(OverflowError, "Addition overflow")
|
||||
@ -36,8 +36,8 @@ func `-`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
|
||||
when compileOption("boundChecks"):
|
||||
if unlikely(
|
||||
((result.most_significant_word xor x.most_significant_word) >= 0) or
|
||||
((result.most_significant_word xor (not y).most_significant_word) >= 0)
|
||||
not(result.isNegative xor x.isNegative) or
|
||||
not(result.isNegative xor y.isNegative.not)
|
||||
):
|
||||
return
|
||||
raise newException(OverflowError, "Substraction underflow")
|
||||
|
@ -11,20 +11,20 @@ import ./datatypes, ./as_words
|
||||
|
||||
func `not`*(x: IntImpl): IntImpl {.inline.}=
|
||||
## Bitwise complement of unsigned integer x
|
||||
m_asWordsZip(result, x, ignoreEndianness = true):
|
||||
result = not x
|
||||
for wr, wx in asWords(result, x):
|
||||
wr = not wx
|
||||
|
||||
func `or`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
## `Bitwise or` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x or y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx or wy
|
||||
|
||||
func `and`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
## `Bitwise and` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x and y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx and wy
|
||||
|
||||
func `xor`*(x, y: IntImpl): IntImpl {.inline.}=
|
||||
## `Bitwise xor` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x xor y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx xor wy
|
||||
|
@ -14,14 +14,14 @@ func isZero*(n: SomeSignedInt): bool {.inline.} =
|
||||
n == 0
|
||||
|
||||
func isZero*(n: IntImpl): bool {.inline.} =
|
||||
asWords(n, ignoreEndianness = true):
|
||||
if n != 0:
|
||||
for word in asWords(n):
|
||||
if word != 0:
|
||||
return false
|
||||
return true
|
||||
|
||||
func isNegative*(n: IntImpl): bool {.inline.} =
|
||||
## Returns true if a number is negative:
|
||||
n.msb.bool
|
||||
n.isMsbSet
|
||||
|
||||
func `<`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Lower comparison for multi-precision integers
|
||||
@ -32,8 +32,8 @@ func `<`*(x, y: IntImpl): bool {.inline.}=
|
||||
|
||||
func `==`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Equal comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = true):
|
||||
if x != y:
|
||||
for wx, wy in asWords(x, y):
|
||||
if wx != wy:
|
||||
return false
|
||||
return true # they're equal
|
||||
|
||||
|
@ -12,23 +12,23 @@ import ./datatypes, ./as_words
|
||||
|
||||
func `not`*(x: UintImpl): UintImpl {.inline.}=
|
||||
## Bitwise complement of unsigned integer x
|
||||
m_asWordsZip(result, x, ignoreEndianness = true):
|
||||
result = not x
|
||||
for wr, wx in asWords(result, x):
|
||||
wr = not wx
|
||||
|
||||
func `or`*(x, y: UintImpl): UintImpl {.inline.}=
|
||||
## `Bitwise or` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x or y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx or wy
|
||||
|
||||
func `and`*(x, y: UintImpl): UintImpl {.inline.}=
|
||||
## `Bitwise and` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x and y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx and wy
|
||||
|
||||
func `xor`*(x, y: UintImpl): UintImpl {.inline.}=
|
||||
## `Bitwise xor` of numbers x and y
|
||||
m_asWordsZip(result, x, y, ignoreEndianness = true):
|
||||
result = x xor y
|
||||
for wr, wx, wy in asWords(result, x, y):
|
||||
wr = wx xor wy
|
||||
|
||||
func `shr`*(x: UintImpl, y: SomeInteger): UintImpl {.inline.}
|
||||
# Forward declaration
|
||||
@ -37,8 +37,7 @@ func `shl`*(x: UintImpl, y: SomeInteger): UintImpl {.inline.}=
|
||||
## Compute the `shift left` operation of x and y
|
||||
# Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1`
|
||||
|
||||
# TODO: would it be better to reimplement this using an array of bytes/uint64
|
||||
# That opens up to endianness issues.
|
||||
# TODO: would it be better to reimplement this with words iteration?
|
||||
const halfSize: type(y) = getSize(x) div 2
|
||||
|
||||
if y == 0:
|
||||
@ -64,4 +63,3 @@ func `shr`*(x: UintImpl, y: SomeInteger): UintImpl {.inline.}=
|
||||
result.hi = x.hi shr y
|
||||
else:
|
||||
result.lo = x.hi shr (y - halfSize)
|
||||
|
||||
|
@ -13,30 +13,30 @@ func isZero*(n: SomeUnsignedInt): bool {.inline.} =
|
||||
n == 0
|
||||
|
||||
func isZero*(n: UintImpl): bool {.inline.} =
|
||||
asWords(n, ignoreEndianness = true):
|
||||
if n != 0:
|
||||
for word in asWords(n):
|
||||
if word != 0:
|
||||
return false
|
||||
return true
|
||||
|
||||
func `<`*(x, y: UintImpl): bool {.inline.}=
|
||||
# Lower comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = false):
|
||||
if x != y:
|
||||
return x < y
|
||||
for wx, wy in asWords(x, y):
|
||||
if wx != wy:
|
||||
return wx < wy
|
||||
return false # they're equal
|
||||
|
||||
func `==`*(x, y: UintImpl): bool {.inline.}=
|
||||
# Equal comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = true):
|
||||
if x != y:
|
||||
for wx, wy in asWords(x, y):
|
||||
if wx != wy:
|
||||
return false
|
||||
return true # they're equal
|
||||
|
||||
func `<=`*(x, y: UintImpl): bool {.inline.}=
|
||||
# Lower or equal comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = false):
|
||||
if x != y:
|
||||
return x < y
|
||||
for wx, wy in asWords(x, y):
|
||||
if wx != wy:
|
||||
return wx < wy
|
||||
return true # they're equal
|
||||
|
||||
func isOdd*(x: UintImpl): bool {.inline.}=
|
||||
|
Loading…
x
Reference in New Issue
Block a user