diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index eac7944..da602c0 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -14,11 +14,9 @@ import # Internals - ../constantine/config/[common, curves], + ../constantine/config/curves, ../constantine/arithmetic, - ../constantine/io/[io_bigints, io_fields], - ../constantine/primitives, - ../constantine/tower_field_extensions/[abelian_groups, fp2_complex, fp6_1_plus_i], + ../constantine/towers, # Helpers ../helpers/[timers, prng, static_for], # Standard library diff --git a/benchmarks/bench_fp.nim b/benchmarks/bench_fp.nim index 3633fe7..b3a74d0 100644 --- a/benchmarks/bench_fp.nim +++ b/benchmarks/bench_fp.nim @@ -55,5 +55,6 @@ proc main() = main() echo "Notes:" -echo " GCC is significantly slower than Clang on multiprecision arithmetic." -echo " The simplest operations might be optimized away by the compiler." +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - The simplest operations might be optimized away by the compiler." +echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" diff --git a/benchmarks/bench_fp12.nim b/benchmarks/bench_fp12.nim index 9742673..ce5fb84 100644 --- a/benchmarks/bench_fp12.nim +++ b/benchmarks/bench_fp12.nim @@ -9,7 +9,7 @@ import # Internals ../constantine/config/curves, - ../constantine/tower_field_extensions/[abelian_groups, fp12_quad_fp6], + ../constantine/towers, # Helpers ../helpers/static_for, ./bench_fields_template, @@ -27,14 +27,14 @@ const Iters = 10_000 const InvIters = 1000 const AvailableCurves = [ # Pairing-Friendly curves - BN254_Nogami, + # BN254_Nogami, BN254_Snarks, BLS12_377, - BLS12_381, - BN446, - FKM12_447, - BLS12_461, - BN462 + BLS12_381 + # BN446, + # FKM12_447, + # BLS12_461, + # BN462 ] proc main() = @@ -52,4 +52,6 @@ proc main() = main() echo "Notes:" -echo " GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." diff --git a/benchmarks/bench_fp2.nim b/benchmarks/bench_fp2.nim index 2002e34..ea16f4e 100644 --- a/benchmarks/bench_fp2.nim +++ b/benchmarks/bench_fp2.nim @@ -9,7 +9,7 @@ import # Internals ../constantine/config/curves, - ../constantine/tower_field_extensions/[abelian_groups, fp2_complex], + ../constantine/towers, # Helpers ../helpers/static_for, ./bench_fields_template, @@ -27,14 +27,14 @@ const Iters = 1_000_000 const InvIters = 1000 const AvailableCurves = [ # Pairing-Friendly curves - BN254_Nogami, + # BN254_Nogami, BN254_Snarks, BLS12_377, - BLS12_381, - BN446, - FKM12_447, - BLS12_461, - BN462 + BLS12_381 + # BN446, + # FKM12_447, + # BLS12_461, + # BN462 ] proc main() = @@ -52,4 +52,6 @@ proc main() = main() echo "Notes:" -echo " GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." diff --git a/benchmarks/bench_fp6.nim b/benchmarks/bench_fp6.nim index 1b130e5..b341dbe 100644 --- a/benchmarks/bench_fp6.nim +++ b/benchmarks/bench_fp6.nim @@ -9,7 +9,7 @@ import # Internals ../constantine/config/curves, - ../constantine/tower_field_extensions/[abelian_groups, fp6_1_plus_i], + ../constantine/towers, # Helpers ../helpers/static_for, ./bench_fields_template, @@ -27,14 +27,14 @@ const Iters = 1_000_000 const InvIters = 1000 const AvailableCurves = [ # Pairing-Friendly curves - BN254_Nogami, + # BN254_Nogami, BN254_Snarks, BLS12_377, - BLS12_381, - BN446, - FKM12_447, - BLS12_461, - BN462 + BLS12_381 + # BN446, + # FKM12_447, + # BLS12_461, + # BN462 ] proc main() = @@ -52,4 +52,6 @@ proc main() = main() echo "Notes:" -echo " GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" +echo " - The tower of extension fields chosen can lead to a large difference of performance between primes of similar bitwidth." diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 6a21722..af0b9d0 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -416,3 +416,8 @@ func `*=`*(a: var Fp, b: static int) {.inline.} = a += t4 else: {.error: "Multiplication by this small int not implemented".} + +func `*`*(b: static int, a: Fp): Fp {.noinit, inline.} = + ## Multiplication by a small integer known at compile-time + result = a + result *= b diff --git a/constantine/arithmetic/finite_fields_inversion.nim b/constantine/arithmetic/finite_fields_inversion.nim index 8d10a9f..661a884 100644 --- a/constantine/arithmetic/finite_fields_inversion.nim +++ b/constantine/arithmetic/finite_fields_inversion.nim @@ -152,6 +152,11 @@ func invmod_addchain_bn[C](r: var Fp[C], a: Fp[C]) = func inv*(r: var Fp, a: Fp) = ## Inversion modulo p + ## + ## The inverse of 0 is 0. + ## Incidentally this avoids extra check + ## to convert Jacobian and Projective coordinates + ## to affine for elliptic curve # For now we don't activate the addition chains # neither for Secp256k1 nor BN curves # Performance is slower than GCD diff --git a/constantine/arithmetic/limbs_modular.nim b/constantine/arithmetic/limbs_modular.nim index 2e3966f..726ece0 100644 --- a/constantine/arithmetic/limbs_modular.nim +++ b/constantine/arithmetic/limbs_modular.nim @@ -65,6 +65,8 @@ func steinsGCD*(v: var Limbs, a: Limbs, F, M: Limbs, bits: int, mp1div2: Limbs) ## ## This takes (M+1)/2 (mp1div2) as a precomputed parameter as a slight optimization ## in stack size and speed. + ## + ## The inverse of 0 is 0. # Ideally we need registers for a, b, u, v # but: diff --git a/constantine/tower_field_extensions/abelian_groups.nim b/constantine/tower_field_extensions/abelian_groups.nim deleted file mode 100644 index 65b46c0..0000000 --- a/constantine/tower_field_extensions/abelian_groups.nim +++ /dev/null @@ -1,187 +0,0 @@ -# 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 - ../arithmetic, - ../config/common, - ../primitives - -# ############################################################ -# -# Algebraic concepts -# -# ############################################################ -# Too heavy on the Nim compiler, we just rely on generic instantiation -# to complain if the base field procedures don't exist. - -# type -# AbelianGroup* {.explain.} = concept a, b, var mA, var mR -# setZero(mA) -# setOne(mA) -# `+=`(mA, b) -# `-=`(mA, b) -# double(mR, a) -# sum(mR, a, b) -# diff(mR, a, b) - -# ############################################################ -# -# Cubic Extension fields -# -# ############################################################ - -type - CubicExtAddGroup* = concept x - ## Cubic extension fields - Abelian Additive Group concept - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField - x.c2 is BaseField - -func `==`*(a, b: CubicExtAddGroup): CTBool[Word] = - ## Constant-time equality check - (a.c0 == b.c0) and (a.c1 == b.c1) and (a.c2 == b.c2) - -func setZero*(a: var CubicExtAddGroup) = - ## Set ``a`` to zero in the extension field - ## Coordinates 0 + 0 w + 0 w² - ## with w the solution of f(x) = x³ - µ = 0 - a.c0.setZero() - a.c1.setZero() - a.c2.setZero() - -func setOne*(a: var CubicExtAddGroup) = - ## Set ``a`` to one in the extension field - ## Coordinates 1 + 0 w + 0 w² - ## with w the solution of f(x) = x³ - µ = 0 - a.c0.setOne() - a.c1.setZero() - a.c2.setZero() - -func `+=`*(a: var CubicExtAddGroup, b: CubicExtAddGroup) = - ## Addition in the extension field - a.c0 += b.c0 - a.c1 += b.c1 - a.c2 += b.c2 - -func `-=`*(a: var CubicExtAddGroup, b: CubicExtAddGroup) = - ## Substraction in the extension field - a.c0 -= b.c0 - a.c1 -= b.c1 - a.c2 -= b.c2 - -func double*(r: var CubicExtAddGroup, a: CubicExtAddGroup) = - ## Double in the extension field - r.c0.double(a.c0) - r.c1.double(a.c1) - r.c2.double(a.c2) - -func double*(a: var CubicExtAddGroup) = - ## Double in the extension field - double(a.c0) - double(a.c1) - double(a.c2) - -func sum*(r: var CubicExtAddGroup, a, b: CubicExtAddGroup) = - ## Sum ``a`` and ``b`` into r - r.c0.sum(a.c0, b.c0) - r.c1.sum(a.c1, b.c1) - r.c2.sum(a.c2, b.c2) - -func diff*(r: var CubicExtAddGroup, a, b: CubicExtAddGroup) = - ## Difference of ``a`` by `b`` into r - r.c0.diff(a.c0, b.c0) - r.c1.diff(a.c1, b.c1) - r.c2.diff(a.c2, b.c2) - -func neg*(r: var CubicExtAddGroup, a: CubicExtAddGroup) = - ## Negate ``a`` and store the result into r - r.c0.neg(a.c0) - r.c1.neg(a.c1) - r.c2.neg(a.c2) - -# TODO: The type system is lost with out-of-place result concepts -# func `+`*(a, b: CubicExtAddGroup): CubicExtAddGroup = -# result.sum(a, b) -# -# func `-`*(a, b: CubicExtAddGroup): CubicExtAddGroup = -# result.sum(a, b) - -# ############################################################ -# -# Quadratic Extension fields -# -# ############################################################ - -type - QuadExtAddGroup* = concept x - ## Quadratic extension fields - Abelian Additive Group concept - not(x is CubicExtAddGroup) - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField - -func `==`*(a, b: QuadExtAddGroup): CTBool[Word] = - ## Constant-time equality check - (a.c0 == b.c0) and (a.c1 == b.c1) - -func setZero*(a: var QuadExtAddGroup) = - ## Set ``a`` to zero in the extension field - ## Coordinates 0 + 0 𝛼 - ## with 𝛼 the solution of f(x) = x² - µ = 0 - a.c0.setZero() - a.c1.setZero() - -func setOne*(a: var QuadExtAddGroup) = - ## Set ``a`` to one in the extension field - ## Coordinates 1 + 0 𝛼 - ## with 𝛼 the solution of f(x) = x² - µ = 0 - a.c0.setOne() - a.c1.setZero() - -func `+=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = - ## Addition in the extension field - a.c0 += b.c0 - a.c1 += b.c1 - -func `-=`*(a: var QuadExtAddGroup, b: QuadExtAddGroup) = - ## Substraction in the extension field - a.c0 -= b.c0 - a.c1 -= b.c1 - -func double*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = - ## Double in the extension field - r.c0.double(a.c0) - r.c1.double(a.c1) - -func double*(a: var QuadExtAddGroup) = - ## Double in the extension field - double(a.c0) - double(a.c1) - -func sum*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = - ## Sum ``a`` and ``b`` into r - r.c0.sum(a.c0, b.c0) - r.c1.sum(a.c1, b.c1) - -func diff*(r: var QuadExtAddGroup, a, b: QuadExtAddGroup) = - ## Difference of ``a`` by `b`` into r - r.c0.diff(a.c0, b.c0) - r.c1.diff(a.c1, b.c1) - -func neg*(r: var QuadExtAddGroup, a: QuadExtAddGroup) = - ## Negate ``a`` into ``r`` - r.c0.neg(a.c0) - r.c1.neg(a.c1) - -# TODO: The type system is lost with out-of-place result concepts -# func `+`*(a, b: CubicExtAddGroup): CubicExtAddGroup = -# result.sum(a, b) -# -# func `-`*(a, b: CubicExtAddGroup): CubicExtAddGroup = -# result.sum(a, b) diff --git a/constantine/tower_field_extensions/fp6_1_plus_i.nim b/constantine/tower_field_extensions/cubic_extensions.nim similarity index 51% rename from constantine/tower_field_extensions/fp6_1_plus_i.nim rename to constantine/tower_field_extensions/cubic_extensions.nim index 1e97c36..4ca4996 100644 --- a/constantine/tower_field_extensions/fp6_1_plus_i.nim +++ b/constantine/tower_field_extensions/cubic_extensions.nim @@ -6,74 +6,28 @@ # * 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. -# ############################################################ -# -# Cubic Extension field over extension field 𝔽p2 -# 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] -# -# ############################################################ - -# This implements a quadratic extension field over -# 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] -# with element A of coordinates (a0, a1, a2) represented -# by a0 + a1 v + a2 v² -# -# The irreducible polynomial chosen is -# v³ - ξ with ξ = 𝑖+1 -# -# -# Consequently, for this file 𝔽p6 to be valid -# 𝑖+1 MUST not be a cube in 𝔽p2 - import ../arithmetic, - ../config/curves, - ./abelian_groups, - ./fp2_complex + ../primitives, + ./tower_common -type - Fp6*[C: static Curve] = object - ## Element of the extension field - ## 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] - ## - ## with coordinates (c0, c1, c2) such as - ## c0 + c1 v + c2 v² and v³ = ξ = 1+𝑖 - ## - ## This requires 1 + 𝑖 to not be a cube in 𝔽p2 - c0*, c1*, c2*: Fp2[C] +# Commutative ring implementation for Cubic Extension Fields +# ------------------------------------------------------------------- - Xi* = object - ## ξ (Xi) the cubic non-residue of 𝔽p2 - -func `*`*(_: typedesc[Xi], a: Fp2): Fp2 {.inline.}= - ## Multiply an element of 𝔽p2 by 𝔽p6 cubic non-residue ξ = 1 + 𝑖 - ## (c0 + c1 𝑖) (1 + 𝑖) => c0 + (c0 + c1)𝑖 + c1 𝑖² - ## => c0 - c1 + (c0 + c1) 𝑖 - result.c0.diff(a.c0, a.c1) - result.c1.sum(a.c0, a.c1) - -template `*`*(a: Fp2, _: typedesc[Xi]): Fp2 = - Xi * a - -func `*=`*(a: var Fp2, _: typedesc[Xi]) {.inline.}= - ## Inplace multiply an element of 𝔽p2 by 𝔽p6 cubic non-residue 1 + 𝑖 - let t = a.c0 - a.c0 -= a.c1 - a.c1 += t - -func square*[C](r: var Fp6[C], a: Fp6[C]) = +func square*(r: var CubicExt, a: CubicExt) = ## Returns r = a² # Algorithm is Chung-Hasan Squaring SQR2 # http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf # # TODO: change to SQR1 or SQR3 (requires div2) # which are faster for the sizes we are interested in. - var v2{.noInit.}, v3{.noInit.}, v4{.noInit.}, v5{.noInit.}: Fp2[C] + mixin prod, square, sum + var v2{.noInit.}, v3{.noInit.}, v4{.noInit.}, v5{.noInit.}: typeof(r.c0) v4.prod(a.c0, a.c1) v4.double() v5.square(a.c2) - r.c1 = Xi * v5 + r.c1 = β * v5 r.c1 += v4 v2.diff(v4, v5) v3.square(a.c0) @@ -82,39 +36,39 @@ func square*[C](r: var Fp6[C], a: Fp6[C]) = v5.prod(a.c1, a.c2) v5.double() v4.square() - r.c0 = Xi * v5 + r.c0 = β * v5 r.c0 += v3 r.c2.sum(v2, v4) r.c2 += v5 r.c2 -= v3 -func prod*[C](r: var Fp6[C], a, b: Fp6[C]) = +func prod*(r: var CubicExt, a, b: CubicExt) = ## Returns r = a * b ## ## r MUST not share a buffer with a # Algorithm is Karatsuba - var v0{.noInit.}, v1{.noInit.}, v2{.noInit.}, t{.noInit.}: Fp2[C] + var v0{.noInit.}, v1{.noInit.}, v2{.noInit.}, t{.noInit.}: typeof(r.c0) v0.prod(a.c0, b.c0) v1.prod(a.c1, b.c1) v2.prod(a.c2, b.c2) - # r.c0 = ((a.c1 + a.c2) * (b.c1 + b.c2) - v1 - v2) * Xi + v0 + # r.c0 = β ((a.c1 + a.c2) * (b.c1 + b.c2) - v1 - v2) + v0 r.c0.sum(a.c1, a.c2) t.sum(b.c1, b.c2) r.c0 *= t r.c0 -= v1 r.c0 -= v2 - r.c0 *= Xi + r.c0 *= β r.c0 += v0 - # r.c1 = (a.c0 + a.c1) * (b.c0 + b.c1) - v0 - v1 + Xi * v2 + # r.c1 = (a.c0 + a.c1) * (b.c0 + b.c1) - v0 - v1 + β v2 r.c1.sum(a.c0, a.c1) t.sum(b.c0, b.c1) r.c1 *= t r.c1 -= v0 r.c1 -= v1 - r.c1 += Xi * v2 + r.c1 += β * v2 # r.c2 = (a.c0 + a.c2) * (b.c0 + b.c2) - v0 - v2 + v1 r.c2.sum(a.c0, a.c2) @@ -124,9 +78,13 @@ func prod*[C](r: var Fp6[C], a, b: Fp6[C]) = r.c2 -= v2 r.c2 += v1 -func inv*[C](r: var Fp6[C], a: Fp6[C]) = +func inv*(r: var CubicExt, a: CubicExt) = ## Compute the multiplicative inverse of ``a`` - ## in 𝔽p6 = 𝔽p2[∛(1 + 𝑖)] + ## + ## The inverse of 0 is 0. + ## Incidentally this avoids extra check + ## to convert Jacobian and Projective coordinates + ## to affine for elliptic curve # # Algorithm 5.23 # @@ -139,20 +97,19 @@ func inv*[C](r: var Fp6[C], a: Fp6[C]) = # instead of 9, because 5 * 2 (𝔽p2) * Bitsize would be: # - ~2540 bits for BN254 # - ~3810 bits for BLS12-381 - var - v1 {.noInit.}, v2 {.noInit.}, v3 {.noInit.}: Fp2[C] + var v1 {.noInit.}, v2 {.noInit.}, v3 {.noInit.}: typeof(r.c0) # A in r0 - # A <- a0² - ξ(a1 a2) + # A <- a0² - β a1 a2 r.c0.square(a.c0) v1.prod(a.c1, a.c2) - v1 *= Xi + v1 *= β r.c0 -= v1 # B in v1 - # B <- ξ a2² - a0 a1 + # B <- β a2² - a0 a1 v1.square(a.c2) - v1 *= Xi + v1 *= β v2.prod(a.c0, a.c1) v1 -= v2 @@ -163,9 +120,9 @@ func inv*[C](r: var Fp6[C], a: Fp6[C]) = v2 -= v3 # F in v3 - # F <- ξ a1 C + a0 A + ξ a2 B - r.c1.prod(v1, Xi * a.c2) - r.c2.prod(v2, Xi * a.c1) + # F <- β a1 C + a0 A + β a2 B + r.c1.prod(v1, β * a.c2) + r.c2.prod(v2, β * a.c1) v3.prod(r.c0, a.c0) v3 += r.c1 v3 += r.c2 @@ -177,10 +134,9 @@ func inv*[C](r: var Fp6[C], a: Fp6[C]) = r.c1.prod(v1, v3) r.c2.prod(v2, v3) -func `*=`*(a: var Fp6, b: Fp6) {.inline.} = - # Need to ensure different buffers for ``a`` and result +func `*=`*(a: var CubicExt, b: CubicExt) {.inline.} = + ## In-place multiplication + # On higher extension field like 𝔽p6, + # if `prod` is called on shared in and out buffer, the result is wrong let t = a a.prod(t, b) - -func `*`*(a, b: Fp6): Fp6 {.inline, noInit.} = - result.prod(a, b) diff --git a/constantine/tower_field_extensions/fp12_quad_fp6.nim b/constantine/tower_field_extensions/fp12_quad_fp6.nim deleted file mode 100644 index 07123a5..0000000 --- a/constantine/tower_field_extensions/fp12_quad_fp6.nim +++ /dev/null @@ -1,153 +0,0 @@ -# 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. - -# ############################################################ -# -# Quadratic Extension field over extension field 𝔽p6 -# 𝔽p12 = 𝔽p6[√γ] -# with γ the cubic root of the non-residue of 𝔽p6 -# -# ############################################################ - -# This implements a quadratic extension field over -# 𝔽p12 = 𝔽p6[γ] -# with γ the cubic root of the non-residue of 𝔽p6 -# with element A of coordinates (a0, a1) represented -# by a0 + a1 γ -# -# The irreducible polynomial chosen is -# w² - γ -# with γ the cubic root of the non-residue of 𝔽p6 -# I.e. if 𝔽p6 irreducible polynomial is -# v³ - ξ with ξ = 1+𝑖 -# γ = v = ∛(1 + 𝑖) -# -# Consequently, for this file 𝔽p12 to be valid -# ∛(1 + 𝑖) MUST not be a square in 𝔽p6 - -import - ../arithmetic, - ../config/curves, - ./abelian_groups, - ./fp6_1_plus_i - -type - Fp12*[C: static Curve] = object - ## Element of the extension field - ## 𝔽p12 = 𝔽p6[γ] - ## - ## I.e. if 𝔽p6 irreducible polynomial is - ## v³ - ξ with ξ = 1+𝑖 - ## γ = v = ∛(1 + 𝑖) - ## - ## with coordinates (c0, c1) such as - ## c0 + c1 w - c0*, c1*: Fp6[C] - - Gamma = object - ## γ (Gamma) the quadratic non-residue of 𝔽p6 - ## γ = v with v the factor in for 𝔽p6 coordinate - ## i.e. a point in 𝔽p6 as coordinates a0 + a1 v + a2 v² - -func `*`(_: typedesc[Gamma], a: Fp6): Fp6 {.noInit, inline.} = - ## Multiply an element of 𝔽p6 by 𝔽p12 quadratic non-residue - ## Conveniently γ = v with v the factor in for 𝔽p6 coordinate - ## and v³ = ξ - ## (c0 + c1 v + c2 v²) v => ξ c2 + c0 v + c1 v² - discard - - result.c0 = a.c2 * Xi - result.c1 = a.c0 - result.c2 = a.c1 - -template `*`(a: Fp6, _: typedesc[Gamma]): Fp6 = - Gamma * a - -func `*=`(a: var Fp6, _: typedesc[Gamma]) {.inline.} = - a = Gamma * a - -func square*(r: var Fp12, a: Fp12) = - ## Return a² in ``r`` - ## ``r`` is initialized/overwritten - # (c0, c1)² => (c0 + c1 w)² - # => c0² + 2 c0 c1 w + c1²w² - # => c0² + γ c1² + 2 c0 c1 w - # => (c0² + γ c1², 2 c0 c1) - # We have 2 squarings and 1 multiplication in 𝔽p6 - # which are significantly more costly: - # - 4 limbs like BN254: multiplication is 20x slower than addition/substraction - # - 6 limbs like BLS12-381: multiplication is 28x slower than addition/substraction - # - # We can save operations with one of the following expressions - # of c0² + γ c1² and noticing that c0c1 is already computed for the "y" coordinate - # - # Alternative 1: - # c0² + γ c1² <=> (c0 - c1)(c0 - γ c1) + γ c0c1 + c0c1 - # - # Alternative 2: - # c0² + γ c1² <=> (c0 + c1)(c0 + γ c1) - γ c0c1 - c0c1 - - # r0 <- (c0 + c1)(c0 + γ c1) - r.c0.sum(a.c0, a.c1) - r.c1.sum(a.c0, Gamma * a.c1) - r.c0 *= r.c1 - - # r1 <- c0 c1 - r.c1.prod(a.c0, a.c1) - - # r0 = (c0 + c1)(c0 + γ c1) - γ c0c1 - c0c1 - r.c0 -= Gamma * r.c1 - r.c0 -= r.c1 - - # r1 = 2 c0c1 - r.c1.double() - -func prod*[C](r: var Fp12[C], a, b: Fp12[C]) = - ## Returns r = a * b - # r0 = a0 b0 + γ a1 b1 - # r1 = (a0 + a1) (b0 + b1) - a0 b0 - a1 b1 (Karatsuba) - var t {.noInit.}: Fp6[C] - - # r1 <- (a0 + a1)(b0 + b1) - r.c0.sum(a.c0, a.c1) - t.sum(b.c0, b.c1) - r.c1.prod(r.c0, t) - - # r0 <- a0 b0 - # r1 <- (a0 + a1)(b0 + b1) - a0 b0 - a1 b1 - r.c0.prod(a.c0, b.c0) - t.prod(a.c1, b.c1) - r.c1 -= r.c0 - r.c1 -= t - - # r0 <- a0 b0 + γ a1 b1 - r.c0 += Gamma * t - -func inv*[C](r: var Fp12[C], a: Fp12[C]) = - ## Compute the multiplicative inverse of ``a`` - # - # Algorithm: (the inverse exist if a != 0 which might cause constant-time issue) - # - # 1 / (a0 + a1 w) <=> (a0 - a1 w) / (a0 + a1 w)(a0 - a1 w) - # <=> (a0 - a1 w) / (a0² - a1² w²) - # In our case 𝔽p12 = 𝔽p6[γ], we have w² = γ - # So the inverse is (a0 - a1 w) / (a0² - γ a1²) - - # [2 Sqr, 1 Add] - var v0 {.noInit.}, v1 {.noInit.}: Fp6[C] - v0.square(a.c0) - v1.square(a.c1) - v0 -= Gamma * v1 # v0 = a0² - γ a1² (the norm / squared magnitude of a) - - # [1 Inv, 2 Sqr, 1 Add] - v1.inv(v0) # v1 = 1 / (a0² - γ a1²) - - # [1 Inv, 2 Mul, 2 Sqr, 1 Add, 1 Neg] - r.c0.prod(a.c0, v1) # r0 = a0 / (a0² - γ a1²) - v0.neg(v1) # v0 = -1 / (a0² - γ a1²) - r.c1.prod(a.c1, v0) # r1 = -a1 / (a0² - γ a1²) diff --git a/constantine/tower_field_extensions/fp2_complex.nim b/constantine/tower_field_extensions/fp2_complex.nim deleted file mode 100644 index 616a678..0000000 --- a/constantine/tower_field_extensions/fp2_complex.nim +++ /dev/null @@ -1,169 +0,0 @@ -# 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. - -# ############################################################ -# -# Quadratic Extension field over base field 𝔽p -# 𝔽p2 = 𝔽p[𝑖] -# -# ############################################################ - -# This implements a quadratic extension field over -# the base field 𝔽p: -# 𝔽p2 = 𝔽p[x] -# with element A of coordinates (a0, a1) represented -# by a0 + a1 x -# -# The irreducible polynomial chosen is -# x² - µ with µ = -1 -# i.e. 𝔽p2 = 𝔽p[𝑖], 𝑖 being the imaginary unit -# -# Consequently, for this file Fp2 to be valid -# -1 MUST not be a square in 𝔽p -# -# µ is also chosen to simplify multiplication and squaring -# => A(a0, a1) * B(b0, b1) -# => (a0 + a1 x) * (b0 + b1 x) -# => a0 b0 + (a0 b1 + a1 b0) x + a1 b1 x² -# We need x² to be as cheap as possible -# -# References -# [1] Constructing Tower Extensions for the implementation of Pairing-Based Cryptography\ -# Naomi Benger and Michael Scott, 2009\ -# https://eprint.iacr.org/2009/556 -# -# [2] Choosing and generating parameters for low level pairing implementation on BN curves\ -# Sylvain Duquesne and Nadia El Mrabet and Safia Haloui and Franck Rondepierre, 2015\ -# https://eprint.iacr.org/2015/1212 - -# TODO: Clarify some assumptions about the prime p ≡ 3 (mod 4) - -import - ../arithmetic, - ../config/curves, - ./abelian_groups - -type - Fp2*[C: static Curve] = object - ## Element of the extension field - ## 𝔽p2 = 𝔽p[𝑖] of a prime p - ## - ## with coordinates (c0, c1) such as - ## c0 + c1 𝑖 - ## - ## This requires 𝑖² = -1 to not - ## be a square (mod p) - c0*, c1*: Fp[C] - -func square*(r: var Fp2, a: Fp2) = - ## Return a² in 𝔽p2 = 𝔽p[𝑖] in ``r`` - ## ``r`` is initialized/overwritten - # (c0, c1)² => (c0 + c1𝑖)² - # => c0² + 2 c0 c1𝑖 + (c1𝑖)² - # => c0²-c1² + 2 c0 c1𝑖 - # => (c0²-c1², 2 c0 c1) - # or - # => ((c0-c1)(c0+c1), 2 c0 c1) - # => ((c0-c1)(c0-c1 + 2 c1), c0 * 2 c1) - # - # Costs (naive implementation) - # - 1 Multiplication 𝔽p - # - 2 Squarings 𝔽p - # - 1 Doubling 𝔽p - # - 1 Substraction 𝔽p - # Stack: 4 * ModulusBitSize (4x 𝔽p element) - # - # Or (with 1 less Mul/Squaring at the cost of 1 addition and extra 2 𝔽p stack space) - # - # - 2 Multiplications 𝔽p - # - 1 Addition 𝔽p - # - 1 Doubling 𝔽p - # - 1 Substraction 𝔽p - # Stack: 6 * ModulusBitSize (4x 𝔽p element + 1 named temporaries + 1 in-place multiplication temporary) - # as in-place multiplications require a (shared) internal temporary - - var c0mc1 {.noInit.}: Fp[Fp2.C] - c0mc1.diff(a.c0, a.c1) # c0mc1 = c0 - c1 [1 Sub] - r.c1.double(a.c1) # result.c1 = 2 c1 [1 Dbl, 1 Sub] - r.c0.sum(c0mc1, r.c1) # result.c0 = c0 - c1 + 2 c1 [1 Add, 1 Dbl, 1 Sub] - r.c0 *= c0mc1 # result.c0 = (c0 + c1)(c0 - c1) = c0² - c1² [1 Mul, 1 Add, 1 Dbl, 1 Sub] - 𝔽p temporary - r.c1 *= a.c0 # result.c1 = 2 c1 c0 [2 Mul, 1 Add, 1 Dbl, 1 Sub] - 𝔽p temporary - -func prod*(r: var Fp2, a, b: Fp2) = - ## Return a * b in 𝔽p2 = 𝔽p[𝑖] in ``r`` - ## ``r`` is initialized/overwritten - # (a0, a1) (b0, b1) => (a0 + a1𝑖) (b0 + b1𝑖) - # => (a0 b0 - a1 b1) + (a0 b1 + a1 b0) 𝑖 - # - # In Fp, multiplication has cost O(n²) with n the number of limbs - # while addition has cost O(3n) (n for addition, n for overflow, n for conditional substraction) - # and substraction has cost O(2n) (n for substraction + underflow, n for conditional addition) - # - # Even for 256-bit primes, we are looking at always a minimum of n=5 limbs (with 2^63 words) - # where addition/substraction are significantly cheaper than multiplication - # - # So we always reframe the imaginary part using Karatsuba approach to save a multiplication - # (a0, a1) (b0, b1) => (a0 b0 - a1 b1) + 𝑖( (a0 + a1)(b0 + b1) - a0 b0 - a1 b1 ) - # - # Costs (naive implementation) - # - 4 Multiplications 𝔽p - # - 1 Addition 𝔽p - # - 1 Substraction 𝔽p - # Stack: 6 * ModulusBitSize (4x 𝔽p element + 2x named temporaries) - # - # Costs (Karatsuba) - # - 3 Multiplications 𝔽p - # - 3 Substraction 𝔽p (2 are fused) - # - 2 Addition 𝔽p - # Stack: 6 * ModulusBitSize (4x 𝔽p element + 2x named temporaries + 1 in-place multiplication temporary) - var a0b0 {.noInit.}, a1b1 {.noInit.}: Fp[Fp2.C] - a0b0.prod(a.c0, b.c0) # [1 Mul] - a1b1.prod(a.c1, b.c1) # [2 Mul] - - r.c0.sum(a.c0, a.c1) # r0 = (a0 + a1) # [2 Mul, 1 Add] - r.c1.sum(b.c0, b.c1) # r1 = (b0 + b1) # [2 Mul, 2 Add] - r.c1 *= r.c0 # r1 = (b0 + b1)(a0 + a1) # [3 Mul, 2 Add] - 𝔽p temporary - - r.c0.diff(a0b0, a1b1) # r0 = a0 b0 - a1 b1 # [3 Mul, 2 Add, 1 Sub] - r.c1 -= a0b0 # r1 = (b0 + b1)(a0 + a1) - a0b0 # [3 Mul, 2 Add, 2 Sub] - r.c1 -= a1b1 # r1 = (b0 + b1)(a0 + a1) - a0b0 - a1b1 # [3 Mul, 2 Add, 3 Sub] - -func inv*(r: var Fp2, a: Fp2) = - ## Compute the multiplicative inverse of ``a`` - ## in 𝔽p2 = 𝔽p[𝑖] - # - # Algorithm: (the inverse exist if a != 0 which might cause constant-time issue) - # - # 1 / (a0 + a1 x) <=> (a0 - a1 x) / (a0 + a1 x)(a0 - a1 x) - # <=> (a0 - a1 x) / (a0² - a1² x²) - # In our case 𝔽p2 = 𝔽p[𝑖], we have x = 𝑖 - # So the inverse is (a0 - a1 𝑖) / (a0² + a1²) - - # [2 Sqr, 1 Add] - var t0 {.noInit.}, t1 {.noInit.}: Fp[Fp2.C] - t0.square(a.c0) - t1.square(a.c1) - t0 += t1 # t0 = a0² + a1² (the norm / squared magnitude of a) - - # [1 Inv, 2 Sqr, 1 Add] - t0.inv(t0) # t0 = 1 / (a0² + a1²) - - # [1 Inv, 2 Mul, 2 Sqr, 1 Add, 1 Neg] - r.c0.prod(a.c0, t0) # r0 = a0 / (a0² + a1²) - t1.neg(t0) # t0 = -1 / (a0² + a1²) - r.c1.prod(a.c1, t1) # r1 = -a1 / (a0² + a1²) - -func square*(a: var Fp2) {.inline.} = - let t = a - a.square(t) - -func `*=`*(a: var Fp2, b: Fp2) {.inline.} = - a.prod(a, b) - -func `*`*(a, b: Fp2): Fp2 {.inline, noInit.} = - result.prod(a, b) diff --git a/constantine/tower_field_extensions/fp2_sqrt_minus2.nim b/constantine/tower_field_extensions/fp2_sqrt_minus2.nim deleted file mode 100644 index 0d44826..0000000 --- a/constantine/tower_field_extensions/fp2_sqrt_minus2.nim +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -# ############################################################ -# -# Quadratic Extension field over base field 𝔽p -# 𝔽p2 = 𝔽p[√-5] -# -# ############################################################ - -# This implements a quadratic extension field over -# the base field 𝔽p: -# 𝔽p2 = 𝔽p[x] -# with element A of coordinates (a0, a1) represented -# by a0 + a1 x -# -# The irreducible polynomial chosen is -# x² - µ with µ = -2 -# i.e. 𝔽p2 = 𝔽p[√-2] -# -# Consequently, for this file Fp2 to be valid -# -2 MUST not be a square in 𝔽p -# -# References -# [1] Software Implementation of Pairings\ -# D. Hankerson, A. Menezes, and M. Scott, 2009\ -# http://cacr.uwaterloo.ca/~ajmeneze/publications/pairings_software.pdf - - -import - ../arithmetic, - ../config/curves, - ./abelian_groups - -type - Fp2*[C: static Curve] = object - ## Element of the extension field - ## 𝔽p2 = 𝔽p[√-2] of a prime p - ## - ## with coordinates (c0, c1) such as - ## c0 + c1 √-2 - ## - ## This requires -2 to not be a square (mod p) - c0*, c1*: Fp[C] - -func square*(r: var Fp2, a: Fp2) = - ## Return a^2 in 𝔽p2 in ``r`` - ## ``r`` is initialized/overwritten - # (c0, c1)² => (c0 + c1√-2)² - # => c0² + 2 c0 c1√-2 + (c1√-2)² - # => c0² - 2c1² + 2 c0 c1 √-2 - # => (c0²-2c1², 2 c0 c1) - # - # Costs (naive implementation) - # - 2 Multiplications 𝔽p - # - 1 Squaring 𝔽p - # - 1 Doubling 𝔽p - # - 1 Substraction 𝔽p - # Stack: 6 * ModulusBitSize (4x 𝔽p element + 2 named temporaries + 1 "in-place" mul temporary) - - var c1d, c0s {.noInit.}: typeof(a.c1) - c1d.double(a.c1) # c1d = 2 c1 [1 Dbl] - c0s.square(a.c0) # c0s = c0² [1 Sqr, 1 Dbl] - - r.c1.prod(c1d, a.c0) # r.c1 = 2 c1 c0 [1 Mul, 1 Sqr, 1 Dbl] - c1d *= a.c1 # c1d = 2 c1² [2 Mul, 1 Sqr, 1 Dbl] - 1 "in-place" temporary - r.c0.diff(c0s, c1d) # r.c0 = c0²-2c1² [2 Mul, 1 Sqr, 1 Dbl, 1 Sub] diff --git a/constantine/tower_field_extensions/fp2_sqrt_minus5.nim b/constantine/tower_field_extensions/fp2_sqrt_minus5.nim deleted file mode 100644 index c8a3b34..0000000 --- a/constantine/tower_field_extensions/fp2_sqrt_minus5.nim +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -# ############################################################ -# -# Quadratic Extension field over base field 𝔽p -# 𝔽p2 = 𝔽p[√-5] -# -# ############################################################ - -# This implements a quadratic extension field over -# the base field 𝔽p: -# 𝔽p2 = 𝔽p[x] -# with element A of coordinates (a0, a1) represented -# by a0 + a1 x -# -# The irreducible polynomial chosen is -# x² - µ with µ = -5 -# i.e. 𝔽p2 = 𝔽p[√-5] -# -# Consequently, for this file Fp2 to be valid -# -5 MUST not be a square in 𝔽p -# -# References -# [1] High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves\ -# Jean-Luc Beuchat and Jorge Enrique González Díaz and Shigeo Mitsunari and Eiji Okamoto and Francisco Rodríguez-Henríquez and Tadanori Teruya, 2010\ -# https://eprint.iacr.org/2010/354 - -import - ../arithmetic, - ../config/curves, - ./abelian_groups - -type - Fp2*[C: static Curve] = object - ## Element of the extension field - ## 𝔽p2 = 𝔽p[√-5] of a prime p - ## - ## with coordinates (c0, c1) such as - ## c0 + c1 √-5 - ## - ## This requires -5 to not be a square (mod p) - c0*, c1*: Fp[C] - -# TODO: need fast multiplication by small constant -# which probably requires lazy carries -# https://github.com/mratsim/constantine/issues/15 diff --git a/constantine/tower_field_extensions/quadratic_extensions.nim b/constantine/tower_field_extensions/quadratic_extensions.nim new file mode 100644 index 0000000..f7a6a12 --- /dev/null +++ b/constantine/tower_field_extensions/quadratic_extensions.nim @@ -0,0 +1,227 @@ +# 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 + ../arithmetic, + ../config/common, + ../primitives, + ./tower_common + +# Commutative ring implementation for complex extension fields +# ------------------------------------------------------------------- + +func square_complex(r: var QuadraticExt, a: QuadraticExt) = + ## Return a² in 𝔽p2 = 𝔽p[𝑖] in ``r`` + ## ``r`` is initialized/overwritten + ## + ## Requires a complex extension field + # (c0, c1)² => (c0 + c1𝑖)² + # => c0² + 2 c0 c1𝑖 + (c1𝑖)² + # => c0²-c1² + 2 c0 c1𝑖 + # => (c0²-c1², 2 c0 c1) + # or + # => ((c0-c1)(c0+c1), 2 c0 c1) + # => ((c0-c1)(c0-c1 + 2 c1), c0 * 2 c1) + # + # Costs (naive implementation) + # - 1 Multiplication 𝔽p + # - 2 Squarings 𝔽p + # - 1 Doubling 𝔽p + # - 1 Substraction 𝔽p + # Stack: 4 * ModulusBitSize (4x 𝔽p element) + # + # Or (with 1 less Mul/Squaring at the cost of 1 addition and extra 2 𝔽p stack space) + # + # - 2 Multiplications 𝔽p + # - 1 Addition 𝔽p + # - 1 Doubling 𝔽p + # - 1 Substraction 𝔽p + # Stack: 6 * ModulusBitSize (4x 𝔽p element + 1 named temporaries + 1 in-place multiplication temporary) + # as in-place multiplications require a (shared) internal temporary + mixin fromComplexExtension + static: doAssert r.fromComplexExtension() + + var c0mc1 {.noInit.}: typeof(r.c0) + c0mc1.diff(a.c0, a.c1) # c0mc1 = c0 - c1 [1 Sub] + r.c1.double(a.c1) # result.c1 = 2 c1 [1 Dbl, 1 Sub] + r.c0.sum(c0mc1, r.c1) # result.c0 = c0 - c1 + 2 c1 [1 Add, 1 Dbl, 1 Sub] + r.c0 *= c0mc1 # result.c0 = (c0 + c1)(c0 - c1) = c0² - c1² [1 Mul, 1 Add, 1 Dbl, 1 Sub] - 𝔽p temporary + r.c1 *= a.c0 # result.c1 = 2 c1 c0 [2 Mul, 1 Add, 1 Dbl, 1 Sub] - 𝔽p temporary + +func prod_complex(r: var QuadraticExt, a, b: QuadraticExt) = + ## Return a * b in 𝔽p2 = 𝔽p[𝑖] in ``r`` + ## ``r`` is initialized/overwritten + ## + ## Requires a complex extension field + # (a0, a1) (b0, b1) => (a0 + a1𝑖) (b0 + b1𝑖) + # => (a0 b0 - a1 b1) + (a0 b1 + a1 b0) 𝑖 + # + # In Fp, multiplication has cost O(n²) with n the number of limbs + # while addition has cost O(3n) (n for addition, n for overflow, n for conditional substraction) + # and substraction has cost O(2n) (n for substraction + underflow, n for conditional addition) + # + # Even for 256-bit primes, we are looking at always a minimum of n=5 limbs (with 2^63 words) + # where addition/substraction are significantly cheaper than multiplication + # + # So we always reframe the imaginary part using Karatsuba approach to save a multiplication + # (a0, a1) (b0, b1) => (a0 b0 - a1 b1) + 𝑖( (a0 + a1)(b0 + b1) - a0 b0 - a1 b1 ) + # + # Costs (naive implementation) + # - 4 Multiplications 𝔽p + # - 1 Addition 𝔽p + # - 1 Substraction 𝔽p + # Stack: 6 * ModulusBitSize (4x 𝔽p element + 2x named temporaries) + # + # Costs (Karatsuba) + # - 3 Multiplications 𝔽p + # - 3 Substraction 𝔽p (2 are fused) + # - 2 Addition 𝔽p + # Stack: 6 * ModulusBitSize (4x 𝔽p element + 2x named temporaries + 1 in-place multiplication temporary) + mixin fromComplexExtension + static: doAssert r.fromComplexExtension() + + var a0b0 {.noInit.}, a1b1 {.noInit.}: typeof(r.c0) + a0b0.prod(a.c0, b.c0) # [1 Mul] + a1b1.prod(a.c1, b.c1) # [2 Mul] + + r.c0.sum(a.c0, a.c1) # r0 = (a0 + a1) # [2 Mul, 1 Add] + r.c1.sum(b.c0, b.c1) # r1 = (b0 + b1) # [2 Mul, 2 Add] + r.c1 *= r.c0 # r1 = (b0 + b1)(a0 + a1) # [3 Mul, 2 Add] - 𝔽p temporary + + r.c0.diff(a0b0, a1b1) # r0 = a0 b0 - a1 b1 # [3 Mul, 2 Add, 1 Sub] + r.c1 -= a0b0 # r1 = (b0 + b1)(a0 + a1) - a0b0 # [3 Mul, 2 Add, 2 Sub] + r.c1 -= a1b1 # r1 = (b0 + b1)(a0 + a1) - a0b0 - a1b1 # [3 Mul, 2 Add, 3 Sub] + +# Commutative ring implementation for generic quadratic extension fields +# ------------------------------------------------------------------- + +func square_generic(r: var QuadraticExt, a: QuadraticExt) = + ## Return a² in ``r`` + ## ``r`` is initialized/overwritten + # Algorithm (with β the non-residue in the base field) + # + # (c0, c1)² => (c0 + c1 w)² + # => c0² + 2 c0 c1 w + c1²w² + # => c0² + β c1² + 2 c0 c1 w + # => (c0² + β c1², 2 c0 c1) + # We have 2 squarings and 1 multiplication in the base field + # which are significantly more costly than additions. + # For example when construction 𝔽p12 from 𝔽p6: + # - 4 limbs like BN254: multiplication is 20x slower than addition/substraction + # - 6 limbs like BLS12-381: multiplication is 28x slower than addition/substraction + # + # We can save operations with one of the following expressions + # of c0² + β c1² and noticing that c0c1 is already computed for the "y" coordinate + # + # Alternative 1: + # c0² + β c1² <=> (c0 - c1)(c0 - β c1) + β c0c1 + c0c1 + # + # Alternative 2: + # c0² + β c1² <=> (c0 + c1)(c0 + β c1) - β c0c1 - c0c1 + mixin prod + + # r0 <- (c0 + c1)(c0 + β c1) + r.c0.sum(a.c0, a.c1) + r.c1.sum(a.c0, β * a.c1) + r.c0 *= r.c1 + + # r1 <- c0 c1 + r.c1.prod(a.c0, a.c1) + + # r0 = (c0 + c1)(c0 + β c1) - β c0c1 - c0c1 + r.c0 -= β * r.c1 + r.c0 -= r.c1 + + # r1 = 2 c0c1 + r.c1.double() + +func prod_generic(r: var QuadraticExt, a, b: QuadraticExt) = + ## Returns r = a * b + # Algorithm (with β the non-residue in the base field) + # + # r0 = a0 b0 + β a1 b1 + # r1 = (a0 + a1) (b0 + b1) - a0 b0 - a1 b1 (Karatsuba) + mixin prod + var t {.noInit.}: typeof(r.c0) + + # r1 <- (a0 + a1)(b0 + b1) + r.c0.sum(a.c0, a.c1) + t.sum(b.c0, b.c1) + r.c1.prod(r.c0, t) + + # r0 <- a0 b0 + # r1 <- (a0 + a1)(b0 + b1) - a0 b0 - a1 b1 + r.c0.prod(a.c0, b.c0) + t.prod(a.c1, b.c1) + r.c1 -= r.c0 + r.c1 -= t + + # r0 <- a0 b0 + β a1 b1 + r.c0 += β * t + +# Exported symbols +# ------------------------------------------------------------------- + +func square*(r: var QuadraticExt, a: QuadraticExt) {.inline.} = + mixin fromComplexExtension + when r.fromComplexExtension(): + r.square_complex(a) + else: + r.square_generic(a) + +func prod*(r: var QuadraticExt, a, b: QuadraticExt) {.inline.} = + mixin fromComplexExtension + when r.fromComplexExtension(): + r.prod_complex(a, b) + else: + r.prod_generic(a, b) + +func inv*(r: var QuadraticExt, a: QuadraticExt) = + ## Compute the multiplicative inverse of ``a`` + ## + ## The inverse of 0 is 0. + ## Incidentally this avoids extra check + ## to convert Jacobian and Projective coordinates + ## to affine for elliptic curve + # + # Algorithm: + # + # 1 / (a0 + a1 w) <=> (a0 - a1 w) / (a0 + a1 w)(a0 - a1 w) + # <=> (a0 - a1 w) / (a0² - a1² w²) + # with w being our coordinate system and β the quadratic non-residue + # we have w² = β + # So the inverse is (a0 - a1 w) / (a0² - β a1²) + mixin fromComplexExtension + + # [2 Sqr, 1 Add] + var v0 {.noInit.}, v1 {.noInit.}: typeof(r.c0) + v0.square(a.c0) + v1.square(a.c1) + when r.fromComplexExtension(): + v0 += v1 + else: + v0 -= β * v1 # v0 = a0² - β a1² (the norm / squared magnitude of a) + + # [1 Inv, 2 Sqr, 1 Add] + v1.inv(v0) # v1 = 1 / (a0² - β a1²) + + # [1 Inv, 2 Mul, 2 Sqr, 1 Add, 1 Neg] + r.c0.prod(a.c0, v1) # r0 = a0 / (a0² - β a1²) + v0.neg(v1) # v0 = -1 / (a0² - β a1²) + r.c1.prod(a.c1, v0) # r1 = -a1 / (a0² - β a1²) + +func `*=`*(a: var QuadraticExt, b: QuadraticExt) {.inline.} = + ## In-place multiplication + # On higher extension field like 𝔽p12, + # if `prod` is called on shared in and out buffer, the result is wrong + let t = a + a.prod(t, b) + +func square*(a: var QuadraticExt){.inline.} = + let t = a + a.square(t) diff --git a/constantine/tower_field_extensions/tower_common.nim b/constantine/tower_field_extensions/tower_common.nim new file mode 100644 index 0000000..7d37880 --- /dev/null +++ b/constantine/tower_field_extensions/tower_common.nim @@ -0,0 +1,116 @@ +# 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 + ../config/common, + ../primitives, + ../arithmetic + +# Note: to avoid burdening the Nim compiler, we rely on generic extension +# to complain if the base field procedures don't exist + +# Common type definition +# ------------------------------------------------------------------- + +type + β* = object + ## Non-Residue β + ## + ## Placeholder for the appropriate quadratic or cubic non-residue + + CubicExt* = concept x + ## Cubic Extension field concept + type BaseField = auto + x.c0 is BaseField + x.c1 is BaseField + x.c2 is BaseField + + QuadraticExt* = concept x + ## Quadratic Extension field concept + not(x is CubicExt) + + type BaseField = auto + x.c0 is BaseField + x.c1 is BaseField + + ExtensionField = QuadraticExt or CubicExt + +# Initialization +# ------------------------------------------------------------------- + +func setZero*(a: var ExtensionField) = + ## Set ``a`` to 0 in the extension field + for field in fields(a): + field.setZero() + +func setOne*(a: var ExtensionField) = + ## Set ``a`` to 1 in the extension field + for fieldName, fA in fieldPairs(a): + when fieldName == "c0": + fA.setOne() + else: + fA.setZero() + +# Comparison +# ------------------------------------------------------------------- + +func `==`*(a, b: ExtensionField): CTBool[Word] = + ## Constant-time equality check + result = CtFalse + for fA, fB in fields(a, b): + result = result or (fA == fB) + +# Abelian group +# ------------------------------------------------------------------- + +func `+=`*(a: var ExtensionField, b: ExtensionField) = + ## Addition in the extension field + for fA, fB in fields(a, b): + fA += fB + +func `-=`*(a: var ExtensionField, b: ExtensionField) = + ## Substraction in the extension field + for fA, fB in fields(a, b): + fA -= fB + +func double*(r: var ExtensionField, a: ExtensionField) = + ## Field out-of-place doubling + for fR, fA in fields(r, a): + fR.double(fA) + +func double*(a: var ExtensionField) = + ## Field in-place doubling + for fA in fields(a): + fA.double() + +func neg*(r: var ExtensionField, a: ExtensionField) = + ## Field out-of-place negation + for fR, fA in fields(r, a): + fR.neg(fA) + +func sum*(r: var QuadraticExt, a, b: QuadraticExt) = + ## Sum ``a`` and ``b`` into ``r`` + r.c0.sum(a.c0, b.c0) + r.c1.sum(a.c1, b.c1) + +func sum*(r: var CubicExt, a, b: CubicExt) = + ## Sum ``a`` and ``b`` into ``r`` + r.c0.sum(a.c0, b.c0) + r.c1.sum(a.c1, b.c1) + r.c2.sum(a.c2, b.c2) + +func diff*(r: var QuadraticExt, a, b: QuadraticExt) = + ## Diff ``a`` and ``b`` into ``r`` + r.c0.sum(a.c0, b.c0) + r.c1.sum(a.c1, b.c1) + +func diff*(r: var CubicExt, a, b: CubicExt) = + ## Diff ``a`` and ``b`` into ``r`` + r.c0.sum(a.c0, b.c0) + r.c1.sum(a.c1, b.c1) + r.c2.sum(a.c2, b.c2) diff --git a/constantine/towers.nim b/constantine/towers.nim index 24712df..b2e1692 100644 --- a/constantine/towers.nim +++ b/constantine/towers.nim @@ -7,15 +7,119 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - tower_field_extensions/[ - abelian_groups, - fp2_complex, - fp6_1_plus_i, - fp12_quad_fp6 + ./arithmetic, + ./config/curves, + ./tower_field_extensions/[ + tower_common, + quadratic_extensions, + cubic_extensions ] -export - abelian_groups, - fp2_complex, - fp6_1_plus_i, - fp12_quad_fp6 +export tower_common, quadratic_extensions, cubic_extensions + +# 𝔽p2 +# ---------------------------------------------------------------- + +type + Fp2*[C: static Curve] = object + c0*, c1*: Fp[C] + +template fromComplexExtension*[F](elem: F): static bool = + ## Returns true if the input is a complex extension + ## i.e. the irreducible polynomial chosen is + ## x² - µ with µ = -1 + ## and so 𝔽p2 = 𝔽p[x] / x² - µ = 𝔽p[𝑖] + when F is Fp2 and F.C.get_QNR_Fp() == -1: + true + else: + false + +func `*=`*(a: var Fp, _: typedesc[β]) {.inline.} = + ## Multiply an element of 𝔽p by the quadratic non-residue + ## chosen to construct 𝔽p2 + static: doAssert Fp.C.get_QNR_Fp() != -1, "𝔽p2 should be specialized for complex extension" + a *= Fp.C.get_QNR_Fp() + +func `*`*(_: typedesc[β], a: Fp): Fp {.inline, noInit.} = + ## Multiply an element of 𝔽p by the quadratic non-residue + ## chosen to construct 𝔽p2 + result = a + result *= β + +# 𝔽p6 +# ---------------------------------------------------------------- + +type + Fp6*[C: static Curve] = object + c0*, c1*, c2*: Fp2[C] + + ξ = β + # We call the non-residue ξ on 𝔽p6 to avoid confusion between non-residue + # of different tower level + +func `*`*(_: typedesc[β], a: Fp2): Fp2 {.inline, noInit.} = + ## Multiply an element of 𝔽p2 by the cubic non-residue + ## chosen to construct 𝔽p6 + # Yet another const tuple unpacking bug + const u = Fp2.C.get_CNR_Fp2()[0] # Cubic non-residue to construct 𝔽p6 + const v = Fp2.C.get_CNR_Fp2()[1] + const Beta = Fp2.C.get_QNR_Fp() # Quadratic non-residue to construct 𝔽p2 + # ξ = u + v x + # and x² = β + # + # (c0 + c1 x) (u + v x) => u c0 + (u c0 + u c1)x + v c1 x² + # => u c0 + β v c1 + (v c0 + u c1) x + + # TODO: check code generated when ξ = 1 + 𝑖 + # The mul by constant are inline but + # since we don't have __restrict tag + # and we use arrays (which decay into pointer) + # the compiler might not elide the temporary + when a.fromComplexExtension(): + result.c0.diff(u * a.c0, v * a.c1) + else: + result.c0.sum(u * a.c0, (Beta * v) * a.c1) + result.c1.sum(v * a.c0, u * a.c1 ) + +func `*=`*(a: var Fp2, _: typedesc[ξ]) {.inline.} = + ## Multiply an element of 𝔽p by the quadratic non-residue + ## chosen to construct 𝔽p2 + # Yet another const tuple unpacking bug + const u = Fp2.C.get_CNR_Fp2()[0] # Cubic non-residue to construct 𝔽p6 + const v = Fp2.C.get_CNR_Fp2()[1] + const Beta = Fp2.C.get_QNR_Fp() # Quadratic non-residue to construct 𝔽p2 + # ξ = u + v x + # and x² = β + # + # (c0 + c1 x) (u + v x) => u c0 + (u c0 + u c1)x + v c1 x² + # => u c0 + β v c1 + (v c0 + u c1) x + when a.fromComplexExtension() and u == 1 and v == 1: + let t = a.c0 + a.c0 -= a.c1 + a.c1 += t + else: # TODO: optim for inline + a = ξ * a + +# 𝔽p12 +# ---------------------------------------------------------------- + +type + Fp12*[C: static Curve] = object + c0*, c1*: Fp6[C] + + γ = β + # We call the non-residue γ (Gamma) on 𝔽p6 to avoid confusion between non-residue + # of different tower level + +func `*`*(_: typedesc[γ], a: Fp6): Fp6 {.noInit, inline.} = + ## Multiply an element of 𝔽p6 by the cubic non-residue + ## chosen to construct 𝔽p12 + ## For all curves γ = v with v the factor for 𝔽p6 coordinate + ## and v³ = ξ + ## (c0 + c1 v + c2 v²) v => ξ c2 + c0 v + c1 v² + result.c0 = ξ * a.c2 + result.c1 = a.c0 + result.c2 = a.c1 + +func `*=`*(a: var Fp6, _: typedesc[γ]) {.inline.} = + a = γ * a diff --git a/tests/test_fp12.nim b/tests/test_fp12.nim index a85d7a5..94b4536 100644 --- a/tests/test_fp12.nim +++ b/tests/test_fp12.nim @@ -10,7 +10,7 @@ import # Standard library unittest, times, random, # Internals - ../constantine/tower_field_extensions/[abelian_groups, fp12_quad_fp6], + ../constantine/towers, ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities @@ -50,14 +50,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring 2 returns 4": template test(C: static Curve) = @@ -87,14 +87,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring 3 returns 9": template test(C: static Curve) = @@ -126,14 +126,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring -3 returns 9": template test(C: static Curve) = @@ -165,14 +165,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication by 0 and 1": template test(C: static Curve, body: untyped) = @@ -194,18 +194,18 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami): - r.prod(x, Zero) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(Zero, x) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(x, One) - check: bool(r == x) - test(BN254_Nogami): - r.prod(One, x) - check: bool(r == x) + # test(BN254_Nogami): + # r.prod(x, Zero) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(Zero, x) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(x, One) + # check: bool(r == x) + # test(BN254_Nogami): + # r.prod(One, x) + # check: bool(r == x) test(BN254_Snarks): r.prod(x, Zero) check: bool(r == Zero) @@ -230,18 +230,18 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": test(BLS12_381): r.prod(One, x) check: bool(r == x) - test(BN462): - r.prod(x, Zero) - check: bool(r == Zero) - test(BN462): - r.prod(Zero, x) - check: bool(r == Zero) - test(BN462): - r.prod(x, One) - check: bool(r == x) - test(BN462): - r.prod(One, x) - check: bool(r == x) + # test(BN462): + # r.prod(x, Zero) + # check: bool(r == Zero) + # test(BN462): + # r.prod(Zero, x) + # check: bool(r == Zero) + # test(BN462): + # r.prod(x, One) + # check: bool(r == x) + # test(BN462): + # r.prod(One, x) + # check: bool(r == x) test "Multiplication and Squaring are consistent": template test(C: static Curve) = @@ -258,14 +258,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring the opposite gives the same result": template test(C: static Curve) = @@ -285,14 +285,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication and Addition/Substraction are consistent": template test(C: static Curve) = @@ -329,14 +329,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] addition is associative and commutative": proc abelianGroup(curve: static Curve) = @@ -380,14 +380,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": bool(r0 == r3) bool(r0 == r4) - abelianGroup(BN254_Nogami) + # abelianGroup(BN254_Nogami) abelianGroup(BN254_Snarks) abelianGroup(BLS12_377) abelianGroup(BLS12_381) - abelianGroup(BN446) - abelianGroup(FKM12_447) - abelianGroup(BLS12_461) - abelianGroup(BN462) + # abelianGroup(BN446) + # abelianGroup(FKM12_447) + # abelianGroup(BLS12_461) + # abelianGroup(BN462) test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = @@ -431,14 +431,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": bool(r0 == r3) bool(r0 == r4) - commutativeRing(BN254_Nogami) + # commutativeRing(BN254_Nogami) commutativeRing(BN254_Snarks) commutativeRing(BLS12_377) commutativeRing(BLS12_381) - commutativeRing(BN446) - commutativeRing(FKM12_447) - commutativeRing(BLS12_461) - commutativeRing(BN462) + # commutativeRing(BN446) + # commutativeRing(FKM12_447) + # commutativeRing(BLS12_461) + # commutativeRing(BN462) test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse": proc mulInvOne(curve: static Curve) = @@ -462,11 +462,11 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": r.prod(aInv, a) check: bool(r == one) - mulInvOne(BN254_Nogami) + # mulInvOne(BN254_Nogami) mulInvOne(BN254_Snarks) mulInvOne(BLS12_377) mulInvOne(BLS12_381) - mulInvOne(BN446) - mulInvOne(FKM12_447) - mulInvOne(BLS12_461) - mulInvOne(BN462) + # mulInvOne(BN446) + # mulInvOne(FKM12_447) + # mulInvOne(BLS12_461) + # mulInvOne(BN462) diff --git a/tests/test_fp2.nim b/tests/test_fp2.nim index f7b7333..6cbb811 100644 --- a/tests/test_fp2.nim +++ b/tests/test_fp2.nim @@ -10,7 +10,7 @@ import # Standard library unittest, times, random, # Internals - ../constantine/tower_field_extensions/[abelian_groups, fp2_complex], + ../constantine/towers, ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities @@ -51,7 +51,7 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": bool(r == oneBig) bool(oneFp2.c1.mres.isZero()) - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_381) @@ -74,14 +74,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication by 0 and 1": template test(C: static Curve, body: untyped) = @@ -103,18 +103,18 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": testInstance() - test(BN254_Nogami): - r.prod(x, Zero) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(Zero, x) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(x, One) - check: bool(r == x) - test(BN254_Nogami): - r.prod(One, x) - check: bool(r == x) + # test(BN254_Nogami): + # r.prod(x, Zero) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(Zero, x) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(x, One) + # check: bool(r == x) + # test(BN254_Nogami): + # r.prod(One, x) + # check: bool(r == x) test(BN254_Snarks): r.prod(x, Zero) check: bool(r == Zero) @@ -155,14 +155,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring the opposite gives the same result": template test(C: static Curve) = @@ -182,14 +182,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication and Addition/Substraction are consistent": template test(C: static Curve) = @@ -226,14 +226,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "𝔽p2 = 𝔽p[𝑖] addition is associative and commutative": proc abelianGroup(curve: static Curve) = @@ -277,14 +277,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": bool(r0 == r3) bool(r0 == r4) - abelianGroup(BN254_Nogami) + # abelianGroup(BN254_Nogami) abelianGroup(BN254_Snarks) abelianGroup(BLS12_377) abelianGroup(BLS12_381) - abelianGroup(BN446) - abelianGroup(FKM12_447) - abelianGroup(BLS12_461) - abelianGroup(BN462) + # abelianGroup(BN446) + # abelianGroup(FKM12_447) + # abelianGroup(BLS12_461) + # abelianGroup(BN462) test "𝔽p2 = 𝔽p[𝑖] multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = @@ -328,14 +328,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": bool(r0 == r3) bool(r0 == r4) - commutativeRing(BN254_Nogami) + # commutativeRing(BN254_Nogami) commutativeRing(BN254_Snarks) commutativeRing(BLS12_377) commutativeRing(BLS12_381) - commutativeRing(BN446) - commutativeRing(FKM12_447) - commutativeRing(BLS12_461) - commutativeRing(BN462) + # commutativeRing(BN446) + # commutativeRing(FKM12_447) + # commutativeRing(BLS12_461) + # commutativeRing(BN462) test "𝔽p2 = 𝔽p[𝑖] extension field multiplicative inverse": proc mulInvOne(curve: static Curve) = @@ -352,11 +352,11 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": r.prod(aInv, a) check: bool(r == one) - mulInvOne(BN254_Nogami) + # mulInvOne(BN254_Nogami) mulInvOne(BN254_Snarks) mulInvOne(BLS12_377) mulInvOne(BLS12_381) - mulInvOne(BN446) - mulInvOne(FKM12_447) - mulInvOne(BLS12_461) - mulInvOne(BN462) + # mulInvOne(BN446) + # mulInvOne(FKM12_447) + # mulInvOne(BLS12_461) + # mulInvOne(BN462) diff --git a/tests/test_fp6.nim b/tests/test_fp6.nim index 2fce201..fa6f17f 100644 --- a/tests/test_fp6.nim +++ b/tests/test_fp6.nim @@ -10,7 +10,7 @@ import # Standard library unittest, times, random, # Internals - ../constantine/tower_field_extensions/[abelian_groups, fp6_1_plus_i], + ../constantine/towers, ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities @@ -50,14 +50,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring 2 returns 4": template test(C: static Curve) = @@ -87,14 +87,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring 3 returns 9": template test(C: static Curve) = @@ -126,14 +126,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring -3 returns 9": template test(C: static Curve) = @@ -165,14 +165,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication by 0 and 1": template test(C: static Curve, body: untyped) = @@ -194,18 +194,18 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami): - r.prod(x, Zero) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(Zero, x) - check: bool(r == Zero) - test(BN254_Nogami): - r.prod(x, One) - check: bool(r == x) - test(BN254_Nogami): - r.prod(One, x) - check: bool(r == x) + # test(BN254_Nogami): + # r.prod(x, Zero) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(Zero, x) + # check: bool(r == Zero) + # test(BN254_Nogami): + # r.prod(x, One) + # check: bool(r == x) + # test(BN254_Nogami): + # r.prod(One, x) + # check: bool(r == x) test(BN254_Snarks): r.prod(x, Zero) check: bool(r == Zero) @@ -230,18 +230,18 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": test(BLS12_381): r.prod(One, x) check: bool(r == x) - test(BN462): - r.prod(x, Zero) - check: bool(r == Zero) - test(BN462): - r.prod(Zero, x) - check: bool(r == Zero) - test(BN462): - r.prod(x, One) - check: bool(r == x) - test(BN462): - r.prod(One, x) - check: bool(r == x) + # test(BN462): + # r.prod(x, Zero) + # check: bool(r == Zero) + # test(BN462): + # r.prod(Zero, x) + # check: bool(r == Zero) + # test(BN462): + # r.prod(x, One) + # check: bool(r == x) + # test(BN462): + # r.prod(One, x) + # check: bool(r == x) test "Multiplication and Squaring are consistent": template test(C: static Curve) = @@ -258,14 +258,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Squaring the opposite gives the same result": template test(C: static Curve) = @@ -285,14 +285,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "Multiplication and Addition/Substraction are consistent": template test(C: static Curve) = @@ -329,14 +329,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": testInstance() - test(BN254_Nogami) + # test(BN254_Nogami) test(BN254_Snarks) test(BLS12_377) test(BLS12_381) - test(BN446) - test(FKM12_447) - test(BLS12_461) - test(BN462) + # test(BN446) + # test(FKM12_447) + # test(BLS12_461) + # test(BN462) test "𝔽p6 = 𝔽p2[∛(1+𝑖)] addition is associative and commutative": proc abelianGroup(curve: static Curve) = @@ -380,14 +380,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": bool(r0 == r3) bool(r0 == r4) - abelianGroup(BN254_Nogami) + # abelianGroup(BN254_Nogami) abelianGroup(BN254_Snarks) abelianGroup(BLS12_377) abelianGroup(BLS12_381) - abelianGroup(BN446) - abelianGroup(FKM12_447) - abelianGroup(BLS12_461) - abelianGroup(BN462) + # abelianGroup(BN446) + # abelianGroup(FKM12_447) + # abelianGroup(BLS12_461) + # abelianGroup(BN462) test "𝔽p6 = 𝔽p2[∛(1+𝑖)] multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = @@ -431,14 +431,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": bool(r0 == r3) bool(r0 == r4) - commutativeRing(BN254_Nogami) + # commutativeRing(BN254_Nogami) commutativeRing(BN254_Snarks) commutativeRing(BLS12_377) commutativeRing(BLS12_381) - commutativeRing(BN446) - commutativeRing(FKM12_447) - commutativeRing(BLS12_461) - commutativeRing(BN462) + # commutativeRing(BN446) + # commutativeRing(FKM12_447) + # commutativeRing(BLS12_461) + # commutativeRing(BN462) test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse": proc mulInvOne(curve: static Curve) = @@ -462,11 +462,11 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": r.prod(aInv, a) check: bool(r == one) - mulInvOne(BN254_Nogami) + # mulInvOne(BN254_Nogami) mulInvOne(BN254_Snarks) mulInvOne(BLS12_377) mulInvOne(BLS12_381) - mulInvOne(BN446) - mulInvOne(FKM12_447) - mulInvOne(BLS12_461) - mulInvOne(BN462) + # mulInvOne(BN446) + # mulInvOne(FKM12_447) + # mulInvOne(BLS12_461) + # mulInvOne(BN462)