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:
Mamy Ratsimbazafy 2018-03-28 20:45:39 +02:00 committed by GitHub
parent c8190df152
commit 24bd2fc986
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 29 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -9,5 +9,6 @@
import test_endianness,
test_comparison,
test_bitwise,
test_addsub,
test_muldiv

View File

@ -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