diff --git a/src/private/bithacks.nim b/src/private/bithacks.nim index 8470845..bf50436 100644 --- a/src/private/bithacks.nim +++ b/src/private/bithacks.nim @@ -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 diff --git a/src/private/conversion.nim b/src/private/conversion.nim index 53ffc57..8b90193 100644 --- a/src/private/conversion.nim +++ b/src/private/conversion.nim @@ -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 diff --git a/src/private/uint_binary_ops.nim b/src/private/uint_binary_ops.nim index 8c5b5f2..ee48a71 100644 --- a/src/private/uint_binary_ops.nim +++ b/src/private/uint_binary_ops.nim @@ -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 diff --git a/src/private/uint_bitwise_ops.nim b/src/private/uint_bitwise_ops.nim index d1302d1..9918cc1 100644 --- a/src/private/uint_bitwise_ops.nim +++ b/src/private/uint_bitwise_ops.nim @@ -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 diff --git a/src/uint_init.nim b/src/uint_init.nim index 9aa8986..c563372 100644 --- a/src/uint_init.nim +++ b/src/uint_init.nim @@ -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 diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 785a027..bf8fa19 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -9,5 +9,6 @@ import test_endianness, test_comparison, + test_bitwise, test_addsub, test_muldiv diff --git a/tests/test_bitwise.nim b/tests/test_bitwise.nim index bf5ceb9..0929492 100644 --- a/tests/test_bitwise.nim +++ b/tests/test_bitwise.nim @@ -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