diff --git a/src/uint_binary_ops.nim b/src/uint_binary_ops.nim index 601efbe..6f5cf2b 100644 --- a/src/uint_binary_ops.nim +++ b/src/uint_binary_ops.nim @@ -10,11 +10,11 @@ proc `+=`*[T: MpUint](x: var T, y: T) {.noSideEffect.}= # # Optimized assembly should contain adc instruction (add with carry) # Clang on MacOS does with the -d:release switch and MpUint[uint32] (uint64) - type MpBase = type x.lo + type SubT = type x.lo let tmp = x.lo x.lo += y.lo - x.hi += MpBase(x.lo < tmp) + y.hi + x.hi += SubT(x.lo < tmp) + y.hi proc `+`*[T: MpUint](x, y: T): T {.noSideEffect, noInit, inline.}= # Addition for multi-precision unsigned int @@ -26,11 +26,11 @@ proc `-=`*[T: MpUint](x: var T, y: T) {.noSideEffect.}= # # Optimized assembly should contain sbb instruction (substract with borrow) # Clang on MacOS does with the -d:release switch and MpUint[uint32] (uint64) - type MpBase = type x.lo + type SubT = type x.lo let tmp = x.lo x.lo -= y.lo - x.hi -= MpBase(x.lo > tmp) + y.hi + x.hi -= SubT(x.lo > tmp) + y.hi proc `-`*[T: MpUint](x, y: T): T {.noSideEffect, noInit, inline.}= # Substraction for multi-precision unsigned int @@ -148,4 +148,12 @@ proc divmod*[T: BaseUint](x, y: T): tuple[quot, rem: T] {.noSideEffect.}= # - Python implementation: https://bugs.python.org/file11060/fast_div.py and discussion https://bugs.python.org/issue3451 # - C++ implementation: https://github.com/linbox-team/givaro/blob/master/src/kernel/recint/rudiv.h # - The Handbook of Elliptic and Hyperelliptic Cryptography Algorithm 10.35 on page 188 has a more explicit version of the div2NxN algorithm. This algorithm is directly recursive and avoids the mutual recursion of the original paper's calls between div2NxN and div3Nx2N. - # - Comparison of fast division algorithms fro large integers: http://bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/Lectures/Hasselstrom2003.pdf \ No newline at end of file + # - Comparison of fast division algorithms fro large integers: http://bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/Lectures/Hasselstrom2003.pdf + +proc `div`*[T: BaseUint](x, y: T): T {.inline, noSideEffect.} = + ## Division operation for multi-precision unsigned uint + divmod(x,y).quot + +proc `mod`*[T: BaseUint](x, y: T): T {.inline, noSideEffect.} = + ## Division operation for multi-precision unsigned uint + divmod(x,y).rem \ No newline at end of file diff --git a/src/uint_bitwise_ops.nim b/src/uint_bitwise_ops.nim index 59a789a..6b45c9b 100644 --- a/src/uint_bitwise_ops.nim +++ b/src/uint_bitwise_ops.nim @@ -29,24 +29,26 @@ proc `shr`*[T: MpUint](x: T, y: SomeInteger): T {.noInit, noSideEffect.} proc `shl`*[T: MpUint](x: T, y: SomeInteger): T {.noInit, noSideEffect.}= ## Compute the `shift left` operation of x and y + # Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1` let halfSize = T.sizeof * 4 - type Sub = type x.lo + type SubT = 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.Sub + else: 0.SubT proc `shr`*[T: MpUint](x: T, y: SomeInteger): T {.noInit, noSideEffect.}= ## Compute the `shift right` operation of x and y + # Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1` let halfSize = T.sizeof * 4 - type Sub = type x.lo + type SubT = type x.lo 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.Sub + else: 0.SubT diff --git a/tests/all_tests.nim b/tests/all_tests.nim index a49e60d..abb11d9 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -2,6 +2,6 @@ # Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).$ import test_endianness, + test_comparison, test_addsub, - test_mul, - test_comparison \ No newline at end of file + test_muldiv \ No newline at end of file diff --git a/tests/test_mul.nim b/tests/test_muldiv.nim similarity index 75% rename from tests/test_mul.nim rename to tests/test_muldiv.nim index bfda742..c53d7de 100644 --- a/tests/test_mul.nim +++ b/tests/test_muldiv.nim @@ -24,4 +24,15 @@ suite "Testing multiplication implementation": let b = initMpUint(1_000_000_000, uint32) let c = initMpUint(1_000, uint32) - check: cast[uint64](a*b*c) == 1_000_000_000_000_000_000_000'u64 # need 70-bits \ No newline at end of file + check: cast[uint64](a*b*c) == 1_000_000_000_000_000_000_000'u64 # need 70-bits + + +suite "Testing division and modulo implementation": + test "Divmod returns the correct result": + + let a = initMpUint(100, uint32) + let b = initMpUint(13, uint32) + let qr = a.divmod(b) + + check: cast[uint64](qr.quot) == 7'u64 + check: cast[uint64](qr.rem) == 9'u64