mirror of
https://github.com/status-im/nim-stint.git
synced 2025-02-19 18:38:13 +00:00
Recursive init + bitwise as test + fix shr (#9)
* Recursive init compiles * fix bitshift. Missing rolling over * Strange shift required * debug msg leftover
This commit is contained in:
parent
c8190df152
commit
24bd2fc986
@ -53,3 +53,10 @@ proc bit_length*(n: MpUintImpl): int {.noSideEffect.}=
|
||||
let hi_bitlen = n.hi.bit_length
|
||||
result = if hi_bitlen == 0: n.lo.bit_length
|
||||
else: hi_bitlen + maxHalfRepr
|
||||
|
||||
|
||||
proc countLeadingZeroBits*(x: MpUintImpl): int {.inline, nosideeffect.} =
|
||||
## Returns the number of leading zero bits in integer.
|
||||
let hi_clz = x.hi.countLeadingZeroBits
|
||||
result = if hi_clz == 0: x.lo.countLeadingZeroBits
|
||||
else: hi_clz
|
||||
|
@ -7,14 +7,24 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type,
|
||||
import ./uint_type, ./size_mpuintimpl,
|
||||
macros
|
||||
|
||||
# converter mpUint64*(x: MpUintImpl[uint32]): uint64 =
|
||||
# cast[ptr uint64](unsafeAddr x)[]
|
||||
proc initMpUintImpl*[InType, OutType](x: InType, _: typedesc[OutType]): OutType {.noSideEffect.} =
|
||||
|
||||
proc initMpUintImpl*[T: BaseUint](x: T): MpUintImpl[T] {.inline,noSideEffect.} =
|
||||
result.lo = x
|
||||
const
|
||||
size_in = size_mpuintimpl(x)
|
||||
size_out = size_mpuintimpl(result)
|
||||
|
||||
static:
|
||||
assert size_out >= size_in, "The result type size should be equal or bigger than the input type size"
|
||||
|
||||
when OutType is SomeUnsignedInt:
|
||||
result = x.OutType
|
||||
elif size_in == size_out:
|
||||
result = cast[type result](x)
|
||||
else:
|
||||
result.lo = initMpUintImpl(x, type result.lo)
|
||||
|
||||
proc toSubtype*[T: SomeInteger](b: bool, _: typedesc[T]): T {.noSideEffect, inline.}=
|
||||
b.T
|
||||
@ -24,7 +34,7 @@ proc toSubtype*[T: MpUintImpl](b: bool, _: typedesc[T]): T {.noSideEffect, inlin
|
||||
result.lo = toSubtype(b, SubTy)
|
||||
|
||||
proc zero*[T: BaseUint](_: typedesc[T]): T {.noSideEffect, inline.}=
|
||||
result = T(0)
|
||||
discard
|
||||
|
||||
proc one*[T: BaseUint](_: typedesc[T]): T {.noSideEffect, inline.}=
|
||||
when T is SomeUnsignedInt:
|
||||
@ -73,3 +83,7 @@ proc toMpUintImpl*(n: uint16|uint32|uint64): auto {.noSideEffect, inline.} =
|
||||
return (cast[ptr [MpUintImpl[uint16]]](unsafeAddr n))[]
|
||||
elif n is uint16:
|
||||
return (cast[ptr [MpUintImpl[uint8]]](unsafeAddr n))[]
|
||||
|
||||
proc toMpUintImpl*(n: MpUintImpl): MpUintImpl {.noSideEffect, inline.} =
|
||||
## No op
|
||||
n
|
||||
|
@ -52,16 +52,22 @@ proc `-`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit, inline.}=
|
||||
|
||||
proc naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] {.noSideEffect, noInit, inline.}
|
||||
# Forward declaration
|
||||
|
||||
import typetraits
|
||||
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}:
|
||||
const size = size_mpuintimpl(x)
|
||||
|
||||
when size in {8, 16, 32}:
|
||||
# Use types twice bigger to do the multiplication
|
||||
cast[type result](x.asDoubleUint * y.asDoubleUint)
|
||||
|
||||
elif T.sizeof == 8: # uint64 or MpUint[uint32]
|
||||
elif size == 64: # uint64 or MpUint[uint32]
|
||||
# We cannot double uint64 to uint128
|
||||
static:
|
||||
echo "####"
|
||||
echo x.type.name
|
||||
echo size
|
||||
cast[type result](naiveMulImpl(x.toMpUintImpl, y.toMpUintImpl))
|
||||
else:
|
||||
# Case: at least uint128 * uint128 --> uint256
|
||||
@ -92,10 +98,10 @@ proc naiveMulImpl[T: MpUintImpl](x, y: T): MpUintImpl[T] {.noSideEffect, noInit,
|
||||
z1 += naiveMul(x.hi, y.lo)
|
||||
let z2 = (z1 < tmp).toSubtype(T) + naiveMul(x.hi, y.hi)
|
||||
|
||||
let tmp2 = initMpUintImpl(z1.lo shl halfSize)
|
||||
let tmp2 = initMpUintImpl(z1.lo shl halfSize, T)
|
||||
result.lo = tmp2
|
||||
result.lo += z0
|
||||
result.hi = (result.lo < tmp2).toSubtype(T) + z2 + initMpUintImpl(z1.hi)
|
||||
result.hi = (result.lo < tmp2).toSubtype(T) + z2 + initMpUintImpl(z1.hi, type result.hi)
|
||||
|
||||
proc `*`*(x, y: MpUintImpl): MpUintImpl {.noSideEffect, noInit.}=
|
||||
## Multiplication for multi-precision unsigned uint
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./uint_type, size_mpuintimpl
|
||||
import ./uint_type, ./size_mpuintimpl, ./conversion
|
||||
|
||||
|
||||
proc `not`*(x: MpUintImpl): MpUintImpl {.noInit, noSideEffect, inline.}=
|
||||
@ -30,25 +30,23 @@ proc `xor`*(x, y: MpUintImpl): MpUintImpl {.noInit, noSideEffect, inline.}=
|
||||
result.lo = x.lo xor y.lo
|
||||
result.hi = x.hi xor y.hi
|
||||
|
||||
proc `shl`*(x: MpUintImpl, y: SomeInteger): MpUintImpl {.noInit, inline, noSideEffect.}=
|
||||
proc `shl`*(x: MpUintImpl, y: SomeInteger): MpUintImpl {.inline, noSideEffect.}=
|
||||
## Compute the `shift left` operation of x and y
|
||||
# Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1`
|
||||
const halfSize = size_mpuintimpl(x) div 2
|
||||
|
||||
type SubTy = type x.lo
|
||||
|
||||
result.hi = (x.hi shl y) or (x.lo shl (y - halfSize))
|
||||
result.lo = if y < halfSize: x.lo shl y
|
||||
else: 0.SubTy
|
||||
if y < halfSize:
|
||||
result.lo = x.lo shl y
|
||||
|
||||
|
||||
proc `shr`*(x: MpUintImpl, y: SomeInteger): MpUintImpl {.noInit, inline, noSideEffect.}=
|
||||
proc `shr`*(x: MpUintImpl, y: SomeInteger): MpUintImpl {.inline, noSideEffect.}=
|
||||
## Compute the `shift right` operation of x and y
|
||||
# Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1`
|
||||
const halfSize = size_mpuintimpl(x) div 2
|
||||
|
||||
type SubTy = type x.lo
|
||||
let overflow = y < halfSize
|
||||
|
||||
result.lo = (x.lo shr y) or (x.hi shl (y - halfSize)) # the shl is not a mistake
|
||||
result.hi = if y < halfSize: x.hi shr y
|
||||
else: 0.SubTy
|
||||
result.lo = (x.lo shr y) or (
|
||||
if overflow: x.hi shl (halfSize - y) else: x.hi shr (y - halfSize)
|
||||
)
|
||||
if overflow:
|
||||
result.hi = x.hi shr y
|
||||
|
@ -14,18 +14,17 @@ import ./private/bithacks, ./private/conversion,
|
||||
|
||||
import typetraits
|
||||
|
||||
proc initMpUint*(n: SomeInteger, bits: static[int]): MpUint[bits] {.noSideEffect.} =
|
||||
assert n >= 0
|
||||
proc initMpUint*[T: SomeInteger](n: T, bits: static[int]): MpUint[bits] {.noSideEffect.} =
|
||||
assert n >= 0.T
|
||||
when result.data is MpuintImpl:
|
||||
type SubTy = type result.data.lo
|
||||
|
||||
static: echo result.data.type.name
|
||||
let len = n.bit_length
|
||||
if len > bits:
|
||||
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)
|
||||
result.data.lo = initMpUintImpl(n, type result.data.lo)
|
||||
else: # Both have the same size and memory representation
|
||||
when bits == 16:
|
||||
# TODO: If n is int it's not properly converted at the input
|
||||
|
@ -9,5 +9,6 @@
|
||||
|
||||
import test_endianness,
|
||||
test_comparison,
|
||||
test_bitwise,
|
||||
test_addsub,
|
||||
test_muldiv
|
||||
|
@ -14,6 +14,7 @@ suite "Testing bitwise operations":
|
||||
|
||||
let b = a * a
|
||||
let z = 10000'u16
|
||||
assert cast[uint16](b) == z, "Test cannot proceed, something is wrong with the multiplication implementation"
|
||||
|
||||
test "Shift left - by less than half the size of the integer":
|
||||
check: cast[uint16](b) == z # Sanity check
|
||||
|
Loading…
x
Reference in New Issue
Block a user