Temp switch to uint32 words for testing modulo. Remove tests that depend on word size

This commit is contained in:
Mamy André-Ratsimbazafy 2020-02-10 00:16:46 +01:00
parent 166a1075b1
commit 30f8756dfc
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
5 changed files with 64 additions and 31 deletions

View File

@ -40,8 +40,8 @@
import ./primitives
from ./private/primitives_internal import unsafeDiv2n1n, unsafeExtendedPrecMul
type Word* = Ct[uint64]
type BaseType* = uint64 # Exported type for conversion in "normal integers"
type Word* = Ct[uint32]
type BaseType* = uint32 # Exported type for conversion in "normal integers"
const WordBitSize* = sizeof(Word) * 8 - 1
## Limbs are 63-bit by default
@ -217,16 +217,16 @@ func shlAddMod[bits](a: var BigInt[bits], c: Word, M: BigInt[bits]) =
const R = bits and WordBitSize # R = bits mod 64
when R == 0: # If the number of bits is a multiple of 64
let a1 = a.limbs[^2] #
let a0 = a.limbs[^1] #
moveMem(a.limbs[1].addr, a.limbs[0].addr, (len-1) * Word.sizeof) # we can just shift words
a.limbs[0] = c # and replace the first one by c
let a1 = a.limbs[^1]
let m0 = M.limbs[^1]
else: # Need to deal with partial word shifts at the edge.
let a1 = ((a.limbs[^2] shl (WordBitSize-R)) or (a.limbs[^3] shr R)) and MaxWord
let a0 = ((a.limbs[^1] shl (WordBitSize-R)) or (a.limbs[^2] shr R)) and MaxWord
moveMem(a.limbs[1].addr, a.limbs[0].addr, (len-1) * Word.sizeof)
a.limbs[0] = c
let a1 = ((a.limbs[^1] shl (WordBitSize-R)) or (a.limbs[^2] shr R)) and MaxWord
let m0 = ((M.limbs[^1] shl (WordBitSize-R)) or (M.limbs[^2] shr R)) and MaxWord
# m0 has its high bit set. (a0, a1)/p0 fits in a limb.
@ -256,8 +256,8 @@ func shlAddMod[bits](a: var BigInt[bits], c: Word, M: BigInt[bits]) =
block: # q*p
var qp_hi: Word
unsafeExtendedPrecMul(qp_hi, qp_lo, q, M.limbs[i]) # q * p
assert qp_lo.isMsbSet.not.bool
assert carry.isMsbSet.not.bool
# assert qp_lo.isMsbSet.not.bool
# assert carry.isMsbSet.not.bool
qp_lo += carry # Add carry from previous limb
let qp_carry = qp_lo.isMsbSet
carry = mux(qp_carry, qp_hi + One, qp_hi) # New carry

View File

@ -57,6 +57,12 @@ func unsafeExtendedPrecMul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.}
else:
asm_x86_64_extMul(T(hi), T(lo), T(a), T(b))
func unsafeExtendedPrecMul*(hi, lo: var Ct[uint32], a, b: Ct[uint32]) {.inline.}=
## Extended precision multiplication uint32 * uint32 --> uint32
let extMul = uint64(a) * uint64(b)
hi = (Ct[uint32])(extMul shr 32)
lo = (Ct[uint32])(extMul and 31)
func asm_x86_64_div2n1n(q, r: var uint64, n_hi, n_lo, d: uint64) {.inline.}=
## Division uint128 by uint64
## Warning ⚠️ :
@ -118,6 +124,20 @@ func unsafeDiv2n1n*(q, r: var Ct[uint64], n_hi, n_lo, d: Ct[uint64]) {.inline.}=
else:
asm_x86_64_div2n1n(T(q), T(r), T(n_hi), T(n_lo), T(d))
func unsafeDiv2n1n*(q, r: var Ct[uint32], n_hi, n_lo, d: Ct[uint32]) {.inline.}=
## Division uint64 by uint32
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint32
## - if n_hi > d result is undefined
##
## To avoid issues, n_hi, n_lo, d should be normalized.
## i.e. shifted (== multiplied by the same power of 2)
## so that the most significant bit in d is set.
let dividend = (uint64(n_hi) shl 32) or uint64(n_lo)
let divisor = uint64(d)
q = (Ct[uint32])(dividend div divisor)
r = (Ct[uint32])(dividend mod divisor)
when isMainModule:
block: # Multiplication
var hi, lo: uint64

View File

@ -124,22 +124,6 @@ suite "Arithmetic operations - Addition":
bool(a == c)
not bool(carry)
block:
var a = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.add(b, ctrue(Word))
# BigInt[128] takes 3 Words as a BigInt Word is 63-bit
var ab: array[3*sizeof(Word), byte]
ab.dumpRawUint(a, littleEndian)
# Given that it uses 3 words, we actually can store 2^128 in BigInt[128]
var c: array[3*sizeof(Word), byte]
c[16] = 1
check:
c == ab
not bool(carry) # carry can only happen within limbs
suite "Modular operations - small modulus":
# Vectors taken from Stint - https://github.com/status-im/nim-stint
test "100 mod 13":

View File

@ -0,0 +1,38 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Standard library
unittest, random, strutils,
# Third-party
../constantine/[io, bigints, primitives]
suite "Bigints - Multiprecision modulo":
test "bitsize 237 mod bitsize 192":
let a = BigInt[237].fromHex("0x123456789012345678901234567890123456789012345678901234567890")
let m = BigInt[192].fromHex("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB")
let expected = BigInt[192].fromHex("0x34567890123456789012345678901234567886f8091a3087")
var r: BigInt[192]
r.reduce(a, m)
check:
bool(r == expected)
test "bitsize 365 mod bitsize 258":
let a = BigInt[365].fromHex("0x6c8ae85a6cab4bc530b91177e3f399894ff1fe335b6b3fcdc577ea4f8d754bbe71a6353e8609a4769ec8c56727a")
let m = BigInt[258].fromHex("0x2cadadfa2bb7d7141ad9728d6955ddb68a8b81ecb6a7610575bf4d6f562b09f0d")
let expected = BigInt[258].fromHex("0xb7c8844f534bf298645dc118384e975245c1a44ba4b0bca8a04c9db0c9035b9")
var r: BigInt[258]
r.reduce(a, m)
check:
bool(r == expected)

View File

@ -23,15 +23,6 @@ suite "IO":
T(big.limbs[0]) == 0
T(big.limbs[1]) == 0
block: # 2^63 is properly represented on 2 limbs
let x = 1'u64 shl 63
let x_bytes = cast[array[8, byte]](x)
let big = BigInt[64].fromRawUint(x_bytes, cpuEndian)
check:
T(big.limbs[0]) == 0
T(big.limbs[1]) == 1
test "Parsing and dumping round-trip on uint64":
block:
# "Little-endian" - 2^63