Refactor: Higher-Kinded Tower of Extension Fields (#25)

* Mention that the inverse of 0 is 0 (TODO tests)

* Introduce "Higher-Kinded tower extensions"

* rename isCOmplexExtension -> fromComplexExtension

* update benchmarks with the new tower scheme

* Try to recover some speed on mul/squaring for an optimal tower (but this was not it)
This commit is contained in:
Mamy Ratsimbazafy 2020-04-14 02:05:42 +02:00 committed by GitHub
parent 2f839cb1bf
commit c04721a04e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 736 additions and 949 deletions

View File

@ -14,11 +14,9 @@
import import
# Internals # Internals
../constantine/config/[common, curves], ../constantine/config/curves,
../constantine/arithmetic, ../constantine/arithmetic,
../constantine/io/[io_bigints, io_fields], ../constantine/towers,
../constantine/primitives,
../constantine/tower_field_extensions/[abelian_groups, fp2_complex, fp6_1_plus_i],
# Helpers # Helpers
../helpers/[timers, prng, static_for], ../helpers/[timers, prng, static_for],
# Standard library # Standard library

View File

@ -55,5 +55,6 @@ proc main() =
main() main()
echo "Notes:" echo "Notes:"
echo " GCC is significantly slower than Clang on multiprecision arithmetic." echo " - GCC is significantly slower than Clang on multiprecision arithmetic."
echo " The simplest operations might be optimized away by the compiler." 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)"

View File

@ -9,7 +9,7 @@
import import
# Internals # Internals
../constantine/config/curves, ../constantine/config/curves,
../constantine/tower_field_extensions/[abelian_groups, fp12_quad_fp6], ../constantine/towers,
# Helpers # Helpers
../helpers/static_for, ../helpers/static_for,
./bench_fields_template, ./bench_fields_template,
@ -27,14 +27,14 @@ const Iters = 10_000
const InvIters = 1000 const InvIters = 1000
const AvailableCurves = [ const AvailableCurves = [
# Pairing-Friendly curves # Pairing-Friendly curves
BN254_Nogami, # BN254_Nogami,
BN254_Snarks, BN254_Snarks,
BLS12_377, BLS12_377,
BLS12_381, BLS12_381
BN446, # BN446,
FKM12_447, # FKM12_447,
BLS12_461, # BLS12_461,
BN462 # BN462
] ]
proc main() = proc main() =
@ -52,4 +52,6 @@ proc main() =
main() main()
echo "Notes:" 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."

View File

@ -9,7 +9,7 @@
import import
# Internals # Internals
../constantine/config/curves, ../constantine/config/curves,
../constantine/tower_field_extensions/[abelian_groups, fp2_complex], ../constantine/towers,
# Helpers # Helpers
../helpers/static_for, ../helpers/static_for,
./bench_fields_template, ./bench_fields_template,
@ -27,14 +27,14 @@ const Iters = 1_000_000
const InvIters = 1000 const InvIters = 1000
const AvailableCurves = [ const AvailableCurves = [
# Pairing-Friendly curves # Pairing-Friendly curves
BN254_Nogami, # BN254_Nogami,
BN254_Snarks, BN254_Snarks,
BLS12_377, BLS12_377,
BLS12_381, BLS12_381
BN446, # BN446,
FKM12_447, # FKM12_447,
BLS12_461, # BLS12_461,
BN462 # BN462
] ]
proc main() = proc main() =
@ -52,4 +52,6 @@ proc main() =
main() main()
echo "Notes:" 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."

View File

@ -9,7 +9,7 @@
import import
# Internals # Internals
../constantine/config/curves, ../constantine/config/curves,
../constantine/tower_field_extensions/[abelian_groups, fp6_1_plus_i], ../constantine/towers,
# Helpers # Helpers
../helpers/static_for, ../helpers/static_for,
./bench_fields_template, ./bench_fields_template,
@ -27,14 +27,14 @@ const Iters = 1_000_000
const InvIters = 1000 const InvIters = 1000
const AvailableCurves = [ const AvailableCurves = [
# Pairing-Friendly curves # Pairing-Friendly curves
BN254_Nogami, # BN254_Nogami,
BN254_Snarks, BN254_Snarks,
BLS12_377, BLS12_377,
BLS12_381, BLS12_381
BN446, # BN446,
FKM12_447, # FKM12_447,
BLS12_461, # BLS12_461,
BN462 # BN462
] ]
proc main() = proc main() =
@ -52,4 +52,6 @@ proc main() =
main() main()
echo "Notes:" 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."

View File

@ -416,3 +416,8 @@ func `*=`*(a: var Fp, b: static int) {.inline.} =
a += t4 a += t4
else: else:
{.error: "Multiplication by this small int not implemented".} {.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

View File

@ -152,6 +152,11 @@ func invmod_addchain_bn[C](r: var Fp[C], a: Fp[C]) =
func inv*(r: var Fp, a: Fp) = func inv*(r: var Fp, a: Fp) =
## Inversion modulo p ## 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 # For now we don't activate the addition chains
# neither for Secp256k1 nor BN curves # neither for Secp256k1 nor BN curves
# Performance is slower than GCD # Performance is slower than GCD

View File

@ -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 ## This takes (M+1)/2 (mp1div2) as a precomputed parameter as a slight optimization
## in stack size and speed. ## in stack size and speed.
##
## The inverse of 0 is 0.
# Ideally we need registers for a, b, u, v # Ideally we need registers for a, b, u, v
# but: # but:

View File

@ -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)

View File

@ -6,74 +6,28 @@
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). # * 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. # 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 import
../arithmetic, ../arithmetic,
../config/curves, ../primitives,
./abelian_groups, ./tower_common
./fp2_complex
type # Commutative ring implementation for Cubic Extension Fields
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]
Xi* = object func square*(r: var CubicExt, a: CubicExt) =
## ξ (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]) =
## Returns r = a² ## Returns r = a²
# Algorithm is Chung-Hasan Squaring SQR2 # Algorithm is Chung-Hasan Squaring SQR2
# http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf # http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf
# #
# TODO: change to SQR1 or SQR3 (requires div2) # TODO: change to SQR1 or SQR3 (requires div2)
# which are faster for the sizes we are interested in. # 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.prod(a.c0, a.c1)
v4.double() v4.double()
v5.square(a.c2) v5.square(a.c2)
r.c1 = Xi * v5 r.c1 = β * v5
r.c1 += v4 r.c1 += v4
v2.diff(v4, v5) v2.diff(v4, v5)
v3.square(a.c0) 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.prod(a.c1, a.c2)
v5.double() v5.double()
v4.square() v4.square()
r.c0 = Xi * v5 r.c0 = β * v5
r.c0 += v3 r.c0 += v3
r.c2.sum(v2, v4) r.c2.sum(v2, v4)
r.c2 += v5 r.c2 += v5
r.c2 -= v3 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 ## Returns r = a * b
## ##
## r MUST not share a buffer with a ## r MUST not share a buffer with a
# Algorithm is Karatsuba # 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) v0.prod(a.c0, b.c0)
v1.prod(a.c1, b.c1) v1.prod(a.c1, b.c1)
v2.prod(a.c2, b.c2) 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) r.c0.sum(a.c1, a.c2)
t.sum(b.c1, b.c2) t.sum(b.c1, b.c2)
r.c0 *= t r.c0 *= t
r.c0 -= v1 r.c0 -= v1
r.c0 -= v2 r.c0 -= v2
r.c0 *= Xi r.c0 *= β
r.c0 += v0 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) r.c1.sum(a.c0, a.c1)
t.sum(b.c0, b.c1) t.sum(b.c0, b.c1)
r.c1 *= t r.c1 *= t
r.c1 -= v0 r.c1 -= v0
r.c1 -= v1 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 = (a.c0 + a.c2) * (b.c0 + b.c2) - v0 - v2 + v1
r.c2.sum(a.c0, a.c2) 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 -= v2
r.c2 += v1 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`` ## 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 # 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: # instead of 9, because 5 * 2 (𝔽p2) * Bitsize would be:
# - ~2540 bits for BN254 # - ~2540 bits for BN254
# - ~3810 bits for BLS12-381 # - ~3810 bits for BLS12-381
var var v1 {.noInit.}, v2 {.noInit.}, v3 {.noInit.}: typeof(r.c0)
v1 {.noInit.}, v2 {.noInit.}, v3 {.noInit.}: Fp2[C]
# A in r0 # A in r0
# A <- a0² - ξ(a1 a2) # A <- a0² - β a1 a2
r.c0.square(a.c0) r.c0.square(a.c0)
v1.prod(a.c1, a.c2) v1.prod(a.c1, a.c2)
v1 *= Xi v1 *= β
r.c0 -= v1 r.c0 -= v1
# B in v1 # B in v1
# B <- ξ a2² - a0 a1 # B <- β a2² - a0 a1
v1.square(a.c2) v1.square(a.c2)
v1 *= Xi v1 *= β
v2.prod(a.c0, a.c1) v2.prod(a.c0, a.c1)
v1 -= v2 v1 -= v2
@ -163,9 +120,9 @@ func inv*[C](r: var Fp6[C], a: Fp6[C]) =
v2 -= v3 v2 -= v3
# F in v3 # F in v3
# F <- ξ a1 C + a0 A + ξ a2 B # F <- β a1 C + a0 A + β a2 B
r.c1.prod(v1, Xi * a.c2) r.c1.prod(v1, β * a.c2)
r.c2.prod(v2, Xi * a.c1) r.c2.prod(v2, β * a.c1)
v3.prod(r.c0, a.c0) v3.prod(r.c0, a.c0)
v3 += r.c1 v3 += r.c1
v3 += r.c2 v3 += r.c2
@ -177,10 +134,9 @@ func inv*[C](r: var Fp6[C], a: Fp6[C]) =
r.c1.prod(v1, v3) r.c1.prod(v1, v3)
r.c2.prod(v2, v3) r.c2.prod(v2, v3)
func `*=`*(a: var Fp6, b: Fp6) {.inline.} = func `*=`*(a: var CubicExt, b: CubicExt) {.inline.} =
# Need to ensure different buffers for ``a`` and result ## 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 let t = a
a.prod(t, b) a.prod(t, b)
func `*`*(a, b: Fp6): Fp6 {.inline, noInit.} =
result.prod(a, b)

View File

@ -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²)

View File

@ -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)

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -7,15 +7,119 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
tower_field_extensions/[ ./arithmetic,
abelian_groups, ./config/curves,
fp2_complex, ./tower_field_extensions/[
fp6_1_plus_i, tower_common,
fp12_quad_fp6 quadratic_extensions,
cubic_extensions
] ]
export export tower_common, quadratic_extensions, cubic_extensions
abelian_groups,
fp2_complex, # 𝔽p2
fp6_1_plus_i, # ----------------------------------------------------------------
fp12_quad_fp6
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

View File

@ -10,7 +10,7 @@ import
# Standard library # Standard library
unittest, times, random, unittest, times, random,
# Internals # Internals
../constantine/tower_field_extensions/[abelian_groups, fp12_quad_fp6], ../constantine/towers,
../constantine/config/[common, curves], ../constantine/config/[common, curves],
../constantine/arithmetic, ../constantine/arithmetic,
# Test utilities # Test utilities
@ -50,14 +50,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring 2 returns 4": test "Squaring 2 returns 4":
template test(C: static Curve) = template test(C: static Curve) =
@ -87,14 +87,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring 3 returns 9": test "Squaring 3 returns 9":
template test(C: static Curve) = template test(C: static Curve) =
@ -126,14 +126,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring -3 returns 9": test "Squaring -3 returns 9":
template test(C: static Curve) = template test(C: static Curve) =
@ -165,14 +165,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication by 0 and 1": test "Multiplication by 0 and 1":
template test(C: static Curve, body: untyped) = template test(C: static Curve, body: untyped) =
@ -194,18 +194,18 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, Zero) # r.prod(x, Zero)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(Zero, x) # r.prod(Zero, x)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, One) # r.prod(x, One)
check: bool(r == x) # check: bool(r == x)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(One, x) # r.prod(One, x)
check: bool(r == x) # check: bool(r == x)
test(BN254_Snarks): test(BN254_Snarks):
r.prod(x, Zero) r.prod(x, Zero)
check: bool(r == Zero) check: bool(r == Zero)
@ -230,18 +230,18 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
test(BLS12_381): test(BLS12_381):
r.prod(One, x) r.prod(One, x)
check: bool(r == x) check: bool(r == x)
test(BN462): # test(BN462):
r.prod(x, Zero) # r.prod(x, Zero)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN462): # test(BN462):
r.prod(Zero, x) # r.prod(Zero, x)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN462): # test(BN462):
r.prod(x, One) # r.prod(x, One)
check: bool(r == x) # check: bool(r == x)
test(BN462): # test(BN462):
r.prod(One, x) # r.prod(One, x)
check: bool(r == x) # check: bool(r == x)
test "Multiplication and Squaring are consistent": test "Multiplication and Squaring are consistent":
template test(C: static Curve) = template test(C: static Curve) =
@ -258,14 +258,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring the opposite gives the same result": test "Squaring the opposite gives the same result":
template test(C: static Curve) = template test(C: static Curve) =
@ -285,14 +285,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication and Addition/Substraction are consistent": test "Multiplication and Addition/Substraction are consistent":
template test(C: static Curve) = template test(C: static Curve) =
@ -329,14 +329,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] addition is associative and commutative": test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] addition is associative and commutative":
proc abelianGroup(curve: static Curve) = proc abelianGroup(curve: static Curve) =
@ -380,14 +380,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
abelianGroup(BN254_Nogami) # abelianGroup(BN254_Nogami)
abelianGroup(BN254_Snarks) abelianGroup(BN254_Snarks)
abelianGroup(BLS12_377) abelianGroup(BLS12_377)
abelianGroup(BLS12_381) abelianGroup(BLS12_381)
abelianGroup(BN446) # abelianGroup(BN446)
abelianGroup(FKM12_447) # abelianGroup(FKM12_447)
abelianGroup(BLS12_461) # abelianGroup(BLS12_461)
abelianGroup(BN462) # abelianGroup(BN462)
test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] multiplication is associative and commutative": test "𝔽p12 = 𝔽p6[√∛(1+𝑖)] multiplication is associative and commutative":
proc commutativeRing(curve: static Curve) = proc commutativeRing(curve: static Curve) =
@ -431,14 +431,14 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
commutativeRing(BN254_Nogami) # commutativeRing(BN254_Nogami)
commutativeRing(BN254_Snarks) commutativeRing(BN254_Snarks)
commutativeRing(BLS12_377) commutativeRing(BLS12_377)
commutativeRing(BLS12_381) commutativeRing(BLS12_381)
commutativeRing(BN446) # commutativeRing(BN446)
commutativeRing(FKM12_447) # commutativeRing(FKM12_447)
commutativeRing(BLS12_461) # commutativeRing(BLS12_461)
commutativeRing(BN462) # commutativeRing(BN462)
test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse": test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse":
proc mulInvOne(curve: static Curve) = proc mulInvOne(curve: static Curve) =
@ -462,11 +462,11 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]":
r.prod(aInv, a) r.prod(aInv, a)
check: bool(r == one) check: bool(r == one)
mulInvOne(BN254_Nogami) # mulInvOne(BN254_Nogami)
mulInvOne(BN254_Snarks) mulInvOne(BN254_Snarks)
mulInvOne(BLS12_377) mulInvOne(BLS12_377)
mulInvOne(BLS12_381) mulInvOne(BLS12_381)
mulInvOne(BN446) # mulInvOne(BN446)
mulInvOne(FKM12_447) # mulInvOne(FKM12_447)
mulInvOne(BLS12_461) # mulInvOne(BLS12_461)
mulInvOne(BN462) # mulInvOne(BN462)

View File

@ -10,7 +10,7 @@ import
# Standard library # Standard library
unittest, times, random, unittest, times, random,
# Internals # Internals
../constantine/tower_field_extensions/[abelian_groups, fp2_complex], ../constantine/towers,
../constantine/config/[common, curves], ../constantine/config/[common, curves],
../constantine/arithmetic, ../constantine/arithmetic,
# Test utilities # Test utilities
@ -51,7 +51,7 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
bool(r == oneBig) bool(r == oneBig)
bool(oneFp2.c1.mres.isZero()) bool(oneFp2.c1.mres.isZero())
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_381) test(BLS12_381)
@ -74,14 +74,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication by 0 and 1": test "Multiplication by 0 and 1":
template test(C: static Curve, body: untyped) = template test(C: static Curve, body: untyped) =
@ -103,18 +103,18 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
testInstance() testInstance()
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, Zero) # r.prod(x, Zero)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(Zero, x) # r.prod(Zero, x)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, One) # r.prod(x, One)
check: bool(r == x) # check: bool(r == x)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(One, x) # r.prod(One, x)
check: bool(r == x) # check: bool(r == x)
test(BN254_Snarks): test(BN254_Snarks):
r.prod(x, Zero) r.prod(x, Zero)
check: bool(r == Zero) check: bool(r == Zero)
@ -155,14 +155,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring the opposite gives the same result": test "Squaring the opposite gives the same result":
template test(C: static Curve) = template test(C: static Curve) =
@ -182,14 +182,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication and Addition/Substraction are consistent": test "Multiplication and Addition/Substraction are consistent":
template test(C: static Curve) = template test(C: static Curve) =
@ -226,14 +226,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "𝔽p2 = 𝔽p[𝑖] addition is associative and commutative": test "𝔽p2 = 𝔽p[𝑖] addition is associative and commutative":
proc abelianGroup(curve: static Curve) = proc abelianGroup(curve: static Curve) =
@ -277,14 +277,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
abelianGroup(BN254_Nogami) # abelianGroup(BN254_Nogami)
abelianGroup(BN254_Snarks) abelianGroup(BN254_Snarks)
abelianGroup(BLS12_377) abelianGroup(BLS12_377)
abelianGroup(BLS12_381) abelianGroup(BLS12_381)
abelianGroup(BN446) # abelianGroup(BN446)
abelianGroup(FKM12_447) # abelianGroup(FKM12_447)
abelianGroup(BLS12_461) # abelianGroup(BLS12_461)
abelianGroup(BN462) # abelianGroup(BN462)
test "𝔽p2 = 𝔽p[𝑖] multiplication is associative and commutative": test "𝔽p2 = 𝔽p[𝑖] multiplication is associative and commutative":
proc commutativeRing(curve: static Curve) = proc commutativeRing(curve: static Curve) =
@ -328,14 +328,14 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
commutativeRing(BN254_Nogami) # commutativeRing(BN254_Nogami)
commutativeRing(BN254_Snarks) commutativeRing(BN254_Snarks)
commutativeRing(BLS12_377) commutativeRing(BLS12_377)
commutativeRing(BLS12_381) commutativeRing(BLS12_381)
commutativeRing(BN446) # commutativeRing(BN446)
commutativeRing(FKM12_447) # commutativeRing(FKM12_447)
commutativeRing(BLS12_461) # commutativeRing(BLS12_461)
commutativeRing(BN462) # commutativeRing(BN462)
test "𝔽p2 = 𝔽p[𝑖] extension field multiplicative inverse": test "𝔽p2 = 𝔽p[𝑖] extension field multiplicative inverse":
proc mulInvOne(curve: static Curve) = proc mulInvOne(curve: static Curve) =
@ -352,11 +352,11 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)":
r.prod(aInv, a) r.prod(aInv, a)
check: bool(r == one) check: bool(r == one)
mulInvOne(BN254_Nogami) # mulInvOne(BN254_Nogami)
mulInvOne(BN254_Snarks) mulInvOne(BN254_Snarks)
mulInvOne(BLS12_377) mulInvOne(BLS12_377)
mulInvOne(BLS12_381) mulInvOne(BLS12_381)
mulInvOne(BN446) # mulInvOne(BN446)
mulInvOne(FKM12_447) # mulInvOne(FKM12_447)
mulInvOne(BLS12_461) # mulInvOne(BLS12_461)
mulInvOne(BN462) # mulInvOne(BN462)

View File

@ -10,7 +10,7 @@ import
# Standard library # Standard library
unittest, times, random, unittest, times, random,
# Internals # Internals
../constantine/tower_field_extensions/[abelian_groups, fp6_1_plus_i], ../constantine/towers,
../constantine/config/[common, curves], ../constantine/config/[common, curves],
../constantine/arithmetic, ../constantine/arithmetic,
# Test utilities # Test utilities
@ -50,14 +50,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring 2 returns 4": test "Squaring 2 returns 4":
template test(C: static Curve) = template test(C: static Curve) =
@ -87,14 +87,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring 3 returns 9": test "Squaring 3 returns 9":
template test(C: static Curve) = template test(C: static Curve) =
@ -126,14 +126,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring -3 returns 9": test "Squaring -3 returns 9":
template test(C: static Curve) = template test(C: static Curve) =
@ -165,14 +165,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication by 0 and 1": test "Multiplication by 0 and 1":
template test(C: static Curve, body: untyped) = template test(C: static Curve, body: untyped) =
@ -194,18 +194,18 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, Zero) # r.prod(x, Zero)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(Zero, x) # r.prod(Zero, x)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(x, One) # r.prod(x, One)
check: bool(r == x) # check: bool(r == x)
test(BN254_Nogami): # test(BN254_Nogami):
r.prod(One, x) # r.prod(One, x)
check: bool(r == x) # check: bool(r == x)
test(BN254_Snarks): test(BN254_Snarks):
r.prod(x, Zero) r.prod(x, Zero)
check: bool(r == Zero) check: bool(r == Zero)
@ -230,18 +230,18 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
test(BLS12_381): test(BLS12_381):
r.prod(One, x) r.prod(One, x)
check: bool(r == x) check: bool(r == x)
test(BN462): # test(BN462):
r.prod(x, Zero) # r.prod(x, Zero)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN462): # test(BN462):
r.prod(Zero, x) # r.prod(Zero, x)
check: bool(r == Zero) # check: bool(r == Zero)
test(BN462): # test(BN462):
r.prod(x, One) # r.prod(x, One)
check: bool(r == x) # check: bool(r == x)
test(BN462): # test(BN462):
r.prod(One, x) # r.prod(One, x)
check: bool(r == x) # check: bool(r == x)
test "Multiplication and Squaring are consistent": test "Multiplication and Squaring are consistent":
template test(C: static Curve) = template test(C: static Curve) =
@ -258,14 +258,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Squaring the opposite gives the same result": test "Squaring the opposite gives the same result":
template test(C: static Curve) = template test(C: static Curve) =
@ -285,14 +285,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "Multiplication and Addition/Substraction are consistent": test "Multiplication and Addition/Substraction are consistent":
template test(C: static Curve) = template test(C: static Curve) =
@ -329,14 +329,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
testInstance() testInstance()
test(BN254_Nogami) # test(BN254_Nogami)
test(BN254_Snarks) test(BN254_Snarks)
test(BLS12_377) test(BLS12_377)
test(BLS12_381) test(BLS12_381)
test(BN446) # test(BN446)
test(FKM12_447) # test(FKM12_447)
test(BLS12_461) # test(BLS12_461)
test(BN462) # test(BN462)
test "𝔽p6 = 𝔽p2[∛(1+𝑖)] addition is associative and commutative": test "𝔽p6 = 𝔽p2[∛(1+𝑖)] addition is associative and commutative":
proc abelianGroup(curve: static Curve) = proc abelianGroup(curve: static Curve) =
@ -380,14 +380,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
abelianGroup(BN254_Nogami) # abelianGroup(BN254_Nogami)
abelianGroup(BN254_Snarks) abelianGroup(BN254_Snarks)
abelianGroup(BLS12_377) abelianGroup(BLS12_377)
abelianGroup(BLS12_381) abelianGroup(BLS12_381)
abelianGroup(BN446) # abelianGroup(BN446)
abelianGroup(FKM12_447) # abelianGroup(FKM12_447)
abelianGroup(BLS12_461) # abelianGroup(BLS12_461)
abelianGroup(BN462) # abelianGroup(BN462)
test "𝔽p6 = 𝔽p2[∛(1+𝑖)] multiplication is associative and commutative": test "𝔽p6 = 𝔽p2[∛(1+𝑖)] multiplication is associative and commutative":
proc commutativeRing(curve: static Curve) = proc commutativeRing(curve: static Curve) =
@ -431,14 +431,14 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
bool(r0 == r3) bool(r0 == r3)
bool(r0 == r4) bool(r0 == r4)
commutativeRing(BN254_Nogami) # commutativeRing(BN254_Nogami)
commutativeRing(BN254_Snarks) commutativeRing(BN254_Snarks)
commutativeRing(BLS12_377) commutativeRing(BLS12_377)
commutativeRing(BLS12_381) commutativeRing(BLS12_381)
commutativeRing(BN446) # commutativeRing(BN446)
commutativeRing(FKM12_447) # commutativeRing(FKM12_447)
commutativeRing(BLS12_461) # commutativeRing(BLS12_461)
commutativeRing(BN462) # commutativeRing(BN462)
test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse": test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse":
proc mulInvOne(curve: static Curve) = proc mulInvOne(curve: static Curve) =
@ -462,11 +462,11 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))":
r.prod(aInv, a) r.prod(aInv, a)
check: bool(r == one) check: bool(r == one)
mulInvOne(BN254_Nogami) # mulInvOne(BN254_Nogami)
mulInvOne(BN254_Snarks) mulInvOne(BN254_Snarks)
mulInvOne(BLS12_377) mulInvOne(BLS12_377)
mulInvOne(BLS12_381) mulInvOne(BLS12_381)
mulInvOne(BN446) # mulInvOne(BN446)
mulInvOne(FKM12_447) # mulInvOne(FKM12_447)
mulInvOne(BLS12_461) # mulInvOne(BLS12_461)
mulInvOne(BN462) # mulInvOne(BN462)