Temp switch to uint32 words for testing modulo. Remove tests that depend on word size
This commit is contained in:
parent
166a1075b1
commit
30f8756dfc
|
@ -40,8 +40,8 @@
|
||||||
import ./primitives
|
import ./primitives
|
||||||
from ./private/primitives_internal import unsafeDiv2n1n, unsafeExtendedPrecMul
|
from ./private/primitives_internal import unsafeDiv2n1n, unsafeExtendedPrecMul
|
||||||
|
|
||||||
type Word* = Ct[uint64]
|
type Word* = Ct[uint32]
|
||||||
type BaseType* = uint64 # Exported type for conversion in "normal integers"
|
type BaseType* = uint32 # Exported type for conversion in "normal integers"
|
||||||
|
|
||||||
const WordBitSize* = sizeof(Word) * 8 - 1
|
const WordBitSize* = sizeof(Word) * 8 - 1
|
||||||
## Limbs are 63-bit by default
|
## 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
|
const R = bits and WordBitSize # R = bits mod 64
|
||||||
|
|
||||||
when R == 0: # If the number of bits is a multiple of 64
|
when R == 0: # If the number of bits is a multiple of 64
|
||||||
let a1 = a.limbs[^2] #
|
|
||||||
let a0 = a.limbs[^1] #
|
let a0 = a.limbs[^1] #
|
||||||
moveMem(a.limbs[1].addr, a.limbs[0].addr, (len-1) * Word.sizeof) # we can just shift words
|
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
|
a.limbs[0] = c # and replace the first one by c
|
||||||
|
let a1 = a.limbs[^1]
|
||||||
let m0 = M.limbs[^1]
|
let m0 = M.limbs[^1]
|
||||||
else: # Need to deal with partial word shifts at the edge.
|
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
|
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)
|
moveMem(a.limbs[1].addr, a.limbs[0].addr, (len-1) * Word.sizeof)
|
||||||
a.limbs[0] = c
|
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
|
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.
|
# 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
|
block: # q*p
|
||||||
var qp_hi: Word
|
var qp_hi: Word
|
||||||
unsafeExtendedPrecMul(qp_hi, qp_lo, q, M.limbs[i]) # q * p
|
unsafeExtendedPrecMul(qp_hi, qp_lo, q, M.limbs[i]) # q * p
|
||||||
assert qp_lo.isMsbSet.not.bool
|
# assert qp_lo.isMsbSet.not.bool
|
||||||
assert carry.isMsbSet.not.bool
|
# assert carry.isMsbSet.not.bool
|
||||||
qp_lo += carry # Add carry from previous limb
|
qp_lo += carry # Add carry from previous limb
|
||||||
let qp_carry = qp_lo.isMsbSet
|
let qp_carry = qp_lo.isMsbSet
|
||||||
carry = mux(qp_carry, qp_hi + One, qp_hi) # New carry
|
carry = mux(qp_carry, qp_hi + One, qp_hi) # New carry
|
||||||
|
|
|
@ -57,6 +57,12 @@ func unsafeExtendedPrecMul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.}
|
||||||
else:
|
else:
|
||||||
asm_x86_64_extMul(T(hi), T(lo), T(a), T(b))
|
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.}=
|
func asm_x86_64_div2n1n(q, r: var uint64, n_hi, n_lo, d: uint64) {.inline.}=
|
||||||
## Division uint128 by uint64
|
## Division uint128 by uint64
|
||||||
## Warning ⚠️ :
|
## Warning ⚠️ :
|
||||||
|
@ -118,6 +124,20 @@ func unsafeDiv2n1n*(q, r: var Ct[uint64], n_hi, n_lo, d: Ct[uint64]) {.inline.}=
|
||||||
else:
|
else:
|
||||||
asm_x86_64_div2n1n(T(q), T(r), T(n_hi), T(n_lo), T(d))
|
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:
|
when isMainModule:
|
||||||
block: # Multiplication
|
block: # Multiplication
|
||||||
var hi, lo: uint64
|
var hi, lo: uint64
|
||||||
|
|
|
@ -124,22 +124,6 @@ suite "Arithmetic operations - Addition":
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
not bool(carry)
|
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":
|
suite "Modular operations - small modulus":
|
||||||
# Vectors taken from Stint - https://github.com/status-im/nim-stint
|
# Vectors taken from Stint - https://github.com/status-im/nim-stint
|
||||||
test "100 mod 13":
|
test "100 mod 13":
|
||||||
|
|
|
@ -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)
|
|
@ -23,15 +23,6 @@ suite "IO":
|
||||||
T(big.limbs[0]) == 0
|
T(big.limbs[0]) == 0
|
||||||
T(big.limbs[1]) == 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":
|
test "Parsing and dumping round-trip on uint64":
|
||||||
block:
|
block:
|
||||||
# "Little-endian" - 2^63
|
# "Little-endian" - 2^63
|
||||||
|
|
Loading…
Reference in New Issue