constantine/tests/test_fp2.nim
Mamy Ratsimbazafy 4ff0e3d90b
Internals refactor + renewed focus on perf (#17)
* Lay out the refactoring objectives and tradeoffs

* Refactor the 32 and 64-bit primitives [skip ci]

* BigInts and Modular BigInts compile

* Make the bigints test compile

* Fix modular reduction

* Fix reduction tests vs GMP

* Implement montegomery mul, pow, inverse, WIP finite field compilation

* Make FiniteField compile

* Fix exponentiation compilation

* Fix Montgomery magic constant computation  for 2^64 words

* Fix typo in non-optimized CIOS - passing finite fields IO tests

* Add limbs comparisons [skip ci]

* Fix on precomputation of the Montgomery magic constant

* Passing all tests including 𝔽p2

* modular addition, the test for mersenne prime was wrong

* update benches

* Fix "nimble test" + typo on out-of-place field addition

* bigint division, normalization is needed: https://travis-ci.com/github/mratsim/constantine/jobs/298359743

* missing conversion in subborrow non-x86 fallback - https://travis-ci.com/github/mratsim/constantine/jobs/298359744

* Fix little-endian serialization

* Constantine32 flag to run 32-bit constantine on 64-bit machines

* IO Field test, ensure that BaseType is used instead of uint64 when the prime can field in uint32

* Implement proper addcarry and subborrow fallback for the compile-time VM

* Fix export issue when the logical wordbitwidth == physical wordbitwidth - passes all tests (32-bit and 64-bit)

* Fix uint128 on ARM

* Fix C++ conditional copy and ARM addcarry/subborrow

* Add investigation for SIGFPE in Travis

* Fix debug display for unsafeDiv2n1n

* multiplexer typo

* moveMem bug in glibc of Ubuntu 16.04?

* Was probably missing an early clobbered register annotation on conditional mov

* Note on Montgomery-friendly moduli

* Strongly suspect a GCC before GCC 7 codegen bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87139)

* hex conversion was (for debugging) not taking requested order into account + inlining comment

* Use 32-bit limbs on ARM64, uint128 builtin __udivti4 bug?

* Revert "Use 32-bit limbs on ARM64, uint128 builtin __udivti4 bug?"

This reverts commit 087f9aa7fb40bbd058d05cbd8eec7fc082911f49.

* Fix subborrow fallback for non-x86 (need to maks the borrow)
2020-03-16 16:33:51 +01:00

265 lines
6.5 KiB
Nim
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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, times, random,
# Internals
../constantine/tower_field_extensions/[abelian_groups, fp2_complex],
../constantine/config/[common, curves],
../constantine/arithmetic/bigints,
# Test utilities
./prng
const Iters = 128
var rng: RngState
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
rng.seed(seed)
echo "test_fp2 xoshiro512** seed: ", seed
# Import: wrap in field element tests in small procedures
# otherwise they will become globals,
# and will create binary size issues.
# Also due to Nim stack scanning,
# having too many elements on the stack (a couple kB)
# will significantly slow down testing (100x is possible)
suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
test "Fp2 '1' coordinates in canonical domain":
template test(C: static Curve) =
block:
proc testInstance() =
let oneFp2 = block:
var O{.noInit.}: Fp2[C]
O.setOne()
O
let oneBig = block:
var O{.noInit.}: typeof(C.Mod.mres)
O.setOne()
O
var r: typeof(C.Mod.mres)
r.redc(oneFp2.c0.mres, C.Mod.mres, C.getNegInvModWord(), canUseNoCarryMontyMul = false)
check:
bool(r == oneBig)
bool(oneFp2.c1.mres.isZero())
test(BN254)
test(BLS12_381)
test(P256)
test(Secp256k1)
test "Squaring 1 returns 1":
template test(C: static Curve) =
block:
proc testInstance() =
let One = block:
var O{.noInit.}: Fp2[C]
O.setOne()
O
block:
var r{.noinit.}: Fp2[C]
r.square(One)
check: bool(r == One)
block:
var r{.noinit.}: Fp2[C]
r.prod(One, One)
check: bool(r == One)
testInstance()
test(BN254)
test(BLS12_381)
test(P256)
test(Secp256k1)
test "Multiplication by 0 and 1":
template test(C: static Curve, body: untyped) =
block:
proc testInstance() =
let Zero {.inject.} = block:
var Z{.noInit.}: Fp2[C]
Z.setZero()
Z
let One {.inject.} = block:
var O{.noInit.}: Fp2[C]
O.setOne()
O
for _ in 0 ..< Iters:
let x {.inject.} = rng.random(Fp2[C])
var r{.noinit, inject.}: Fp2[C]
body
testInstance()
test(BN254):
r.prod(x, Zero)
check: bool(r == Zero)
test(BN254):
r.prod(Zero, x)
check: bool(r == Zero)
test(BN254):
r.prod(x, One)
check: bool(r == x)
test(BN254):
r.prod(One, x)
check: bool(r == x)
test(BLS12_381):
r.prod(x, Zero)
check: bool(r == Zero)
test(BLS12_381):
r.prod(Zero, x)
check: bool(r == Zero)
test(BLS12_381):
r.prod(x, One)
check: bool(r == x)
test(BLS12_381):
r.prod(One, x)
check: bool(r == x)
test(P256):
r.prod(x, Zero)
check: bool(r == Zero)
test(P256):
r.prod(Zero, x)
check: bool(r == Zero)
test(P256):
r.prod(x, One)
check: bool(r == x)
test(P256):
r.prod(One, x)
check: bool(r == x)
test(Secp256k1):
r.prod(x, Zero)
check: bool(r == Zero)
test(Secp256k1):
r.prod(Zero, x)
check: bool(r == Zero)
test(Secp256k1):
r.prod(x, One)
check: bool(r == x)
test(Secp256k1):
r.prod(One, x)
check: bool(r == x)
test "𝔽p2 = 𝔽p[𝑖] addition is associative and commutative":
proc abelianGroup(curve: static Curve) =
for _ in 0 ..< Iters:
let a = rng.random(Fp2[curve])
let b = rng.random(Fp2[curve])
let c = rng.random(Fp2[curve])
var tmp1{.noInit.}, tmp2{.noInit.}: Fp2[curve]
# r0 = (a + b) + c
tmp1.sum(a, b)
tmp2.sum(tmp1, c)
let r0 = tmp2
# r1 = a + (b + c)
tmp1.sum(b, c)
tmp2.sum(a, tmp1)
let r1 = tmp2
# r2 = (a + c) + b
tmp1.sum(a, c)
tmp2.sum(tmp1, b)
let r2 = tmp2
# r3 = a + (c + b)
tmp1.sum(c, b)
tmp2.sum(a, tmp1)
let r3 = tmp2
# r4 = (c + a) + b
tmp1.sum(c, a)
tmp2.sum(tmp1, b)
let r4 = tmp2
# ...
check:
bool(r0 == r1)
bool(r0 == r2)
bool(r0 == r3)
bool(r0 == r4)
abelianGroup(BN254)
abelianGroup(BLS12_381)
abelianGroup(Secp256k1)
abelianGroup(P256)
test "𝔽p2 = 𝔽p[𝑖] multiplication is associative and commutative":
proc commutativeRing(curve: static Curve) =
for _ in 0 ..< Iters:
let a = rng.random(Fp2[curve])
let b = rng.random(Fp2[curve])
let c = rng.random(Fp2[curve])
var tmp1{.noInit.}, tmp2{.noInit.}: Fp2[curve]
# r0 = (a * b) * c
tmp1.prod(a, b)
tmp2.prod(tmp1, c)
let r0 = tmp2
# r1 = a * (b * c)
tmp1.prod(b, c)
tmp2.prod(a, tmp1)
let r1 = tmp2
# r2 = (a * c) * b
tmp1.prod(a, c)
tmp2.prod(tmp1, b)
let r2 = tmp2
# r3 = a * (c * b)
tmp1.prod(c, b)
tmp2.prod(a, tmp1)
let r3 = tmp2
# r4 = (c * a) * b
tmp1.prod(c, a)
tmp2.prod(tmp1, b)
let r4 = tmp2
# ...
check:
bool(r0 == r1)
bool(r0 == r2)
bool(r0 == r3)
bool(r0 == r4)
commutativeRing(BN254)
commutativeRing(BLS12_381)
commutativeRing(Secp256k1)
commutativeRing(P256)
test "𝔽p2 = 𝔽p[𝑖] extension field multiplicative inverse":
proc mulInvOne(curve: static Curve) =
var one: Fp2[curve]
one.setOne()
var aInv, r{.noInit.}: Fp2[curve]
for _ in 0 ..< Iters:
let a = rng.random(Fp2[curve])
aInv.inv(a)
r.prod(a, aInv)
check: bool(r == one)
r.prod(aInv, a)
check: bool(r == one)
mulInvOne(BN254)
mulInvOne(BLS12_381)
mulInvOne(Secp256k1)
mulInvOne(P256)