mirror of
https://github.com/status-im/nim-stint.git
synced 2025-02-21 19:38:32 +00:00
parent
98b2086262
commit
c65ab16512
@ -7,43 +7,11 @@
|
|||||||
#
|
#
|
||||||
# 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 ./uint_type
|
import ./uint_type, bitops
|
||||||
|
|
||||||
# Compiler defined const: https://github.com/nim-lang/Nim/wiki/Consts-defined-by-the-compiler
|
proc bit_length*(x: SomeInteger): int {.inline, noSideEffect.}=
|
||||||
const withBuiltins = defined(gcc) or defined(clang)
|
if x == 0: 0
|
||||||
|
else: fastlog2(x)
|
||||||
when withBuiltins:
|
|
||||||
proc builtin_clz(n: cuint): cint {.importc: "__builtin_clz", nodecl.}
|
|
||||||
proc builtin_clz(n: culong): cint {.importc: "__builtin_clzl", nodecl.}
|
|
||||||
proc builtin_clz(n: culonglong): cint {.importc: "__builtin_clzll", nodecl.}
|
|
||||||
type TbuiltinSupported = cuint or culong or culonglong
|
|
||||||
# Warning ⚠: if n = 0, clz is undefined
|
|
||||||
|
|
||||||
template bit_length_impl[T: SomeUnsignedInt or Natural or int](n: T, result: int) =
|
|
||||||
# For some reason using "SomeUnsignedInt or Natural" directly makes Nim compiler
|
|
||||||
# throw a type mismatch in a proc, we use a template as a workaround
|
|
||||||
# Plus the template doesn't match natural with int :/
|
|
||||||
when withBuiltins and T is TbuiltinSupported:
|
|
||||||
result = if n == T(0): 0 # Removing this branch would make divmod 4x faster :/
|
|
||||||
else: T.sizeof * 8 - builtin_clz(n)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# The biggest optimization for the naive implementation
|
|
||||||
# is making sure this is inline
|
|
||||||
# This is the difference between returning in 2ms or 10+ second
|
|
||||||
# on a 1000000000 times test loop.
|
|
||||||
var x = n
|
|
||||||
while x != T(0):
|
|
||||||
x = x shr 1
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
proc bit_length*(n: SomeUnsignedInt): int {.noSideEffect, inline.}=
|
|
||||||
## Calculates how many bits are necessary to represent the number
|
|
||||||
bit_length_impl(n, result)
|
|
||||||
|
|
||||||
proc bit_length*(n: Natural): int {.noSideEffect, inline.}=
|
|
||||||
## Calculates how many bits are necessary to represent the number
|
|
||||||
bit_length_impl(n, result)
|
|
||||||
|
|
||||||
proc bit_length*(n: MpUintImpl): int {.noSideEffect.}=
|
proc bit_length*(n: MpUintImpl): int {.noSideEffect.}=
|
||||||
## Calculates how many bits are necessary to represent the number
|
## Calculates how many bits are necessary to represent the number
|
||||||
|
@ -10,15 +10,13 @@
|
|||||||
import ./uint_type,
|
import ./uint_type,
|
||||||
macros
|
macros
|
||||||
|
|
||||||
template convBool(typ: typedesc): untyped =
|
|
||||||
# needed for carry conversion
|
|
||||||
converter boolMpUint*(b: bool): MpUintImpl[typ] {.noSideEffect, inline.}=
|
|
||||||
result.lo = b.typ
|
|
||||||
|
|
||||||
convBool(uint8)
|
proc toSubtype*[T: SomeInteger](b: bool, typ: typedesc[T]): T {.noSideEffect, inline.}=
|
||||||
convBool(uint16)
|
b.T
|
||||||
convBool(uint32)
|
|
||||||
convBool(uint64)
|
proc toSubtype*[T: MpUintImpl](b: bool, typ: typedesc[T]): T {.noSideEffect, inline.}=
|
||||||
|
type SubTy = type result.lo
|
||||||
|
result.lo = toSubtype(b, SubTy)
|
||||||
|
|
||||||
proc zero*(typ: typedesc[BaseUint]): typ {.compileTime.} =
|
proc zero*(typ: typedesc[BaseUint]): typ {.compileTime.} =
|
||||||
typ()
|
typ()
|
||||||
|
@ -21,7 +21,7 @@ proc `+=`*(x: var MpUintImpl, y: MpUintImpl) {.noSideEffect, inline.}=
|
|||||||
let tmp = x.lo
|
let tmp = x.lo
|
||||||
|
|
||||||
x.lo += y.lo
|
x.lo += y.lo
|
||||||
x.hi += SubTy(x.lo < tmp) + y.hi
|
x.hi += (x.lo < tmp).toSubtype(SubTy) + y.hi
|
||||||
|
|
||||||
proc `+`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit, inline.}=
|
proc `+`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit, inline.}=
|
||||||
# Addition for multi-precision unsigned int
|
# Addition for multi-precision unsigned int
|
||||||
@ -37,14 +37,33 @@ proc `-=`*(x: var MpUintImpl, y: MpUintImpl) {.noSideEffect, inline.}=
|
|||||||
let tmp = x.lo
|
let tmp = x.lo
|
||||||
|
|
||||||
x.lo -= y.lo
|
x.lo -= y.lo
|
||||||
x.hi -= SubTy(x.lo > tmp) + y.hi
|
x.hi -= (x.lo > tmp).toSubtype(SubTy) + y.hi
|
||||||
|
|
||||||
proc `-`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit, inline.}=
|
proc `-`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit, inline.}=
|
||||||
# Substraction for multi-precision unsigned int
|
# Substraction for multi-precision unsigned int
|
||||||
result = x
|
result = x
|
||||||
result -= y
|
result -= y
|
||||||
|
|
||||||
template naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] =
|
|
||||||
|
proc naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] {.noSideEffect, noInit, inline.}
|
||||||
|
# Forward declaration
|
||||||
|
|
||||||
|
proc naiveMul[T: BaseUint](x, y: T): MpUintImpl[T] {.noSideEffect, noInit, inline.}=
|
||||||
|
## Naive multiplication algorithm with extended precision
|
||||||
|
|
||||||
|
when T.sizeof in {1, 2, 4}:
|
||||||
|
# Use types twice bigger to do the multiplication
|
||||||
|
cast[type result](x.asDoubleUint * y.asDoubleUint)
|
||||||
|
|
||||||
|
elif T.sizeof == 8: # uint64 or MpUint[uint32]
|
||||||
|
# We cannot double uint64 to uint128
|
||||||
|
naiveMulImpl(x.toMpUintImpl, y.toMpUintImpl)
|
||||||
|
else:
|
||||||
|
# Case: at least uint128 * uint128 --> uint256
|
||||||
|
naiveMulImpl(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
proc naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] {.noSideEffect, noInit, inline.}=
|
||||||
# See details at
|
# See details at
|
||||||
# https://en.wikipedia.org/wiki/Karatsuba_algorithm
|
# https://en.wikipedia.org/wiki/Karatsuba_algorithm
|
||||||
# https://locklessinc.com/articles/256bit_arithmetic/
|
# https://locklessinc.com/articles/256bit_arithmetic/
|
||||||
@ -66,27 +85,12 @@ template naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] =
|
|||||||
|
|
||||||
var z1 = tmp
|
var z1 = tmp
|
||||||
z1 += naiveMul(x.hi, y.lo)
|
z1 += naiveMul(x.hi, y.lo)
|
||||||
let z2 = (z1 < tmp).T + naiveMul(x.hi, y.hi)
|
let z2 = (z1 < tmp).toSubtype(T) + naiveMul(x.hi, y.hi)
|
||||||
|
|
||||||
let tmp2 = z1.lo shl halfSize
|
let tmp2 = z1.lo shl halfSize
|
||||||
result.lo = tmp2
|
result.lo = tmp2
|
||||||
result.lo += z0
|
result.lo += z0
|
||||||
result.hi = (result.lo < tmp2).T + z2 + z1.hi
|
result.hi = (result.lo < tmp2).toSubtype(T) + z2 + z1.hi
|
||||||
|
|
||||||
proc naiveMul[T: BaseUint](x, y: T): MpUintImpl[T] {.noSideEffect, noInit, inline.}=
|
|
||||||
## Naive multiplication algorithm with extended precision
|
|
||||||
|
|
||||||
when T.sizeof in {1, 2, 4}:
|
|
||||||
# Use types twice bigger to do the multiplication
|
|
||||||
cast[type result](x.asDoubleUint * y.asDoubleUint)
|
|
||||||
|
|
||||||
elif T.sizeof == 8: # uint64 or MpUint[uint32]
|
|
||||||
# We cannot double uint64 to uint128
|
|
||||||
naiveMulImpl(x.toMpUint, y.toMpUint)
|
|
||||||
else:
|
|
||||||
# Case: at least uint128 * uint128 --> uint256
|
|
||||||
naiveMulImpl(x, y)
|
|
||||||
|
|
||||||
|
|
||||||
proc `*`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit.}=
|
proc `*`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit.}=
|
||||||
## Multiplication for multi-precision unsigned uint
|
## Multiplication for multi-precision unsigned uint
|
||||||
|
@ -17,8 +17,8 @@ proc `<=`*(x, y: MpUintImpl): bool {.noSideEffect, noInit, inline.}=
|
|||||||
result = if x == y: true
|
result = if x == y: true
|
||||||
else: x < y
|
else: x < y
|
||||||
|
|
||||||
proc isZero[T: SomeUnsignedInt](n: T): bool {.noSideEffect,inline.} =
|
proc isZero*(n: SomeUnsignedInt): bool {.noSideEffect,inline.} =
|
||||||
n == 0.T
|
n == 0
|
||||||
|
|
||||||
proc isZero*(n: MpUintImpl): bool {.noSideEffect,inline.} =
|
proc isZero*(n: MpUintImpl): bool {.noSideEffect,inline.} =
|
||||||
n.lo.isZero and n.hi.isZero
|
n.lo.isZero and n.hi.isZero
|
||||||
|
@ -14,15 +14,15 @@ import ./private/bithacks, ./private/conversion,
|
|||||||
|
|
||||||
import typetraits
|
import typetraits
|
||||||
|
|
||||||
proc initMpUint*(n: SomeUnsignedInt, bits: static[int]): MpUint[bits] {.noSideEffect.} =
|
proc initMpUint*(n: SomeInteger, bits: static[int]): MpUint[bits] {.noSideEffect.} =
|
||||||
|
assert n >= 0
|
||||||
when result.data is MpuintImpl:
|
when result.data is MpuintImpl:
|
||||||
type SubTy = type result.data.lo
|
type SubTy = type result.data.lo
|
||||||
|
|
||||||
let len = n.bit_length
|
let len = n.bit_length
|
||||||
if len > bits:
|
if len > bits:
|
||||||
# Todo print n
|
raise newException(ValueError, "Input " & $n & " cannot be stored in a multi-precision " &
|
||||||
raise newException(ValueError, "Input cannot be stored in a multi-precision " & $bits & "-bit integer." &
|
$bits & "-bit integer." &
|
||||||
"\nIt requires at least " & $len & " bits of precision")
|
"\nIt requires at least " & $len & " bits of precision")
|
||||||
elif len < bits div 2:
|
elif len < bits div 2:
|
||||||
result.data.lo = SubTy(n)
|
result.data.lo = SubTy(n)
|
||||||
@ -35,12 +35,6 @@ proc initMpUint*(n: SomeUnsignedInt, bits: static[int]): MpUint[bits] {.noSideEf
|
|||||||
elif bits == 64:
|
elif bits == 64:
|
||||||
result.data = toMpUintImpl n.uint64
|
result.data = toMpUintImpl n.uint64
|
||||||
else:
|
else:
|
||||||
{.fatal, "unreachable".}
|
raise newException(ValueError, "Fatal")
|
||||||
else:
|
else:
|
||||||
result.data = (type result.data)(n)
|
result.data = (type result.data)(n)
|
||||||
|
|
||||||
proc u128*(n: SomeUnsignedInt): MpUint[128] {.noSideEffect, inline, noInit.}=
|
|
||||||
initMpUint[128](n)
|
|
||||||
|
|
||||||
proc u256*(n: SomeUnsignedInt): MpUint[256] {.noSideEffect, inline, noInit.}=
|
|
||||||
initMpUint[256](n)
|
|
||||||
|
@ -14,6 +14,13 @@ type
|
|||||||
UInt128* = MpUint[128]
|
UInt128* = MpUint[128]
|
||||||
UInt256* = MpUint[256]
|
UInt256* = MpUint[256]
|
||||||
|
|
||||||
|
template make_conv(conv_name: untyped, size: int): untyped =
|
||||||
|
proc `convname`*(n: SomeInteger): MpUint[size] {.noSideEffect, inline, noInit.}=
|
||||||
|
n.initMpUint(size)
|
||||||
|
|
||||||
|
make_conv(u128, 128)
|
||||||
|
make_conv(u256, 256)
|
||||||
|
|
||||||
template make_unary(op, ResultTy): untyped =
|
template make_unary(op, ResultTy): untyped =
|
||||||
proc `op`*(x: MpUint): ResultTy {.noInit, inline, noSideEffect.} =
|
proc `op`*(x: MpUint): ResultTy {.noInit, inline, noSideEffect.} =
|
||||||
when resultTy is MpUint:
|
when resultTy is MpUint:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user