Fix #30 - modulo failures (#32)

* Add corner cases to test suite

* Add failing div2n1n case

* Fix test and fix #30

* Add to tests

* Fix failing tests due to Nim string change between Apr 27 and Apr 29 (doc: 5237ef4f52)
This commit is contained in:
Mamy Ratsimbazafy 2018-05-02 17:49:31 +02:00 committed by GitHub
parent d68c0bf44a
commit 0ee1fb3ec8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 7 deletions

View File

@ -137,19 +137,19 @@ func div2n1n[T: SomeunsignedInt](q, r: var T, n_hi, n_lo, d: T) =
halfSize = size div 2 halfSize = size div 2
halfMask = (1.T shl halfSize) - 1.T halfMask = (1.T shl halfSize) - 1.T
template halfQR(n_hi, n_lo, d_hi, d_lo: T): tuple[q,r: T] = template halfQR(n_hi, n_lo, d, d_hi, d_lo: T): tuple[q,r: T] =
var (q, r) = divmod(n_hi, d_hi) var (q, r) = divmod(n_hi, d_hi)
let m = q * d_lo let m = q * d_lo
r = (r shl halfSize) or n_lo var r = (r shl halfSize) or n_lo
# Fix the reminder, we're at most 2 iterations off # Fix the reminder, we're at most 2 iterations off
if r < m: if r < m:
dec q dec q
r += d_hi r += d
if r >= d_hi and r < m: if r >= d and r < m:
dec q dec q
r += d_hi r += d
r -= m r -= m
(q, r) (q, r)
@ -160,10 +160,10 @@ func div2n1n[T: SomeunsignedInt](q, r: var T, n_hi, n_lo, d: T) =
n_lolo = nlo and halfMask n_lolo = nlo and halfMask
# First half of the quotient # First half of the quotient
let (q1, r1) = halfQR(n_hi, n_lohi, d_hi, d_lo) let (q1, r1) = halfQR(n_hi, n_lohi, d, d_hi, d_lo)
# Second half # Second half
let (q2, r2) = halfQR(r1, n_lolo, d_hi, d_lo) let (q2, r2) = halfQR(r1, n_lolo, d, d_hi, d_lo)
q = (q1 shl halfSize) or q2 q = (q1 shl halfSize) or q2
r = r2 r = r2

View File

@ -20,6 +20,13 @@ proc test(name: string, lang: string = "c") =
switch("out", ("./build/" & name)) switch("out", ("./build/" & name))
setCommand lang, "tests/" & name & ".nim" setCommand lang, "tests/" & name & ".nim"
task test_internal_debug, "Run tests for internal procs - test implementation (StUint[64] = 2x uint32":
switch("define", "mpint_test")
test "internal"
task test_internal_release, "Run tests for internal procs - prod implementation (StUint[64] = uint64":
test "internal"
task test_debug, "Run all tests - test implementation (StUint[64] = 2x uint32": task test_debug, "Run all tests - test implementation (StUint[64] = 2x uint32":
switch("define", "mpint_test") switch("define", "mpint_test")
test "all_tests" test "all_tests"
@ -52,6 +59,8 @@ task test_property_uint256_release, "Run random tests (release mode) vs TTMath o
test "property_based", "cpp" test "property_based", "cpp"
task test, "Run all tests - test and production implementation": task test, "Run all tests - test and production implementation":
exec "nimble test_internal_debug"
exec "nimble test_internal_release"
exec "nimble test_debug" exec "nimble test_debug"
exec "nimble test_release" exec "nimble test_release"
exec "nimble test_property_debug" exec "nimble test_property_debug"

10
tests/internal.nim Normal file
View File

@ -0,0 +1,10 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import internal_uint_div

View File

@ -0,0 +1,60 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# Test implementation of internal proc:
include ../src/private/uint_div
import unittest
suite "implementation of internal division procecures":
test "Division of 2 words by 1 - specific carry case (issue #30)":
# // Reference implementation from glibc: udiv.h
# #include <stdint.h>
# typedef uint32_t USItype;
# #define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */\
# __asm__ ("divl %4" /* stringification in K&R C */ \
# : "=a" (q), "=d" (r) \
# : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "rm" ((USItype)(dx)))
################################
# #include "udiv.h"
# #include <stdio.h>
# int main( int argc, const char* argv[] )
# {
# UWtype q, r, n1, n0, d;
# q = 0;
# r = 0;
# n1 = 233;
# n0 = 1717253765;
# d = 2659025738;
# udiv_qrnnd(q, r, n1, n0, d);
# printf("q: %u\n", q);
# printf("r: %u\n", r);
# }
###############################
var q, r: uint32
let
n1 = uint32 233
n0 = uint32 1717253765
d = uint32 2659025738
div2n1n(q, r, n1, n0, d)
check:
q == 376
r == 2650956245'u32

View File

@ -68,3 +68,29 @@ suite "Testing unsigned int division and modulo implementation":
check: q == 123456789123456789'u64 check: q == 123456789123456789'u64
check: r == 0'u64 check: r == 0'u64
suite "Testing specific failures highlighted by property-based testing":
test "Modulo: 65696211516342324 mod 174261910798982":
let u = 65696211516342324'u64
let v = 174261910798982'u64
let a = cast[Stuint[64]](u)
let b = cast[Stuint[64]](v)
let z = u mod v
let tz = cast[uint64](a mod b)
check: z == tz
test "Modulo: 15080397990160655 mod 600432699691":
let u = 15080397990160655'u64
let v = 600432699691'u64
let a = cast[Stuint[64]](u)
let b = cast[Stuint[64]](v)
let z = u mod v
let tz = cast[uint64](a mod b)
check: z == tz