mirror of
https://github.com/status-im/nim-stint.git
synced 2025-02-20 10:58:21 +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.
|
||||
|
||||
import ./uint_type
|
||||
import ./uint_type, bitops
|
||||
|
||||
# Compiler defined const: https://github.com/nim-lang/Nim/wiki/Consts-defined-by-the-compiler
|
||||
const withBuiltins = defined(gcc) or defined(clang)
|
||||
|
||||
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*(x: SomeInteger): int {.inline, noSideEffect.}=
|
||||
if x == 0: 0
|
||||
else: fastlog2(x)
|
||||
|
||||
proc bit_length*(n: MpUintImpl): int {.noSideEffect.}=
|
||||
## Calculates how many bits are necessary to represent the number
|
||||
|
@ -10,15 +10,13 @@
|
||||
import ./uint_type,
|
||||
macros
|
||||
|
||||
template convBool(typ: typedesc): untyped =
|
||||
# needed for carry conversion
|
||||
converter boolMpUint*(b: bool): MpUintImpl[typ] {.noSideEffect, inline.}=
|
||||
result.lo = b.typ
|
||||
|
||||
convBool(uint8)
|
||||
convBool(uint16)
|
||||
convBool(uint32)
|
||||
convBool(uint64)
|
||||
proc toSubtype*[T: SomeInteger](b: bool, typ: typedesc[T]): T {.noSideEffect, inline.}=
|
||||
b.T
|
||||
|
||||
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.} =
|
||||
typ()
|
||||
|
@ -21,7 +21,7 @@ proc `+=`*(x: var MpUintImpl, y: MpUintImpl) {.noSideEffect, inline.}=
|
||||
let tmp = x.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.}=
|
||||
# Addition for multi-precision unsigned int
|
||||
@ -37,14 +37,33 @@ proc `-=`*(x: var MpUintImpl, y: MpUintImpl) {.noSideEffect, inline.}=
|
||||
let tmp = x.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.}=
|
||||
# Substraction for multi-precision unsigned int
|
||||
result = x
|
||||
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
|
||||
# https://en.wikipedia.org/wiki/Karatsuba_algorithm
|
||||
# https://locklessinc.com/articles/256bit_arithmetic/
|
||||
@ -66,27 +85,12 @@ template naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] =
|
||||
|
||||
var z1 = tmp
|
||||
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
|
||||
result.lo = tmp2
|
||||
result.lo += z0
|
||||
result.hi = (result.lo < tmp2).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)
|
||||
|
||||
result.hi = (result.lo < tmp2).toSubtype(T) + z2 + z1.hi
|
||||
|
||||
proc `*`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit.}=
|
||||
## Multiplication for multi-precision unsigned uint
|
||||
|
@ -17,8 +17,8 @@ proc `<=`*(x, y: MpUintImpl): bool {.noSideEffect, noInit, inline.}=
|
||||
result = if x == y: true
|
||||
else: x < y
|
||||
|
||||
proc isZero[T: SomeUnsignedInt](n: T): bool {.noSideEffect,inline.} =
|
||||
n == 0.T
|
||||
proc isZero*(n: SomeUnsignedInt): bool {.noSideEffect,inline.} =
|
||||
n == 0
|
||||
|
||||
proc isZero*(n: MpUintImpl): bool {.noSideEffect,inline.} =
|
||||
n.lo.isZero and n.hi.isZero
|
||||
|
@ -14,15 +14,15 @@ import ./private/bithacks, ./private/conversion,
|
||||
|
||||
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:
|
||||
type SubTy = type result.data.lo
|
||||
|
||||
let len = n.bit_length
|
||||
if len > bits:
|
||||
# Todo print n
|
||||
raise newException(ValueError, "Input cannot be stored in a multi-precision " & $bits & "-bit integer." &
|
||||
raise newException(ValueError, "Input " & $n & " cannot be stored in a multi-precision " &
|
||||
$bits & "-bit integer." &
|
||||
"\nIt requires at least " & $len & " bits of precision")
|
||||
elif len < bits div 2:
|
||||
result.data.lo = SubTy(n)
|
||||
@ -35,12 +35,6 @@ proc initMpUint*(n: SomeUnsignedInt, bits: static[int]): MpUint[bits] {.noSideEf
|
||||
elif bits == 64:
|
||||
result.data = toMpUintImpl n.uint64
|
||||
else:
|
||||
{.fatal, "unreachable".}
|
||||
raise newException(ValueError, "Fatal")
|
||||
else:
|
||||
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]
|
||||
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 =
|
||||
proc `op`*(x: MpUint): ResultTy {.noInit, inline, noSideEffect.} =
|
||||
when resultTy is MpUint:
|
||||
|
Loading…
x
Reference in New Issue
Block a user