Squashed 'src/snark/' content from commit 9ada3f8
git-subtree-dir: src/snark git-subtree-split: 9ada3f84ab484c57b2247c2f41091fd6a0916573
This commit is contained in:
70
src/algebra/fields/bigint.hpp
Normal file
70
src/algebra/fields/bigint.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Declaration of bigint wrapper class around GMP's MPZ long integers.
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef BIGINT_HPP_
|
||||
#define BIGINT_HPP_
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <gmp.h>
|
||||
#include "common/serialization.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n> class bigint;
|
||||
template<mp_size_t n> std::ostream& operator<<(std::ostream &, const bigint<n>&);
|
||||
template<mp_size_t n> std::istream& operator>>(std::istream &, bigint<n>&);
|
||||
|
||||
/**
|
||||
* Wrapper class around GMP's MPZ long integers. It supports arithmetic operations,
|
||||
* serialization and randomization. Serialization is fragile, see common/serialization.hpp.
|
||||
*/
|
||||
|
||||
template<mp_size_t n>
|
||||
class bigint {
|
||||
public:
|
||||
static const mp_size_t N = n;
|
||||
|
||||
mp_limb_t data[n] = {0};
|
||||
|
||||
bigint() = default;
|
||||
bigint(const unsigned long x); /// Initalize from a small integer
|
||||
bigint(const char* s); /// Initialize from a string containing an integer in decimal notation
|
||||
bigint(const mpz_t r); /// Initialize from MPZ element
|
||||
|
||||
void print() const;
|
||||
void print_hex() const;
|
||||
bool operator==(const bigint<n>& other) const;
|
||||
bool operator!=(const bigint<n>& other) const;
|
||||
void clear();
|
||||
bool is_zero() const;
|
||||
size_t max_bits() const { return n * GMP_NUMB_BITS; }
|
||||
size_t num_bits() const;
|
||||
|
||||
unsigned long as_ulong() const; /* return the last limb of the integer */
|
||||
void to_mpz(mpz_t r) const;
|
||||
bool test_bit(const std::size_t bitno) const;
|
||||
|
||||
template<mp_size_t m> inline void operator+=(const bigint<m>& other);
|
||||
template<mp_size_t m> inline bigint<n+m> operator*(const bigint<m>& other) const;
|
||||
template<mp_size_t d> static inline void div_qr(bigint<n-d+1>& quotient, bigint<d>& remainder,
|
||||
const bigint<n>& dividend, const bigint<d>& divisor);
|
||||
template<mp_size_t m> inline bigint<m> shorten(const bigint<m>& q, const char *msg) const;
|
||||
|
||||
inline void limit(const bigint<n>& q, const char *msg) const;
|
||||
bool operator>(const bigint<n>& other) const;
|
||||
|
||||
bigint& randomize();
|
||||
|
||||
friend std::ostream& operator<< <n>(std::ostream &out, const bigint<n> &b);
|
||||
friend std::istream& operator>> <n>(std::istream &in, bigint<n> &b);
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/bigint.tcc"
|
||||
#endif
|
||||
278
src/algebra/fields/bigint.tcc
Normal file
278
src/algebra/fields/bigint.tcc
Normal file
@@ -0,0 +1,278 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of bigint wrapper class around GMP's MPZ long integers.
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef BIGINT_TCC_
|
||||
#define BIGINT_TCC_
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include "sodium.h"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n>
|
||||
bigint<n>::bigint(const unsigned long x) /// Initalize from a small integer
|
||||
{
|
||||
static_assert(ULONG_MAX <= GMP_NUMB_MAX, "unsigned long does not fit in a GMP limb");
|
||||
this->data[0] = x;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bigint<n>::bigint(const char* s) /// Initialize from a string containing an integer in decimal notation
|
||||
{
|
||||
size_t l = strlen(s);
|
||||
unsigned char* s_copy = new unsigned char[l];
|
||||
|
||||
for (size_t i = 0; i < l; ++i)
|
||||
{
|
||||
assert(s[i] >= '0' && s[i] <= '9');
|
||||
s_copy[i] = s[i] - '0';
|
||||
}
|
||||
|
||||
mp_size_t limbs_written = mpn_set_str(this->data, s_copy, l, 10);
|
||||
assert(limbs_written <= n);
|
||||
|
||||
delete[] s_copy;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bigint<n>::bigint(const mpz_t r) /// Initialize from MPZ element
|
||||
{
|
||||
mpz_t k;
|
||||
mpz_init_set(k, r);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
data[i] = mpz_get_ui(k);
|
||||
mpz_fdiv_q_2exp(k, k, GMP_NUMB_BITS);
|
||||
}
|
||||
|
||||
assert(mpz_sgn(k) == 0);
|
||||
mpz_clear(k);
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
void bigint<n>::print() const
|
||||
{
|
||||
gmp_printf("%Nd\n", this->data, n);
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
void bigint<n>::print_hex() const
|
||||
{
|
||||
gmp_printf("%Nx\n", this->data, n);
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bool bigint<n>::operator==(const bigint<n>& other) const
|
||||
{
|
||||
return (mpn_cmp(this->data, other.data, n) == 0);
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bool bigint<n>::operator!=(const bigint<n>& other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
void bigint<n>::clear()
|
||||
{
|
||||
mpn_zero(this->data, n);
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bool bigint<n>::is_zero() const
|
||||
{
|
||||
for (mp_size_t i = 0; i < n; ++i)
|
||||
{
|
||||
if (this->data[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
size_t bigint<n>::num_bits() const
|
||||
{
|
||||
/*
|
||||
for (long i = max_bits(); i >= 0; --i)
|
||||
{
|
||||
if (this->test_bit(i))
|
||||
{
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
*/
|
||||
for (long i = n-1; i >= 0; --i)
|
||||
{
|
||||
mp_limb_t x = this->data[i];
|
||||
if (x == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((i+1) * GMP_NUMB_BITS) - __builtin_clzl(x);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
unsigned long bigint<n>::as_ulong() const
|
||||
{
|
||||
return this->data[0];
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
void bigint<n>::to_mpz(mpz_t r) const
|
||||
{
|
||||
mpz_set_ui(r, 0);
|
||||
|
||||
for (int i = n-1; i >= 0; --i)
|
||||
{
|
||||
mpz_mul_2exp(r, r, GMP_NUMB_BITS);
|
||||
mpz_add_ui(r, r, this->data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bool bigint<n>::test_bit(const std::size_t bitno) const
|
||||
{
|
||||
if (bitno >= n * GMP_NUMB_BITS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::size_t part = bitno/GMP_NUMB_BITS;
|
||||
const std::size_t bit = bitno - (GMP_NUMB_BITS*part);
|
||||
const mp_limb_t one = 1;
|
||||
return (this->data[part] & (one<<bit));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<mp_size_t n> template<mp_size_t m>
|
||||
inline void bigint<n>::operator+=(const bigint<m>& other)
|
||||
{
|
||||
static_assert(n >= m, "first arg must not be smaller than second arg for bigint in-place add");
|
||||
mpn_add(data, data, n, other.data, m);
|
||||
}
|
||||
|
||||
template<mp_size_t n> template<mp_size_t m>
|
||||
inline bigint<n+m> bigint<n>::operator*(const bigint<m>& other) const
|
||||
{
|
||||
static_assert(n >= m, "first arg must not be smaller than second arg for bigint mul");
|
||||
bigint<n+m> res;
|
||||
mpn_mul(res.data, data, n, other.data, m);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<mp_size_t n> template<mp_size_t d>
|
||||
inline void bigint<n>::div_qr(bigint<n-d+1>& quotient, bigint<d>& remainder,
|
||||
const bigint<n>& dividend, const bigint<d>& divisor)
|
||||
{
|
||||
static_assert(n >= d, "dividend must not be smaller than divisor for bigint::div_qr");
|
||||
assert(divisor.data[d-1] != 0);
|
||||
mpn_tdiv_qr(quotient.data, remainder.data, 0, dividend.data, n, divisor.data, d);
|
||||
}
|
||||
|
||||
// Return a copy shortened to m limbs provided it is less than limit, throwing std::domain_error if not in range.
|
||||
template<mp_size_t n> template<mp_size_t m>
|
||||
inline bigint<m> bigint<n>::shorten(const bigint<m>& q, const char *msg) const
|
||||
{
|
||||
static_assert(m <= n, "number of limbs must not increase for bigint::shorten");
|
||||
for (mp_size_t i = m; i < n; i++) { // high-order limbs
|
||||
if (data[i] != 0) {
|
||||
throw std::domain_error(msg);
|
||||
}
|
||||
}
|
||||
bigint<m> res;
|
||||
mpn_copyi(res.data, data, n);
|
||||
res.limit(q, msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
inline void bigint<n>::limit(const bigint<n>& q, const char *msg) const
|
||||
{
|
||||
if (!(q > *this)) {
|
||||
throw std::domain_error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
inline bool bigint<n>::operator>(const bigint<n>& other) const
|
||||
{
|
||||
return mpn_cmp(this->data, other.data, n) > 0;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
bigint<n>& bigint<n>::randomize()
|
||||
{
|
||||
assert(GMP_NUMB_BITS == sizeof(mp_limb_t) * 8);
|
||||
|
||||
randombytes_buf(this->data, sizeof(mp_limb_t) * n);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
|
||||
template<mp_size_t n>
|
||||
std::ostream& operator<<(std::ostream &out, const bigint<n> &b)
|
||||
{
|
||||
#ifdef BINARY_OUTPUT
|
||||
out.write((char*)b.data, sizeof(b.data[0]) * n);
|
||||
#else
|
||||
mpz_t t;
|
||||
mpz_init(t);
|
||||
b.to_mpz(t);
|
||||
|
||||
out << t;
|
||||
|
||||
mpz_clear(t);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n>
|
||||
std::istream& operator>>(std::istream &in, bigint<n> &b)
|
||||
{
|
||||
#ifdef BINARY_OUTPUT
|
||||
in.read((char*)b.data, sizeof(b.data[0]) * n);
|
||||
#else
|
||||
std::string s;
|
||||
in >> s;
|
||||
|
||||
size_t l = s.size();
|
||||
unsigned char* s_copy = new unsigned char[l];
|
||||
|
||||
for (size_t i = 0; i < l; ++i)
|
||||
{
|
||||
assert(s[i] >= '0' && s[i] <= '9');
|
||||
s_copy[i] = s[i] - '0';
|
||||
}
|
||||
|
||||
mp_size_t limbs_written = mpn_set_str(b.data, s_copy, l, 10);
|
||||
assert(limbs_written <= n);
|
||||
|
||||
delete[] s_copy;
|
||||
#endif
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // BIGINT_TCC_
|
||||
51
src/algebra/fields/field_utils.hpp
Normal file
51
src/algebra/fields/field_utils.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FIELD_UTILS_HPP_
|
||||
#define FIELD_UTILS_HPP_
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/utils.hpp"
|
||||
#include "algebra/fields/bigint.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
// returns root of unity of order n (for n a power of 2), if one exists
|
||||
template<typename FieldT>
|
||||
FieldT get_root_of_unity(const size_t n);
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_int_vector_into_field_element_vector(const std::vector<size_t> &v, const size_t w);
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits);
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v);
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> convert_bit_vector_to_field_element_vector(const bit_vector &v);
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_vector_to_bit_vector(const std::vector<FieldT> &v);
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_to_bit_vector(const FieldT &el);
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount);
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT convert_bit_vector_to_field_element(const bit_vector &v);
|
||||
|
||||
template<typename FieldT>
|
||||
void batch_invert(std::vector<FieldT> &vec);
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/field_utils.tcc"
|
||||
|
||||
#endif // FIELD_UTILS_HPP_
|
||||
183
src/algebra/fields/field_utils.tcc
Normal file
183
src/algebra/fields/field_utils.tcc
Normal file
@@ -0,0 +1,183 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of misc. math and serialization utility functions
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FIELD_UTILS_TCC_
|
||||
#define FIELD_UTILS_TCC_
|
||||
|
||||
#include "common/utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT coset_shift()
|
||||
{
|
||||
return FieldT::multiplicative_generator.squared();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT get_root_of_unity(const size_t n)
|
||||
{
|
||||
const size_t logn = log2(n);
|
||||
assert(n == (1u << logn));
|
||||
assert(logn <= FieldT::s);
|
||||
|
||||
FieldT omega = FieldT::root_of_unity;
|
||||
for (size_t i = FieldT::s; i > logn; --i)
|
||||
{
|
||||
omega *= omega;
|
||||
}
|
||||
|
||||
return omega;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_int_vector_into_field_element_vector(const std::vector<size_t> &v, const size_t w)
|
||||
{
|
||||
const size_t chunk_bits = FieldT::capacity();
|
||||
const size_t repacked_size = div_ceil(v.size() * w, chunk_bits);
|
||||
std::vector<FieldT> result(repacked_size);
|
||||
|
||||
for (size_t i = 0; i < repacked_size; ++i)
|
||||
{
|
||||
bigint<FieldT::num_limbs> b;
|
||||
for (size_t j = 0; j < chunk_bits; ++j)
|
||||
{
|
||||
const size_t word_index = (i * chunk_bits + j) / w;
|
||||
const size_t pos_in_word = (i * chunk_bits + j) % w;
|
||||
const size_t word_or_0 = (word_index < v.size() ? v[word_index] : 0);
|
||||
const size_t bit = (word_or_0 >> pos_in_word) & 1;
|
||||
|
||||
b.data[j / GMP_NUMB_BITS] |= bit << (j % GMP_NUMB_BITS);
|
||||
}
|
||||
result[i] = FieldT(b);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits)
|
||||
{
|
||||
assert(chunk_bits <= FieldT::capacity());
|
||||
|
||||
const size_t repacked_size = div_ceil(v.size(), chunk_bits);
|
||||
std::vector<FieldT> result(repacked_size);
|
||||
|
||||
for (size_t i = 0; i < repacked_size; ++i)
|
||||
{
|
||||
bigint<FieldT::num_limbs> b;
|
||||
for (size_t j = 0; j < chunk_bits; ++j)
|
||||
{
|
||||
b.data[j / GMP_NUMB_BITS] |= ((i * chunk_bits + j) < v.size() && v[i * chunk_bits + j] ? 1ll : 0ll) << (j % GMP_NUMB_BITS);
|
||||
}
|
||||
result[i] = FieldT(b);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v)
|
||||
{
|
||||
return pack_bit_vector_into_field_element_vector<FieldT>(v, FieldT::capacity());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> convert_bit_vector_to_field_element_vector(const bit_vector &v)
|
||||
{
|
||||
std::vector<FieldT> result;
|
||||
result.reserve(v.size());
|
||||
|
||||
for (const bool b : v)
|
||||
{
|
||||
result.emplace_back(b ? FieldT::one() : FieldT::zero());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_vector_to_bit_vector(const std::vector<FieldT> &v)
|
||||
{
|
||||
bit_vector result;
|
||||
|
||||
for (const FieldT &el : v)
|
||||
{
|
||||
const bit_vector el_bits = convert_field_element_to_bit_vector<FieldT>(el);
|
||||
result.insert(result.end(), el_bits.begin(), el_bits.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_to_bit_vector(const FieldT &el)
|
||||
{
|
||||
bit_vector result;
|
||||
|
||||
bigint<FieldT::num_limbs> b = el.as_bigint();
|
||||
for (size_t i = 0; i < FieldT::size_in_bits(); ++i)
|
||||
{
|
||||
result.push_back(b.test_bit(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount)
|
||||
{
|
||||
bit_vector result = convert_field_element_to_bit_vector(el);
|
||||
result.resize(bitcount);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT convert_bit_vector_to_field_element(const bit_vector &v)
|
||||
{
|
||||
assert(v.size() <= FieldT::size_in_bits());
|
||||
|
||||
FieldT res = FieldT::zero();
|
||||
FieldT c = FieldT::one();
|
||||
for (bool b : v)
|
||||
{
|
||||
res += b ? c : FieldT::zero();
|
||||
c += c;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void batch_invert(std::vector<FieldT> &vec)
|
||||
{
|
||||
std::vector<FieldT> prod;
|
||||
prod.reserve(vec.size());
|
||||
|
||||
FieldT acc = FieldT::one();
|
||||
|
||||
for (auto el : vec)
|
||||
{
|
||||
assert(!el.is_zero());
|
||||
prod.emplace_back(acc);
|
||||
acc = acc * el;
|
||||
}
|
||||
|
||||
FieldT acc_inverse = acc.inverse();
|
||||
|
||||
for (long i = vec.size()-1; i >= 0; --i)
|
||||
{
|
||||
const FieldT old_el = vec[i];
|
||||
vec[i] = acc_inverse * prod[i];
|
||||
acc_inverse = acc_inverse * old_el;
|
||||
}
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FIELD_UTILS_TCC_
|
||||
182
src/algebra/fields/fp.hpp
Normal file
182
src/algebra/fields/fp.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Declaration of arithmetic in the finite field F[p], for prime p of fixed length.
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP_HPP_
|
||||
#define FP_HPP_
|
||||
|
||||
#include "algebra/fields/bigint.hpp"
|
||||
#include "algebra/exponentiation/exponentiation.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp_model;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &, const Fp_model<n, modulus>&);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &, Fp_model<n, modulus> &);
|
||||
|
||||
/**
|
||||
* Arithmetic in the finite field F[p], for prime p of fixed length.
|
||||
*
|
||||
* This class implements Fp-arithmetic, for a large prime p, using a fixed number
|
||||
* of words. It is optimized for tight memory consumption, so the modulus p is
|
||||
* passed as a template parameter, to avoid per-element overheads.
|
||||
*
|
||||
* The implementation is mostly a wrapper around GMP's MPN (constant-size integers).
|
||||
* But for the integer sizes of interest for libsnark (3 to 5 limbs of 64 bits each),
|
||||
* we implement performance-critical routines, like addition and multiplication,
|
||||
* using hand-optimzied assembly code.
|
||||
*/
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp_model {
|
||||
public:
|
||||
bigint<n> mont_repr;
|
||||
public:
|
||||
static const mp_size_t num_limbs = n;
|
||||
static const constexpr bigint<n>& mod = modulus;
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
static long long add_cnt;
|
||||
static long long sub_cnt;
|
||||
static long long mul_cnt;
|
||||
static long long sqr_cnt;
|
||||
static long long inv_cnt;
|
||||
#endif
|
||||
static size_t num_bits;
|
||||
static bigint<n> euler; // (modulus-1)/2
|
||||
static size_t s; // modulus = 2^s * t + 1
|
||||
static bigint<n> t; // with t odd
|
||||
static bigint<n> t_minus_1_over_2; // (t-1)/2
|
||||
static Fp_model<n, modulus> nqr; // a quadratic nonresidue
|
||||
static Fp_model<n, modulus> nqr_to_t; // nqr^t
|
||||
static Fp_model<n, modulus> multiplicative_generator; // generator of Fp^*
|
||||
static Fp_model<n, modulus> root_of_unity; // generator^((modulus-1)/2^s)
|
||||
static mp_limb_t inv; // modulus^(-1) mod W, where W = 2^(word size)
|
||||
static bigint<n> Rsquared; // R^2, where R = W^k, where k = ??
|
||||
static bigint<n> Rcubed; // R^3
|
||||
|
||||
static bool modulus_is_valid() { return modulus.data[n-1] != 0; } // mpn inverse assumes that highest limb is non-zero
|
||||
|
||||
Fp_model() {};
|
||||
Fp_model(const bigint<n> &b);
|
||||
Fp_model(const long x, const bool is_unsigned=false);
|
||||
|
||||
void set_ulong(const unsigned long x);
|
||||
|
||||
void mul_reduce(const bigint<n> &other);
|
||||
|
||||
void clear();
|
||||
|
||||
/* Return the standard (not Montgomery) representation of the
|
||||
Field element's requivalence class. I.e. Fp(2).as_bigint()
|
||||
would return bigint(2) */
|
||||
bigint<n> as_bigint() const;
|
||||
/* Return the last limb of the standard representation of the
|
||||
field element. E.g. on 64-bit architectures Fp(123).as_ulong()
|
||||
and Fp(2^64+123).as_ulong() would both return 123. */
|
||||
unsigned long as_ulong() const;
|
||||
|
||||
bool operator==(const Fp_model& other) const;
|
||||
bool operator!=(const Fp_model& other) const;
|
||||
bool is_zero() const;
|
||||
|
||||
void print() const;
|
||||
|
||||
Fp_model& operator+=(const Fp_model& other);
|
||||
Fp_model& operator-=(const Fp_model& other);
|
||||
Fp_model& operator*=(const Fp_model& other);
|
||||
Fp_model& operator^=(const unsigned long pow);
|
||||
|
||||
template<mp_size_t m>
|
||||
Fp_model& operator^=(const bigint<m> &pow);
|
||||
|
||||
Fp_model operator+(const Fp_model& other) const;
|
||||
Fp_model operator-(const Fp_model& other) const;
|
||||
Fp_model operator*(const Fp_model& other) const;
|
||||
Fp_model operator-() const;
|
||||
Fp_model squared() const;
|
||||
Fp_model& invert();
|
||||
Fp_model inverse() const;
|
||||
Fp_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate)
|
||||
|
||||
Fp_model operator^(const unsigned long pow) const;
|
||||
template<mp_size_t m>
|
||||
Fp_model operator^(const bigint<m> &pow) const;
|
||||
|
||||
static size_t size_in_bits() { return num_bits; }
|
||||
static size_t capacity() { return num_bits - 1; }
|
||||
static bigint<n> field_char() { return modulus; }
|
||||
|
||||
static Fp_model<n, modulus> zero();
|
||||
static Fp_model<n, modulus> one();
|
||||
static Fp_model<n, modulus> random_element();
|
||||
|
||||
friend std::ostream& operator<< <n,modulus>(std::ostream &out, const Fp_model<n, modulus> &p);
|
||||
friend std::istream& operator>> <n,modulus>(std::istream &in, Fp_model<n, modulus> &p);
|
||||
};
|
||||
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
long long Fp_model<n, modulus>::add_cnt = 0;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
long long Fp_model<n, modulus>::sub_cnt = 0;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
long long Fp_model<n, modulus>::mul_cnt = 0;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
long long Fp_model<n, modulus>::sqr_cnt = 0;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
long long Fp_model<n, modulus>::inv_cnt = 0;
|
||||
#endif
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
size_t Fp_model<n, modulus>::num_bits;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n, modulus>::euler;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
size_t Fp_model<n, modulus>::s;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n, modulus>::t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n, modulus>::t_minus_1_over_2;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp_model<n, modulus>::nqr;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp_model<n, modulus>::nqr_to_t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp_model<n, modulus>::multiplicative_generator;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp_model<n, modulus>::root_of_unity;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
mp_limb_t Fp_model<n, modulus>::inv;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n, modulus>::Rsquared;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n, modulus>::Rcubed;
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/fp.tcc"
|
||||
|
||||
#endif // FP_HPP_
|
||||
790
src/algebra/fields/fp.tcc
Normal file
790
src/algebra/fields/fp.tcc
Normal file
@@ -0,0 +1,790 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[p], for prime p of fixed length.
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP_TCC_
|
||||
#define FP_TCC_
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
#include "algebra/fields/fp_aux.tcc"
|
||||
#include "algebra/fields/field_utils.hpp"
|
||||
#include "common/assert_except.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
void Fp_model<n,modulus>::mul_reduce(const bigint<n> &other)
|
||||
{
|
||||
/* stupid pre-processor tricks; beware */
|
||||
#if defined(__x86_64__) && defined(USE_ASM)
|
||||
if (n == 3)
|
||||
{ // Use asm-optimized Comba multiplication and reduction
|
||||
mp_limb_t res[2*n];
|
||||
mp_limb_t c0, c1, c2;
|
||||
COMBA_3_BY_3_MUL(c0, c1, c2, res, this->mont_repr.data, other.data);
|
||||
|
||||
mp_limb_t k;
|
||||
mp_limb_t tmp1, tmp2, tmp3;
|
||||
REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data);
|
||||
|
||||
/* subtract t > mod */
|
||||
__asm__
|
||||
("/* check for overflow */ \n\t"
|
||||
MONT_CMP(16)
|
||||
MONT_CMP(8)
|
||||
MONT_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
MONT_FIRSTSUB
|
||||
MONT_NEXTSUB(8)
|
||||
MONT_NEXTSUB(16)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [tmp] "r" (res+n), [M] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
mpn_copyi(this->mont_repr.data, res+n, n);
|
||||
}
|
||||
else if (n == 4)
|
||||
{ // use asm-optimized "CIOS method"
|
||||
|
||||
mp_limb_t tmp[n+1];
|
||||
mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this
|
||||
|
||||
__asm__ (MONT_PRECOMPUTE
|
||||
MONT_FIRSTITER(1)
|
||||
MONT_FIRSTITER(2)
|
||||
MONT_FIRSTITER(3)
|
||||
MONT_FINALIZE(3)
|
||||
MONT_ITERFIRST(1)
|
||||
MONT_ITERITER(1, 1)
|
||||
MONT_ITERITER(1, 2)
|
||||
MONT_ITERITER(1, 3)
|
||||
MONT_FINALIZE(3)
|
||||
MONT_ITERFIRST(2)
|
||||
MONT_ITERITER(2, 1)
|
||||
MONT_ITERITER(2, 2)
|
||||
MONT_ITERITER(2, 3)
|
||||
MONT_FINALIZE(3)
|
||||
MONT_ITERFIRST(3)
|
||||
MONT_ITERITER(3, 1)
|
||||
MONT_ITERITER(3, 2)
|
||||
MONT_ITERITER(3, 3)
|
||||
MONT_FINALIZE(3)
|
||||
"/* check for overflow */ \n\t"
|
||||
MONT_CMP(24)
|
||||
MONT_CMP(16)
|
||||
MONT_CMP(8)
|
||||
MONT_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
MONT_FIRSTSUB
|
||||
MONT_NEXTSUB(8)
|
||||
MONT_NEXTSUB(16)
|
||||
MONT_NEXTSUB(24)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data),
|
||||
[T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u)
|
||||
: "cc", "memory", "%rax", "%rdx"
|
||||
);
|
||||
mpn_copyi(this->mont_repr.data, tmp, n);
|
||||
}
|
||||
else if (n == 5)
|
||||
{ // use asm-optimized "CIOS method"
|
||||
|
||||
mp_limb_t tmp[n+1];
|
||||
mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this
|
||||
|
||||
__asm__ (MONT_PRECOMPUTE
|
||||
MONT_FIRSTITER(1)
|
||||
MONT_FIRSTITER(2)
|
||||
MONT_FIRSTITER(3)
|
||||
MONT_FIRSTITER(4)
|
||||
MONT_FINALIZE(4)
|
||||
MONT_ITERFIRST(1)
|
||||
MONT_ITERITER(1, 1)
|
||||
MONT_ITERITER(1, 2)
|
||||
MONT_ITERITER(1, 3)
|
||||
MONT_ITERITER(1, 4)
|
||||
MONT_FINALIZE(4)
|
||||
MONT_ITERFIRST(2)
|
||||
MONT_ITERITER(2, 1)
|
||||
MONT_ITERITER(2, 2)
|
||||
MONT_ITERITER(2, 3)
|
||||
MONT_ITERITER(2, 4)
|
||||
MONT_FINALIZE(4)
|
||||
MONT_ITERFIRST(3)
|
||||
MONT_ITERITER(3, 1)
|
||||
MONT_ITERITER(3, 2)
|
||||
MONT_ITERITER(3, 3)
|
||||
MONT_ITERITER(3, 4)
|
||||
MONT_FINALIZE(4)
|
||||
MONT_ITERFIRST(4)
|
||||
MONT_ITERITER(4, 1)
|
||||
MONT_ITERITER(4, 2)
|
||||
MONT_ITERITER(4, 3)
|
||||
MONT_ITERITER(4, 4)
|
||||
MONT_FINALIZE(4)
|
||||
"/* check for overflow */ \n\t"
|
||||
MONT_CMP(32)
|
||||
MONT_CMP(24)
|
||||
MONT_CMP(16)
|
||||
MONT_CMP(8)
|
||||
MONT_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
MONT_FIRSTSUB
|
||||
MONT_NEXTSUB(8)
|
||||
MONT_NEXTSUB(16)
|
||||
MONT_NEXTSUB(24)
|
||||
MONT_NEXTSUB(32)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data),
|
||||
[T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u)
|
||||
: "cc", "memory", "%rax", "%rdx"
|
||||
);
|
||||
mpn_copyi(this->mont_repr.data, tmp, n);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
mp_limb_t res[2*n];
|
||||
mpn_mul_n(res, this->mont_repr.data, other.data, n);
|
||||
|
||||
/*
|
||||
The Montgomery reduction here is based on Algorithm 14.32 in
|
||||
Handbook of Applied Cryptography
|
||||
<http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
*/
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
mp_limb_t k = inv * res[i];
|
||||
/* calculate res = res + k * mod * b^i */
|
||||
mp_limb_t carryout = mpn_addmul_1(res+i, modulus.data, n, k);
|
||||
carryout = mpn_add_1(res+n+i, res+n+i, n-i, carryout);
|
||||
assert(carryout == 0);
|
||||
}
|
||||
|
||||
if (mpn_cmp(res+n, modulus.data, n) >= 0)
|
||||
{
|
||||
const mp_limb_t borrow = mpn_sub(res+n, res+n, n, modulus.data, n);
|
||||
assert(borrow == 0);
|
||||
}
|
||||
|
||||
mpn_copyi(this->mont_repr.data, res+n, n);
|
||||
}
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>::Fp_model(const bigint<n> &b)
|
||||
{
|
||||
mpn_copyi(this->mont_repr.data, Rsquared.data, n);
|
||||
mul_reduce(b);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>::Fp_model(const long x, const bool is_unsigned)
|
||||
{
|
||||
if (is_unsigned || x >= 0)
|
||||
{
|
||||
this->mont_repr.data[0] = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
const mp_limb_t borrow = mpn_sub_1(this->mont_repr.data, modulus.data, n, -x);
|
||||
assert(borrow == 0);
|
||||
}
|
||||
|
||||
mul_reduce(Rsquared);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
void Fp_model<n,modulus>::set_ulong(const unsigned long x)
|
||||
{
|
||||
this->mont_repr.clear();
|
||||
this->mont_repr.data[0] = x;
|
||||
mul_reduce(Rsquared);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
void Fp_model<n,modulus>::clear()
|
||||
{
|
||||
this->mont_repr.clear();
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<n> Fp_model<n,modulus>::as_bigint() const
|
||||
{
|
||||
bigint<n> one;
|
||||
one.clear();
|
||||
one.data[0] = 1;
|
||||
|
||||
Fp_model<n, modulus> res(*this);
|
||||
res.mul_reduce(one);
|
||||
|
||||
return (res.mont_repr);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
unsigned long Fp_model<n,modulus>::as_ulong() const
|
||||
{
|
||||
return this->as_bigint().as_ulong();
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp_model<n,modulus>::operator==(const Fp_model& other) const
|
||||
{
|
||||
return (this->mont_repr == other.mont_repr);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp_model<n,modulus>::operator!=(const Fp_model& other) const
|
||||
{
|
||||
return (this->mont_repr != other.mont_repr);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp_model<n,modulus>::is_zero() const
|
||||
{
|
||||
return (this->mont_repr.is_zero()); // zero maps to zero
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
void Fp_model<n,modulus>::print() const
|
||||
{
|
||||
Fp_model<n,modulus> tmp;
|
||||
tmp.mont_repr.data[0] = 1;
|
||||
tmp.mul_reduce(this->mont_repr);
|
||||
|
||||
tmp.mont_repr.print();
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::zero()
|
||||
{
|
||||
Fp_model<n,modulus> res;
|
||||
res.mont_repr.clear();
|
||||
return res;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::one()
|
||||
{
|
||||
Fp_model<n,modulus> res;
|
||||
res.mont_repr.data[0] = 1;
|
||||
res.mul_reduce(Rsquared);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::operator+=(const Fp_model<n,modulus>& other)
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->add_cnt++;
|
||||
#endif
|
||||
#if defined(__x86_64__) && defined(USE_ASM)
|
||||
if (n == 3)
|
||||
{
|
||||
__asm__
|
||||
("/* perform bignum addition */ \n\t"
|
||||
ADD_FIRSTADD
|
||||
ADD_NEXTADD(8)
|
||||
ADD_NEXTADD(16)
|
||||
"/* if overflow: subtract */ \n\t"
|
||||
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
|
||||
"jc subtract%= \n\t"
|
||||
|
||||
"/* check for overflow */ \n\t"
|
||||
ADD_CMP(16)
|
||||
ADD_CMP(8)
|
||||
ADD_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
ADD_FIRSTSUB
|
||||
ADD_NEXTSUB(8)
|
||||
ADD_NEXTSUB(16)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else if (n == 4)
|
||||
{
|
||||
__asm__
|
||||
("/* perform bignum addition */ \n\t"
|
||||
ADD_FIRSTADD
|
||||
ADD_NEXTADD(8)
|
||||
ADD_NEXTADD(16)
|
||||
ADD_NEXTADD(24)
|
||||
"/* if overflow: subtract */ \n\t"
|
||||
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
|
||||
"jc subtract%= \n\t"
|
||||
|
||||
"/* check for overflow */ \n\t"
|
||||
ADD_CMP(24)
|
||||
ADD_CMP(16)
|
||||
ADD_CMP(8)
|
||||
ADD_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
ADD_FIRSTSUB
|
||||
ADD_NEXTSUB(8)
|
||||
ADD_NEXTSUB(16)
|
||||
ADD_NEXTSUB(24)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else if (n == 5)
|
||||
{
|
||||
__asm__
|
||||
("/* perform bignum addition */ \n\t"
|
||||
ADD_FIRSTADD
|
||||
ADD_NEXTADD(8)
|
||||
ADD_NEXTADD(16)
|
||||
ADD_NEXTADD(24)
|
||||
ADD_NEXTADD(32)
|
||||
"/* if overflow: subtract */ \n\t"
|
||||
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
|
||||
"jc subtract%= \n\t"
|
||||
|
||||
"/* check for overflow */ \n\t"
|
||||
ADD_CMP(32)
|
||||
ADD_CMP(24)
|
||||
ADD_CMP(16)
|
||||
ADD_CMP(8)
|
||||
ADD_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
ADD_FIRSTSUB
|
||||
ADD_NEXTSUB(8)
|
||||
ADD_NEXTSUB(16)
|
||||
ADD_NEXTSUB(24)
|
||||
ADD_NEXTSUB(32)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
mp_limb_t scratch[n+1];
|
||||
const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, other.mont_repr.data, n);
|
||||
scratch[n] = carry;
|
||||
|
||||
if (carry || mpn_cmp(scratch, modulus.data, n) >= 0)
|
||||
{
|
||||
const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, modulus.data, n);
|
||||
assert(borrow == 0);
|
||||
}
|
||||
|
||||
mpn_copyi(this->mont_repr.data, scratch, n);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::operator-=(const Fp_model<n,modulus>& other)
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->sub_cnt++;
|
||||
#endif
|
||||
#if defined(__x86_64__) && defined(USE_ASM)
|
||||
if (n == 3)
|
||||
{
|
||||
__asm__
|
||||
(SUB_FIRSTSUB
|
||||
SUB_NEXTSUB(8)
|
||||
SUB_NEXTSUB(16)
|
||||
|
||||
"jnc done%=\n\t"
|
||||
|
||||
SUB_FIRSTADD
|
||||
SUB_NEXTADD(8)
|
||||
SUB_NEXTADD(16)
|
||||
|
||||
"done%=:\n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else if (n == 4)
|
||||
{
|
||||
__asm__
|
||||
(SUB_FIRSTSUB
|
||||
SUB_NEXTSUB(8)
|
||||
SUB_NEXTSUB(16)
|
||||
SUB_NEXTSUB(24)
|
||||
|
||||
"jnc done%=\n\t"
|
||||
|
||||
SUB_FIRSTADD
|
||||
SUB_NEXTADD(8)
|
||||
SUB_NEXTADD(16)
|
||||
SUB_NEXTADD(24)
|
||||
|
||||
"done%=:\n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else if (n == 5)
|
||||
{
|
||||
__asm__
|
||||
(SUB_FIRSTSUB
|
||||
SUB_NEXTSUB(8)
|
||||
SUB_NEXTSUB(16)
|
||||
SUB_NEXTSUB(24)
|
||||
SUB_NEXTSUB(32)
|
||||
|
||||
"jnc done%=\n\t"
|
||||
|
||||
SUB_FIRSTADD
|
||||
SUB_NEXTADD(8)
|
||||
SUB_NEXTADD(16)
|
||||
SUB_NEXTADD(24)
|
||||
SUB_NEXTADD(32)
|
||||
|
||||
"done%=:\n\t"
|
||||
:
|
||||
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
mp_limb_t scratch[n+1];
|
||||
if (mpn_cmp(this->mont_repr.data, other.mont_repr.data, n) < 0)
|
||||
{
|
||||
const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, modulus.data, n);
|
||||
scratch[n] = carry;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpn_copyi(scratch, this->mont_repr.data, n);
|
||||
scratch[n] = 0;
|
||||
}
|
||||
|
||||
const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, other.mont_repr.data, n);
|
||||
assert(borrow == 0);
|
||||
|
||||
mpn_copyi(this->mont_repr.data, scratch, n);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::operator*=(const Fp_model<n,modulus>& other)
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->mul_cnt++;
|
||||
#endif
|
||||
|
||||
mul_reduce(other.mont_repr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::operator^=(const unsigned long pow)
|
||||
{
|
||||
(*this) = power<Fp_model<n, modulus> >(*this, pow);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::operator^=(const bigint<m> &pow)
|
||||
{
|
||||
(*this) = power<Fp_model<n, modulus>, m>(*this, pow);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator+(const Fp_model<n,modulus>& other) const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r += other);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator-(const Fp_model<n,modulus>& other) const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r -= other);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator*(const Fp_model<n,modulus>& other) const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r *= other);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator^(const unsigned long pow) const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r ^= pow);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator^(const bigint<m> &pow) const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r ^= pow);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::operator-() const
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->sub_cnt++;
|
||||
#endif
|
||||
|
||||
if (this->is_zero())
|
||||
{
|
||||
return (*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Fp_model<n, modulus> r;
|
||||
mpn_sub_n(r.mont_repr.data, modulus.data, this->mont_repr.data, n);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::squared() const
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->sqr_cnt++;
|
||||
this->mul_cnt--; // zero out the upcoming mul
|
||||
#endif
|
||||
/* stupid pre-processor tricks; beware */
|
||||
#if defined(__x86_64__) && defined(USE_ASM)
|
||||
if (n == 3)
|
||||
{ // use asm-optimized Comba squaring
|
||||
mp_limb_t res[2*n];
|
||||
mp_limb_t c0, c1, c2;
|
||||
COMBA_3_BY_3_SQR(c0, c1, c2, res, this->mont_repr.data);
|
||||
|
||||
mp_limb_t k;
|
||||
mp_limb_t tmp1, tmp2, tmp3;
|
||||
REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data);
|
||||
|
||||
/* subtract t > mod */
|
||||
__asm__ volatile
|
||||
("/* check for overflow */ \n\t"
|
||||
MONT_CMP(16)
|
||||
MONT_CMP(8)
|
||||
MONT_CMP(0)
|
||||
|
||||
"/* subtract mod if overflow */ \n\t"
|
||||
"subtract%=: \n\t"
|
||||
MONT_FIRSTSUB
|
||||
MONT_NEXTSUB(8)
|
||||
MONT_NEXTSUB(16)
|
||||
"done%=: \n\t"
|
||||
:
|
||||
: [tmp] "r" (res+n), [M] "r" (modulus.data)
|
||||
: "cc", "memory", "%rax");
|
||||
|
||||
Fp_model<n, modulus> r;
|
||||
mpn_copyi(r.mont_repr.data, res+n, n);
|
||||
return r;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r *= r);
|
||||
}
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus>& Fp_model<n,modulus>::invert()
|
||||
{
|
||||
#ifdef PROFILE_OP_COUNTS
|
||||
this->inv_cnt++;
|
||||
#endif
|
||||
|
||||
assert(!this->is_zero());
|
||||
|
||||
bigint<n> g; /* gp should have room for vn = n limbs */
|
||||
|
||||
mp_limb_t s[n+1]; /* sp should have room for vn+1 limbs */
|
||||
mp_size_t sn;
|
||||
|
||||
bigint<n> v = modulus; // both source operands are destroyed by mpn_gcdext
|
||||
|
||||
/* computes gcd(u, v) = g = u*s + v*t, so s*u will be 1 (mod v) */
|
||||
const mp_size_t gn = mpn_gcdext(g.data, s, &sn, this->mont_repr.data, n, v.data, n);
|
||||
assert(gn == 1 && g.data[0] == 1); /* inverse exists */
|
||||
|
||||
mp_limb_t q; /* division result fits into q, as sn <= n+1 */
|
||||
/* sn < 0 indicates negative sn; will fix up later */
|
||||
|
||||
if (std::abs(sn) >= n)
|
||||
{
|
||||
/* if sn could require modulus reduction, do it here */
|
||||
mpn_tdiv_qr(&q, this->mont_repr.data, 0, s, std::abs(sn), modulus.data, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise just copy it over */
|
||||
mpn_zero(this->mont_repr.data, n);
|
||||
mpn_copyi(this->mont_repr.data, s, std::abs(sn));
|
||||
}
|
||||
|
||||
/* fix up the negative sn */
|
||||
if (sn < 0)
|
||||
{
|
||||
const mp_limb_t borrow = mpn_sub_n(this->mont_repr.data, modulus.data, this->mont_repr.data, n);
|
||||
assert(borrow == 0);
|
||||
}
|
||||
|
||||
mul_reduce(Rcubed);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::inverse() const
|
||||
{
|
||||
Fp_model<n, modulus> r(*this);
|
||||
return (r.invert());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp_model<n,modulus>::random_element() /// returns random element of Fp_model
|
||||
{
|
||||
/* note that as Montgomery representation is a bijection then
|
||||
selecting a random element of {xR} is the same as selecting a
|
||||
random element of {x} */
|
||||
Fp_model<n, modulus> r;
|
||||
do
|
||||
{
|
||||
r.mont_repr.randomize();
|
||||
|
||||
/* clear all bits higher than MSB of modulus */
|
||||
size_t bitno = GMP_NUMB_BITS * n - 1;
|
||||
while (modulus.test_bit(bitno) == false)
|
||||
{
|
||||
const std::size_t part = bitno/GMP_NUMB_BITS;
|
||||
const std::size_t bit = bitno - (GMP_NUMB_BITS*part);
|
||||
|
||||
r.mont_repr.data[part] &= ~(1ul<<bit);
|
||||
|
||||
bitno--;
|
||||
}
|
||||
}
|
||||
/* if r.data is still >= modulus -- repeat (rejection sampling) */
|
||||
while (mpn_cmp(r.mont_repr.data, modulus.data, n) >= 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n,modulus> Fp_model<n,modulus>::sqrt() const
|
||||
{
|
||||
if (is_zero()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fp_model<n,modulus> one = Fp_model<n,modulus>::one();
|
||||
|
||||
size_t v = Fp_model<n,modulus>::s;
|
||||
Fp_model<n,modulus> z = Fp_model<n,modulus>::nqr_to_t;
|
||||
Fp_model<n,modulus> w = (*this)^Fp_model<n,modulus>::t_minus_1_over_2;
|
||||
Fp_model<n,modulus> x = (*this) * w;
|
||||
Fp_model<n,modulus> b = x * w; // b = (*this)^t
|
||||
|
||||
|
||||
// check if square with euler's criterion
|
||||
Fp_model<n,modulus> check = b;
|
||||
for (size_t i = 0; i < v-1; ++i)
|
||||
{
|
||||
check = check.squared();
|
||||
}
|
||||
if (check != one)
|
||||
{
|
||||
assert_except(0);
|
||||
}
|
||||
|
||||
|
||||
// compute square root with Tonelli--Shanks
|
||||
// (does not terminate if not a square!)
|
||||
|
||||
while (b != one)
|
||||
{
|
||||
size_t m = 0;
|
||||
Fp_model<n,modulus> b2m = b;
|
||||
while (b2m != one)
|
||||
{
|
||||
/* invariant: b2m = b^(2^m) after entering this loop */
|
||||
b2m = b2m.squared();
|
||||
m += 1;
|
||||
}
|
||||
|
||||
int j = v-m-1;
|
||||
w = z;
|
||||
while (j > 0)
|
||||
{
|
||||
w = w.squared();
|
||||
--j;
|
||||
} // w = z^2^(v-m-1)
|
||||
|
||||
z = w.squared();
|
||||
b = b * z;
|
||||
x = x * w;
|
||||
v = m;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &out, const Fp_model<n, modulus> &p)
|
||||
{
|
||||
#ifndef MONTGOMERY_OUTPUT
|
||||
Fp_model<n,modulus> tmp;
|
||||
tmp.mont_repr.data[0] = 1;
|
||||
tmp.mul_reduce(p.mont_repr);
|
||||
out << tmp.mont_repr;
|
||||
#else
|
||||
out << p.mont_repr;
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &in, Fp_model<n, modulus> &p)
|
||||
{
|
||||
#ifndef MONTGOMERY_OUTPUT
|
||||
in >> p.mont_repr;
|
||||
p.mul_reduce(Fp_model<n, modulus>::Rsquared);
|
||||
#else
|
||||
in >> p.mont_repr;
|
||||
#endif
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FP_TCC_
|
||||
116
src/algebra/fields/fp12_2over3over2.hpp
Normal file
116
src/algebra/fields/fp12_2over3over2.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Declaration of arithmetic in the finite field F[((p^2)^3)^2].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP12_2OVER3OVER2_HPP_
|
||||
#define FP12_2OVER3OVER2_HPP_
|
||||
#include "algebra/fields/fp.hpp"
|
||||
#include "algebra/fields/fp2.hpp"
|
||||
#include "algebra/fields/fp6_3over2.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp12_2over3over2_model;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &, const Fp12_2over3over2_model<n, modulus> &);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &, Fp12_2over3over2_model<n, modulus> &);
|
||||
|
||||
/**
|
||||
* Arithmetic in the finite field F[((p^2)^3)^2].
|
||||
*
|
||||
* Let p := modulus. This interface provides arithmetic for the extension field
|
||||
* Fp12 = Fp6[W]/(W^2-V) where Fp6 = Fp2[V]/(V^3-non_residue) and non_residue is in Fp2
|
||||
*
|
||||
* ASSUMPTION: p = 1 (mod 6)
|
||||
*/
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp12_2over3over2_model {
|
||||
public:
|
||||
typedef Fp_model<n, modulus> my_Fp;
|
||||
typedef Fp2_model<n, modulus> my_Fp2;
|
||||
typedef Fp6_3over2_model<n, modulus> my_Fp6;
|
||||
|
||||
static Fp2_model<n, modulus> non_residue;
|
||||
static Fp2_model<n, modulus> Frobenius_coeffs_c1[12]; // non_residue^((modulus^i-1)/6) for i=0,...,11
|
||||
|
||||
my_Fp6 c0, c1;
|
||||
Fp12_2over3over2_model() {};
|
||||
Fp12_2over3over2_model(const my_Fp6& c0, const my_Fp6& c1) : c0(c0), c1(c1) {};
|
||||
|
||||
void clear() { c0.clear(); c1.clear(); }
|
||||
void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); }
|
||||
|
||||
static Fp12_2over3over2_model<n, modulus> zero();
|
||||
static Fp12_2over3over2_model<n, modulus> one();
|
||||
static Fp12_2over3over2_model<n, modulus> random_element();
|
||||
|
||||
bool is_zero() const { return c0.is_zero() && c1.is_zero(); }
|
||||
bool operator==(const Fp12_2over3over2_model &other) const;
|
||||
bool operator!=(const Fp12_2over3over2_model &other) const;
|
||||
|
||||
Fp12_2over3over2_model operator+(const Fp12_2over3over2_model &other) const;
|
||||
Fp12_2over3over2_model operator-(const Fp12_2over3over2_model &other) const;
|
||||
Fp12_2over3over2_model operator*(const Fp12_2over3over2_model &other) const;
|
||||
Fp12_2over3over2_model operator-() const;
|
||||
Fp12_2over3over2_model squared() const; // default is squared_complex
|
||||
Fp12_2over3over2_model squared_karatsuba() const;
|
||||
Fp12_2over3over2_model squared_complex() const;
|
||||
Fp12_2over3over2_model inverse() const;
|
||||
Fp12_2over3over2_model Frobenius_map(unsigned long power) const;
|
||||
Fp12_2over3over2_model unitary_inverse() const;
|
||||
Fp12_2over3over2_model cyclotomic_squared() const;
|
||||
|
||||
Fp12_2over3over2_model mul_by_024(const my_Fp2 &ell_0, const my_Fp2 &ell_VW, const my_Fp2 &ell_VV) const;
|
||||
|
||||
static my_Fp6 mul_by_non_residue(const my_Fp6 &elt);
|
||||
|
||||
template<mp_size_t m>
|
||||
Fp12_2over3over2_model cyclotomic_exp(const bigint<m> &exponent) const;
|
||||
|
||||
static bigint<n> base_field_char() { return modulus; }
|
||||
static size_t extension_degree() { return 12; }
|
||||
|
||||
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp12_2over3over2_model<n, modulus> &el);
|
||||
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp12_2over3over2_model<n, modulus> &el);
|
||||
};
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp12_2over3over2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp12_2over3over2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp6_3over2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m>
|
||||
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const bigint<m> &exponent);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m, const bigint<m>& exp_modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const Fp_model<m, exp_modulus> &exponent);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp12_2over3over2_model<n, modulus>::non_residue;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp12_2over3over2_model<n, modulus>::Frobenius_coeffs_c1[12];
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/fp12_2over3over2.tcc"
|
||||
#endif // FP12_2OVER3OVER2_HPP_
|
||||
412
src/algebra/fields/fp12_2over3over2.tcc
Normal file
412
src/algebra/fields/fp12_2over3over2.tcc
Normal file
@@ -0,0 +1,412 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[((p^2)^3)^2].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP12_2OVER3OVER2_TCC_
|
||||
#define FP12_2OVER3OVER2_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n, modulus> Fp12_2over3over2_model<n,modulus>::mul_by_non_residue(const Fp6_3over2_model<n, modulus> &elt)
|
||||
{
|
||||
return Fp6_3over2_model<n, modulus>(non_residue * elt.c2, elt.c0, elt.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::zero()
|
||||
{
|
||||
return Fp12_2over3over2_model<n, modulus>(my_Fp6::zero(), my_Fp6::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::one()
|
||||
{
|
||||
return Fp12_2over3over2_model<n, modulus>(my_Fp6::one(), my_Fp6::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::random_element()
|
||||
{
|
||||
Fp12_2over3over2_model<n, modulus> r;
|
||||
r.c0 = my_Fp6::random_element();
|
||||
r.c1 = my_Fp6::random_element();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp12_2over3over2_model<n,modulus>::operator==(const Fp12_2over3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return (this->c0 == other.c0 && this->c1 == other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp12_2over3over2_model<n,modulus>::operator!=(const Fp12_2over3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator+(const Fp12_2over3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(this->c0 + other.c0,
|
||||
this->c1 + other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator-(const Fp12_2over3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(this->c0 - other.c0,
|
||||
this->c1 - other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator*(const Fp6_3over2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator*(const Fp12_2over3over2_model<n,modulus> &other) const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */
|
||||
|
||||
const my_Fp6 &A = other.c0, &B = other.c1,
|
||||
&a = this->c0, &b = this->c1;
|
||||
const my_Fp6 aA = a * A;
|
||||
const my_Fp6 bB = b * B;
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>(aA + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(bB),
|
||||
(a + b)*(A+B) - aA - bB);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator-() const
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(-this->c0,
|
||||
-this->c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared() const
|
||||
{
|
||||
return squared_complex();
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared_karatsuba() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */
|
||||
|
||||
const my_Fp6 &a = this->c0, &b = this->c1;
|
||||
const my_Fp6 asq = a.squared();
|
||||
const my_Fp6 bsq = b.squared();
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>(asq + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(bsq),
|
||||
(a + b).squared() - asq - bsq);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared_complex() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */
|
||||
|
||||
const my_Fp6 &a = this->c0, &b = this->c1;
|
||||
const my_Fp6 ab = a * b;
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>((a + b) * (a + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(b)) - ab - Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(ab),
|
||||
ab + ab);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::inverse() const
|
||||
{
|
||||
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */
|
||||
|
||||
const my_Fp6 &a = this->c0, &b = this->c1;
|
||||
const my_Fp6 t0 = a.squared();
|
||||
const my_Fp6 t1 = b.squared();
|
||||
const my_Fp6 t2 = t0 - Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(t1);
|
||||
const my_Fp6 t3 = t2.inverse();
|
||||
const my_Fp6 c0 = a * t3;
|
||||
const my_Fp6 c1 = - (b * t3);
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>(c0, c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::Frobenius_map(unsigned long power) const
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(c0.Frobenius_map(power),
|
||||
Frobenius_coeffs_c1[power % 12] * c1.Frobenius_map(power));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::unitary_inverse() const
|
||||
{
|
||||
return Fp12_2over3over2_model<n,modulus>(this->c0,
|
||||
-this->c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::cyclotomic_squared() const
|
||||
{
|
||||
/* OLD: naive implementation
|
||||
return (*this).squared();
|
||||
*/
|
||||
my_Fp2 z0 = this->c0.c0;
|
||||
my_Fp2 z4 = this->c0.c1;
|
||||
my_Fp2 z3 = this->c0.c2;
|
||||
my_Fp2 z2 = this->c1.c0;
|
||||
my_Fp2 z1 = this->c1.c1;
|
||||
my_Fp2 z5 = this->c1.c2;
|
||||
|
||||
my_Fp2 t0, t1, t2, t3, t4, t5, tmp;
|
||||
|
||||
// t0 + t1*y = (z0 + z1*y)^2 = a^2
|
||||
tmp = z0 * z1;
|
||||
t0 = (z0 + z1) * (z0 + my_Fp6::non_residue * z1) - tmp - my_Fp6::non_residue * tmp;
|
||||
t1 = tmp + tmp;
|
||||
// t2 + t3*y = (z2 + z3*y)^2 = b^2
|
||||
tmp = z2 * z3;
|
||||
t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp;
|
||||
t3 = tmp + tmp;
|
||||
// t4 + t5*y = (z4 + z5*y)^2 = c^2
|
||||
tmp = z4 * z5;
|
||||
t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp;
|
||||
t5 = tmp + tmp;
|
||||
|
||||
// for A
|
||||
|
||||
// z0 = 3 * t0 - 2 * z0
|
||||
z0 = t0 - z0;
|
||||
z0 = z0 + z0;
|
||||
z0 = z0 + t0;
|
||||
// z1 = 3 * t1 + 2 * z1
|
||||
z1 = t1 + z1;
|
||||
z1 = z1 + z1;
|
||||
z1 = z1 + t1;
|
||||
|
||||
// for B
|
||||
|
||||
// z2 = 3 * (xi * t5) + 2 * z2
|
||||
tmp = my_Fp6::non_residue * t5;
|
||||
z2 = tmp + z2;
|
||||
z2 = z2 + z2;
|
||||
z2 = z2 + tmp;
|
||||
|
||||
// z3 = 3 * t4 - 2 * z3
|
||||
z3 = t4 - z3;
|
||||
z3 = z3 + z3;
|
||||
z3 = z3 + t4;
|
||||
|
||||
// for C
|
||||
|
||||
// z4 = 3 * t2 - 2 * z4
|
||||
z4 = t2 - z4;
|
||||
z4 = z4 + z4;
|
||||
z4 = z4 + t2;
|
||||
|
||||
// z5 = 3 * t3 + 2 * z5
|
||||
z5 = t3 + z5;
|
||||
z5 = z5 + z5;
|
||||
z5 = z5 + t3;
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>(my_Fp6(z0,z4,z3),my_Fp6(z2,z1,z5));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::mul_by_024(const Fp2_model<n, modulus> &ell_0,
|
||||
const Fp2_model<n, modulus> &ell_VW,
|
||||
const Fp2_model<n, modulus> &ell_VV) const
|
||||
{
|
||||
/* OLD: naive implementation
|
||||
Fp12_2over3over2_model<n,modulus> a(my_Fp6(ell_0, my_Fp2::zero(), ell_VV),
|
||||
my_Fp6(my_Fp2::zero(), ell_VW, my_Fp2::zero()));
|
||||
|
||||
return (*this) * a;
|
||||
*/
|
||||
my_Fp2 z0 = this->c0.c0;
|
||||
my_Fp2 z1 = this->c0.c1;
|
||||
my_Fp2 z2 = this->c0.c2;
|
||||
my_Fp2 z3 = this->c1.c0;
|
||||
my_Fp2 z4 = this->c1.c1;
|
||||
my_Fp2 z5 = this->c1.c2;
|
||||
|
||||
my_Fp2 x0 = ell_0;
|
||||
my_Fp2 x2 = ell_VV;
|
||||
my_Fp2 x4 = ell_VW;
|
||||
|
||||
my_Fp2 t0, t1, t2, s0, T3, T4, D0, D2, D4, S1;
|
||||
|
||||
D0 = z0 * x0;
|
||||
D2 = z2 * x2;
|
||||
D4 = z4 * x4;
|
||||
t2 = z0 + z4;
|
||||
t1 = z0 + z2;
|
||||
s0 = z1 + z3 + z5;
|
||||
|
||||
// For z.a_.a_ = z0.
|
||||
S1 = z1 * x2;
|
||||
T3 = S1 + D4;
|
||||
T4 = my_Fp6::non_residue * T3 + D0;
|
||||
z0 = T4;
|
||||
|
||||
// For z.a_.b_ = z1
|
||||
T3 = z5 * x4;
|
||||
S1 = S1 + T3;
|
||||
T3 = T3 + D2;
|
||||
T4 = my_Fp6::non_residue * T3;
|
||||
T3 = z1 * x0;
|
||||
S1 = S1 + T3;
|
||||
T4 = T4 + T3;
|
||||
z1 = T4;
|
||||
|
||||
// For z.a_.c_ = z2
|
||||
t0 = x0 + x2;
|
||||
T3 = t1 * t0 - D0 - D2;
|
||||
T4 = z3 * x4;
|
||||
S1 = S1 + T4;
|
||||
T3 = T3 + T4;
|
||||
|
||||
// For z.b_.a_ = z3 (z3 needs z2)
|
||||
t0 = z2 + z4;
|
||||
z2 = T3;
|
||||
t1 = x2 + x4;
|
||||
T3 = t0 * t1 - D2 - D4;
|
||||
T4 = my_Fp6::non_residue * T3;
|
||||
T3 = z3 * x0;
|
||||
S1 = S1 + T3;
|
||||
T4 = T4 + T3;
|
||||
z3 = T4;
|
||||
|
||||
// For z.b_.b_ = z4
|
||||
T3 = z5 * x2;
|
||||
S1 = S1 + T3;
|
||||
T4 = my_Fp6::non_residue * T3;
|
||||
t0 = x0 + x4;
|
||||
T3 = t2 * t0 - D0 - D4;
|
||||
T4 = T4 + T3;
|
||||
z4 = T4;
|
||||
|
||||
// For z.b_.c_ = z5.
|
||||
t0 = x0 + x2 + x4;
|
||||
T3 = s0 * t0 - S1;
|
||||
z5 = T3;
|
||||
|
||||
return Fp12_2over3over2_model<n,modulus>(my_Fp6(z0,z1,z2),my_Fp6(z3,z4,z5));
|
||||
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m>
|
||||
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const bigint<m> &exponent)
|
||||
{
|
||||
return power<Fp12_2over3over2_model<n, modulus> >(self, exponent);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m, const bigint<m>& exp_modulus>
|
||||
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const Fp_model<m, exp_modulus> &exponent)
|
||||
{
|
||||
return self^(exponent.as_bigint());
|
||||
}
|
||||
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp12_2over3over2_model<n, modulus> Fp12_2over3over2_model<n,modulus>::cyclotomic_exp(const bigint<m> &exponent) const
|
||||
{
|
||||
Fp12_2over3over2_model<n,modulus> res = Fp12_2over3over2_model<n,modulus>::one();
|
||||
|
||||
bool found_one = false;
|
||||
for (long i = m-1; i >= 0; --i)
|
||||
{
|
||||
for (long j = GMP_NUMB_BITS - 1; j >= 0; --j)
|
||||
{
|
||||
if (found_one)
|
||||
{
|
||||
res = res.cyclotomic_squared();
|
||||
}
|
||||
|
||||
if (exponent.data[i] & (1ul<<j))
|
||||
{
|
||||
found_one = true;
|
||||
res = res * (*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &out, const Fp12_2over3over2_model<n, modulus> &el)
|
||||
{
|
||||
out << el.c0 << OUTPUT_SEPARATOR << el.c1;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &in, Fp12_2over3over2_model<n, modulus> &el)
|
||||
{
|
||||
in >> el.c0 >> el.c1;
|
||||
return in;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp12_2over3over2_model<n, modulus> > &v)
|
||||
{
|
||||
out << v.size() << "\n";
|
||||
for (const Fp12_2over3over2_model<n, modulus>& t : v)
|
||||
{
|
||||
out << t << OUTPUT_NEWLINE;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp12_2over3over2_model<n, modulus> > &v)
|
||||
{
|
||||
v.clear();
|
||||
|
||||
size_t s;
|
||||
in >> s;
|
||||
|
||||
char b;
|
||||
in.read(&b, 1);
|
||||
|
||||
v.reserve(s);
|
||||
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
{
|
||||
Fp12_2over3over2_model<n, modulus> el;
|
||||
in >> el;
|
||||
v.emplace_back(el);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FP12_2OVER3OVER2_TCC_
|
||||
120
src/algebra/fields/fp2.hpp
Normal file
120
src/algebra/fields/fp2.hpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[p^2].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP2_HPP_
|
||||
#define FP2_HPP_
|
||||
#include "algebra/fields/fp.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp2_model;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &, const Fp2_model<n, modulus> &);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &, Fp2_model<n, modulus> &);
|
||||
|
||||
/**
|
||||
* Arithmetic in the field F[p^3].
|
||||
*
|
||||
* Let p := modulus. This interface provides arithmetic for the extension field
|
||||
* Fp2 = Fp[U]/(U^2-non_residue), where non_residue is in Fp.
|
||||
*
|
||||
* ASSUMPTION: p = 1 (mod 6)
|
||||
*/
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp2_model {
|
||||
public:
|
||||
typedef Fp_model<n, modulus> my_Fp;
|
||||
|
||||
static bigint<2*n> euler; // (modulus^2-1)/2
|
||||
static size_t s; // modulus^2 = 2^s * t + 1
|
||||
static bigint<2*n> t; // with t odd
|
||||
static bigint<2*n> t_minus_1_over_2; // (t-1)/2
|
||||
static my_Fp non_residue; // X^4-non_residue irreducible over Fp; used for constructing Fp2 = Fp[X] / (X^2 - non_residue)
|
||||
static Fp2_model<n, modulus> nqr; // a quadratic nonresidue in Fp2
|
||||
static Fp2_model<n, modulus> nqr_to_t; // nqr^t
|
||||
static my_Fp Frobenius_coeffs_c1[2]; // non_residue^((modulus^i-1)/2) for i=0,1
|
||||
|
||||
my_Fp c0, c1;
|
||||
Fp2_model() {};
|
||||
Fp2_model(const my_Fp& c0, const my_Fp& c1) : c0(c0), c1(c1) {};
|
||||
|
||||
void clear() { c0.clear(); c1.clear(); }
|
||||
void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); }
|
||||
|
||||
static Fp2_model<n, modulus> zero();
|
||||
static Fp2_model<n, modulus> one();
|
||||
static Fp2_model<n, modulus> random_element();
|
||||
|
||||
bool is_zero() const { return c0.is_zero() && c1.is_zero(); }
|
||||
bool operator==(const Fp2_model &other) const;
|
||||
bool operator!=(const Fp2_model &other) const;
|
||||
|
||||
Fp2_model operator+(const Fp2_model &other) const;
|
||||
Fp2_model operator-(const Fp2_model &other) const;
|
||||
Fp2_model operator*(const Fp2_model &other) const;
|
||||
Fp2_model operator-() const;
|
||||
Fp2_model squared() const; // default is squared_complex
|
||||
Fp2_model inverse() const;
|
||||
Fp2_model Frobenius_map(unsigned long power) const;
|
||||
Fp2_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate)
|
||||
Fp2_model squared_karatsuba() const;
|
||||
Fp2_model squared_complex() const;
|
||||
|
||||
template<mp_size_t m>
|
||||
Fp2_model operator^(const bigint<m> &other) const;
|
||||
|
||||
static size_t size_in_bits() { return 2*my_Fp::size_in_bits(); }
|
||||
static bigint<n> base_field_char() { return modulus; }
|
||||
|
||||
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp2_model<n, modulus> &el);
|
||||
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp2_model<n, modulus> &el);
|
||||
};
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<2*n> Fp2_model<n, modulus>::euler;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
size_t Fp2_model<n, modulus>::s;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<2*n> Fp2_model<n, modulus>::t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<2*n> Fp2_model<n, modulus>::t_minus_1_over_2;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp2_model<n, modulus>::non_residue;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp2_model<n, modulus>::nqr;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp2_model<n, modulus>::nqr_to_t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp2_model<n, modulus>::Frobenius_coeffs_c1[2];
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/fp2.tcc"
|
||||
|
||||
#endif // FP2_HPP_
|
||||
261
src/algebra/fields/fp2.tcc
Normal file
261
src/algebra/fields/fp2.tcc
Normal file
@@ -0,0 +1,261 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[p^2].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP2_TCC_
|
||||
#define FP2_TCC_
|
||||
|
||||
#include "algebra/fields/field_utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::zero()
|
||||
{
|
||||
return Fp2_model<n, modulus>(my_Fp::zero(), my_Fp::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::one()
|
||||
{
|
||||
return Fp2_model<n, modulus>(my_Fp::one(), my_Fp::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::random_element()
|
||||
{
|
||||
Fp2_model<n, modulus> r;
|
||||
r.c0 = my_Fp::random_element();
|
||||
r.c1 = my_Fp::random_element();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp2_model<n,modulus>::operator==(const Fp2_model<n,modulus> &other) const
|
||||
{
|
||||
return (this->c0 == other.c0 && this->c1 == other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp2_model<n,modulus>::operator!=(const Fp2_model<n,modulus> &other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator+(const Fp2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp2_model<n,modulus>(this->c0 + other.c0,
|
||||
this->c1 + other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator-(const Fp2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp2_model<n,modulus>(this->c0 - other.c0,
|
||||
this->c1 - other.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator*(const Fp2_model<n,modulus> &other) const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */
|
||||
const my_Fp
|
||||
&A = other.c0, &B = other.c1,
|
||||
&a = this->c0, &b = this->c1;
|
||||
const my_Fp aA = a * A;
|
||||
const my_Fp bB = b * B;
|
||||
|
||||
return Fp2_model<n,modulus>(aA + non_residue * bB,
|
||||
(a + b)*(A+B) - aA - bB);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator-() const
|
||||
{
|
||||
return Fp2_model<n,modulus>(-this->c0,
|
||||
-this->c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared() const
|
||||
{
|
||||
return squared_complex();
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared_karatsuba() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */
|
||||
const my_Fp &a = this->c0, &b = this->c1;
|
||||
const my_Fp asq = a.squared();
|
||||
const my_Fp bsq = b.squared();
|
||||
|
||||
return Fp2_model<n,modulus>(asq + non_residue * bsq,
|
||||
(a + b).squared() - asq - bsq);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared_complex() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */
|
||||
const my_Fp &a = this->c0, &b = this->c1;
|
||||
const my_Fp ab = a * b;
|
||||
|
||||
return Fp2_model<n,modulus>((a + b) * (a + non_residue * b) - ab - non_residue * ab,
|
||||
ab + ab);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::inverse() const
|
||||
{
|
||||
const my_Fp &a = this->c0, &b = this->c1;
|
||||
|
||||
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */
|
||||
const my_Fp t0 = a.squared();
|
||||
const my_Fp t1 = b.squared();
|
||||
const my_Fp t2 = t0 - non_residue * t1;
|
||||
const my_Fp t3 = t2.inverse();
|
||||
const my_Fp c0 = a * t3;
|
||||
const my_Fp c1 = - (b * t3);
|
||||
|
||||
return Fp2_model<n,modulus>(c0, c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::Frobenius_map(unsigned long power) const
|
||||
{
|
||||
return Fp2_model<n,modulus>(c0,
|
||||
Frobenius_coeffs_c1[power % 2] * c1);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::sqrt() const
|
||||
{
|
||||
if (is_zero()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fp2_model<n,modulus> one = Fp2_model<n,modulus>::one();
|
||||
|
||||
size_t v = Fp2_model<n,modulus>::s;
|
||||
Fp2_model<n,modulus> z = Fp2_model<n,modulus>::nqr_to_t;
|
||||
Fp2_model<n,modulus> w = (*this)^Fp2_model<n,modulus>::t_minus_1_over_2;
|
||||
Fp2_model<n,modulus> x = (*this) * w;
|
||||
Fp2_model<n,modulus> b = x * w; // b = (*this)^t
|
||||
|
||||
|
||||
// check if square with euler's criterion
|
||||
Fp2_model<n,modulus> check = b;
|
||||
for (size_t i = 0; i < v-1; ++i)
|
||||
{
|
||||
check = check.squared();
|
||||
}
|
||||
if (check != one)
|
||||
{
|
||||
assert_except(0);
|
||||
}
|
||||
|
||||
|
||||
// compute square root with Tonelli--Shanks
|
||||
// (does not terminate if not a square!)
|
||||
|
||||
while (b != one)
|
||||
{
|
||||
size_t m = 0;
|
||||
Fp2_model<n,modulus> b2m = b;
|
||||
while (b2m != one)
|
||||
{
|
||||
/* invariant: b2m = b^(2^m) after entering this loop */
|
||||
b2m = b2m.squared();
|
||||
m += 1;
|
||||
}
|
||||
|
||||
int j = v-m-1;
|
||||
w = z;
|
||||
while (j > 0)
|
||||
{
|
||||
w = w.squared();
|
||||
--j;
|
||||
} // w = z^2^(v-m-1)
|
||||
|
||||
z = w.squared();
|
||||
b = b * z;
|
||||
x = x * w;
|
||||
v = m;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator^(const bigint<m> &pow) const
|
||||
{
|
||||
return power<Fp2_model<n, modulus>, m>(*this, pow);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &out, const Fp2_model<n, modulus> &el)
|
||||
{
|
||||
out << el.c0 << OUTPUT_SEPARATOR << el.c1;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &in, Fp2_model<n, modulus> &el)
|
||||
{
|
||||
in >> el.c0 >> el.c1;
|
||||
return in;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp2_model<n, modulus> > &v)
|
||||
{
|
||||
out << v.size() << "\n";
|
||||
for (const Fp2_model<n, modulus>& t : v)
|
||||
{
|
||||
out << t << OUTPUT_NEWLINE;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp2_model<n, modulus> > &v)
|
||||
{
|
||||
v.clear();
|
||||
|
||||
size_t s;
|
||||
in >> s;
|
||||
|
||||
char b;
|
||||
in.read(&b, 1);
|
||||
|
||||
v.reserve(s);
|
||||
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
{
|
||||
Fp2_model<n, modulus> el;
|
||||
in >> el;
|
||||
v.emplace_back(el);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FP2_TCC_
|
||||
122
src/algebra/fields/fp3.hpp
Normal file
122
src/algebra/fields/fp3.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Declaration of arithmetic in the finite field F[p^3].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP3_HPP_
|
||||
#define FP3_HPP_
|
||||
#include "algebra/fields/fp.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp3_model;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &, const Fp3_model<n, modulus> &);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &, Fp3_model<n, modulus> &);
|
||||
|
||||
/**
|
||||
* Arithmetic in the field F[p^3].
|
||||
*
|
||||
* Let p := modulus. This interface provides arithmetic for the extension field
|
||||
* Fp3 = Fp[U]/(U^3-non_residue), where non_residue is in Fp.
|
||||
*
|
||||
* ASSUMPTION: p = 1 (mod 6)
|
||||
*/
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp3_model {
|
||||
public:
|
||||
typedef Fp_model<n, modulus> my_Fp;
|
||||
|
||||
static bigint<3*n> euler; // (modulus^3-1)/2
|
||||
static size_t s; // modulus^3 = 2^s * t + 1
|
||||
static bigint<3*n> t; // with t odd
|
||||
static bigint<3*n> t_minus_1_over_2; // (t-1)/2
|
||||
static my_Fp non_residue; // X^6-non_residue irreducible over Fp; used for constructing Fp3 = Fp[X] / (X^3 - non_residue)
|
||||
static Fp3_model<n, modulus> nqr; // a quadratic nonresidue in Fp3
|
||||
static Fp3_model<n, modulus> nqr_to_t; // nqr^t
|
||||
static my_Fp Frobenius_coeffs_c1[3]; // non_residue^((modulus^i-1)/3) for i=0,1,2
|
||||
static my_Fp Frobenius_coeffs_c2[3]; // non_residue^((2*modulus^i-2)/3) for i=0,1,2
|
||||
|
||||
my_Fp c0, c1, c2;
|
||||
Fp3_model() {};
|
||||
Fp3_model(const my_Fp& c0, const my_Fp& c1, const my_Fp& c2) : c0(c0), c1(c1), c2(c2) {};
|
||||
|
||||
void clear() { c0.clear(); c1.clear(); c2.clear(); }
|
||||
void print() const { printf("c0/c1/c2:\n"); c0.print(); c1.print(); c2.print(); }
|
||||
|
||||
static Fp3_model<n, modulus> zero();
|
||||
static Fp3_model<n, modulus> one();
|
||||
static Fp3_model<n, modulus> random_element();
|
||||
|
||||
bool is_zero() const { return c0.is_zero() && c1.is_zero() && c2.is_zero(); }
|
||||
bool operator==(const Fp3_model &other) const;
|
||||
bool operator!=(const Fp3_model &other) const;
|
||||
|
||||
Fp3_model operator+(const Fp3_model &other) const;
|
||||
Fp3_model operator-(const Fp3_model &other) const;
|
||||
Fp3_model operator*(const Fp3_model &other) const;
|
||||
Fp3_model operator-() const;
|
||||
Fp3_model squared() const;
|
||||
Fp3_model inverse() const;
|
||||
Fp3_model Frobenius_map(unsigned long power) const;
|
||||
Fp3_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate)
|
||||
|
||||
template<mp_size_t m>
|
||||
Fp3_model operator^(const bigint<m> &other) const;
|
||||
|
||||
static size_t size_in_bits() { return 3*my_Fp::size_in_bits(); }
|
||||
static bigint<n> base_field_char() { return modulus; }
|
||||
|
||||
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp3_model<n, modulus> &el);
|
||||
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp3_model<n, modulus> &el);
|
||||
};
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp3_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp3_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp3_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<3*n> Fp3_model<n, modulus>::euler;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
size_t Fp3_model<n, modulus>::s;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<3*n> Fp3_model<n, modulus>::t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bigint<3*n> Fp3_model<n, modulus>::t_minus_1_over_2;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp3_model<n, modulus>::non_residue;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n, modulus> Fp3_model<n, modulus>::nqr;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n, modulus> Fp3_model<n, modulus>::nqr_to_t;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp3_model<n, modulus>::Frobenius_coeffs_c1[3];
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp_model<n, modulus> Fp3_model<n, modulus>::Frobenius_coeffs_c2[3];
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/fp3.tcc"
|
||||
|
||||
#endif // FP3_HPP_
|
||||
259
src/algebra/fields/fp3.tcc
Normal file
259
src/algebra/fields/fp3.tcc
Normal file
@@ -0,0 +1,259 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[p^3].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP3_TCC_
|
||||
#define FP3_TCC_
|
||||
|
||||
#include "algebra/fields/field_utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::zero()
|
||||
{
|
||||
return Fp3_model<n, modulus>(my_Fp::zero(), my_Fp::zero(), my_Fp::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::one()
|
||||
{
|
||||
return Fp3_model<n, modulus>(my_Fp::one(), my_Fp::zero(), my_Fp::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::random_element()
|
||||
{
|
||||
Fp3_model<n, modulus> r;
|
||||
r.c0 = my_Fp::random_element();
|
||||
r.c1 = my_Fp::random_element();
|
||||
r.c2 = my_Fp::random_element();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp3_model<n,modulus>::operator==(const Fp3_model<n,modulus> &other) const
|
||||
{
|
||||
return (this->c0 == other.c0 && this->c1 == other.c1 && this->c2 == other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp3_model<n,modulus>::operator!=(const Fp3_model<n,modulus> &other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator+(const Fp3_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp3_model<n,modulus>(this->c0 + other.c0,
|
||||
this->c1 + other.c1,
|
||||
this->c2 + other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator-(const Fp3_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp3_model<n,modulus>(this->c0 - other.c0,
|
||||
this->c1 - other.c1,
|
||||
this->c2 - other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp3_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp3_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1,
|
||||
lhs*rhs.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator*(const Fp3_model<n,modulus> &other) const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (Karatsuba) */
|
||||
const my_Fp
|
||||
&A = other.c0, &B = other.c1, &C = other.c2,
|
||||
&a = this->c0, &b = this->c1, &c = this->c2;
|
||||
const my_Fp aA = a*A;
|
||||
const my_Fp bB = b*B;
|
||||
const my_Fp cC = c*C;
|
||||
|
||||
return Fp3_model<n,modulus>(aA + non_residue*((b+c)*(B+C)-bB-cC),
|
||||
(a+b)*(A+B)-aA-bB+non_residue*cC,
|
||||
(a+c)*(A+C)-aA+bB-cC);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator-() const
|
||||
{
|
||||
return Fp3_model<n,modulus>(-this->c0,
|
||||
-this->c1,
|
||||
-this->c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::squared() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (CH-SQR2) */
|
||||
const my_Fp
|
||||
&a = this->c0, &b = this->c1, &c = this->c2;
|
||||
const my_Fp s0 = a.squared();
|
||||
const my_Fp ab = a*b;
|
||||
const my_Fp s1 = ab + ab;
|
||||
const my_Fp s2 = (a - b + c).squared();
|
||||
const my_Fp bc = b*c;
|
||||
const my_Fp s3 = bc + bc;
|
||||
const my_Fp s4 = c.squared();
|
||||
|
||||
return Fp3_model<n,modulus>(s0 + non_residue * s3,
|
||||
s1 + non_residue * s4,
|
||||
s1 + s2 + s3 - s0 - s4);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::inverse() const
|
||||
{
|
||||
const my_Fp
|
||||
&a = this->c0, &b = this->c1, &c = this->c2;
|
||||
|
||||
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 17 */
|
||||
const my_Fp t0 = a.squared();
|
||||
const my_Fp t1 = b.squared();
|
||||
const my_Fp t2 = c.squared();
|
||||
const my_Fp t3 = a*b;
|
||||
const my_Fp t4 = a*c;
|
||||
const my_Fp t5 = b*c;
|
||||
const my_Fp c0 = t0 - non_residue * t5;
|
||||
const my_Fp c1 = non_residue * t2 - t3;
|
||||
const my_Fp c2 = t1 - t4; // typo in paper referenced above. should be "-" as per Scott, but is "*"
|
||||
const my_Fp t6 = (a * c0 + non_residue * (c * c1 + b * c2)).inverse();
|
||||
return Fp3_model<n,modulus>(t6 * c0, t6 * c1, t6 * c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::Frobenius_map(unsigned long power) const
|
||||
{
|
||||
return Fp3_model<n,modulus>(c0,
|
||||
Frobenius_coeffs_c1[power % 3] * c1,
|
||||
Frobenius_coeffs_c2[power % 3] * c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::sqrt() const
|
||||
{
|
||||
Fp3_model<n,modulus> one = Fp3_model<n,modulus>::one();
|
||||
|
||||
size_t v = Fp3_model<n,modulus>::s;
|
||||
Fp3_model<n,modulus> z = Fp3_model<n,modulus>::nqr_to_t;
|
||||
Fp3_model<n,modulus> w = (*this)^Fp3_model<n,modulus>::t_minus_1_over_2;
|
||||
Fp3_model<n,modulus> x = (*this) * w;
|
||||
Fp3_model<n,modulus> b = x * w; // b = (*this)^t
|
||||
|
||||
#if DEBUG
|
||||
// check if square with euler's criterion
|
||||
Fp3_model<n,modulus> check = b;
|
||||
for (size_t i = 0; i < v-1; ++i)
|
||||
{
|
||||
check = check.squared();
|
||||
}
|
||||
if (check != one)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// compute square root with Tonelli--Shanks
|
||||
// (does not terminate if not a square!)
|
||||
|
||||
while (b != one)
|
||||
{
|
||||
size_t m = 0;
|
||||
Fp3_model<n,modulus> b2m = b;
|
||||
while (b2m != one)
|
||||
{
|
||||
/* invariant: b2m = b^(2^m) after entering this loop */
|
||||
b2m = b2m.squared();
|
||||
m += 1;
|
||||
}
|
||||
|
||||
int j = v-m-1;
|
||||
w = z;
|
||||
while (j > 0)
|
||||
{
|
||||
w = w.squared();
|
||||
--j;
|
||||
} // w = z^2^(v-m-1)
|
||||
|
||||
z = w.squared();
|
||||
b = b * z;
|
||||
x = x * w;
|
||||
v = m;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator^(const bigint<m> &pow) const
|
||||
{
|
||||
return power<Fp3_model<n, modulus> >(*this, pow);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &out, const Fp3_model<n, modulus> &el)
|
||||
{
|
||||
out << el.c0 << OUTPUT_SEPARATOR << el.c1 << OUTPUT_SEPARATOR << el.c2;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &in, Fp3_model<n, modulus> &el)
|
||||
{
|
||||
in >> el.c0 >> el.c1 >> el.c2;
|
||||
return in;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp3_model<n, modulus> > &v)
|
||||
{
|
||||
out << v.size() << "\n";
|
||||
for (const Fp3_model<n, modulus>& t : v)
|
||||
{
|
||||
out << t << OUTPUT_NEWLINE;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp3_model<n, modulus> > &v)
|
||||
{
|
||||
v.clear();
|
||||
|
||||
size_t s;
|
||||
in >> s;
|
||||
|
||||
char b;
|
||||
in.read(&b, 1);
|
||||
|
||||
v.reserve(s);
|
||||
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
{
|
||||
Fp3_model<n, modulus> el;
|
||||
in >> el;
|
||||
v.emplace_back(el);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FP3_TCC_
|
||||
104
src/algebra/fields/fp6_3over2.hpp
Normal file
104
src/algebra/fields/fp6_3over2.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Declaration of arithmetic in the finite field F[(p^2)^3]
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP6_3OVER2_HPP_
|
||||
#define FP6_3OVER2_HPP_
|
||||
#include "algebra/fields/fp.hpp"
|
||||
#include "algebra/fields/fp2.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp6_3over2_model;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &, const Fp6_3over2_model<n, modulus> &);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &, Fp6_3over2_model<n, modulus> &);
|
||||
|
||||
/**
|
||||
* Arithmetic in the finite field F[(p^2)^3].
|
||||
*
|
||||
* Let p := modulus. This interface provides arithmetic for the extension field
|
||||
* Fp6 = Fp2[V]/(V^3-non_residue) where non_residue is in Fp.
|
||||
*
|
||||
* ASSUMPTION: p = 1 (mod 6)
|
||||
*/
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
class Fp6_3over2_model {
|
||||
public:
|
||||
typedef Fp_model<n, modulus> my_Fp;
|
||||
typedef Fp2_model<n, modulus> my_Fp2;
|
||||
|
||||
static my_Fp2 non_residue;
|
||||
static my_Fp2 Frobenius_coeffs_c1[6]; // non_residue^((modulus^i-1)/3) for i=0,1,2,3,4,5
|
||||
static my_Fp2 Frobenius_coeffs_c2[6]; // non_residue^((2*modulus^i-2)/3) for i=0,1,2,3,4,5
|
||||
|
||||
my_Fp2 c0, c1, c2;
|
||||
Fp6_3over2_model() {};
|
||||
Fp6_3over2_model(const my_Fp2& c0, const my_Fp2& c1, const my_Fp2& c2) : c0(c0), c1(c1), c2(c2) {};
|
||||
|
||||
void clear() { c0.clear(); c1.clear(); c2.clear(); }
|
||||
void print() const { printf("c0/c1/c2:\n"); c0.print(); c1.print(); c2.print(); }
|
||||
|
||||
static Fp6_3over2_model<n, modulus> zero();
|
||||
static Fp6_3over2_model<n, modulus> one();
|
||||
static Fp6_3over2_model<n, modulus> random_element();
|
||||
|
||||
bool is_zero() const { return c0.is_zero() && c1.is_zero() && c2.is_zero(); }
|
||||
bool operator==(const Fp6_3over2_model &other) const;
|
||||
bool operator!=(const Fp6_3over2_model &other) const;
|
||||
|
||||
Fp6_3over2_model operator+(const Fp6_3over2_model &other) const;
|
||||
Fp6_3over2_model operator-(const Fp6_3over2_model &other) const;
|
||||
Fp6_3over2_model operator*(const Fp6_3over2_model &other) const;
|
||||
Fp6_3over2_model operator-() const;
|
||||
Fp6_3over2_model squared() const;
|
||||
Fp6_3over2_model inverse() const;
|
||||
Fp6_3over2_model Frobenius_map(unsigned long power) const;
|
||||
|
||||
static my_Fp2 mul_by_non_residue(const my_Fp2 &elt);
|
||||
|
||||
template<mp_size_t m>
|
||||
Fp6_3over2_model operator^(const bigint<m> &other) const;
|
||||
|
||||
static bigint<n> base_field_char() { return modulus; }
|
||||
static size_t extension_degree() { return 6; }
|
||||
|
||||
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp6_3over2_model<n, modulus> &el);
|
||||
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp6_3over2_model<n, modulus> &el);
|
||||
};
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp6_3over2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp6_3over2_model<n, modulus> > &v);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs);
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::non_residue;
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::Frobenius_coeffs_c1[6];
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::Frobenius_coeffs_c2[6];
|
||||
|
||||
} // libsnark
|
||||
#include "algebra/fields/fp6_3over2.tcc"
|
||||
|
||||
#endif // FP6_3OVER2_HPP_
|
||||
216
src/algebra/fields/fp6_3over2.tcc
Normal file
216
src/algebra/fields/fp6_3over2.tcc
Normal file
@@ -0,0 +1,216 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Implementation of arithmetic in the finite field F[(p^2)^3].
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP6_3OVER2_TCC_
|
||||
#define FP6_3OVER2_TCC_
|
||||
#include "algebra/fields/field_utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp2_model<n, modulus> Fp6_3over2_model<n,modulus>::mul_by_non_residue(const Fp2_model<n, modulus> &elt)
|
||||
{
|
||||
return Fp2_model<n, modulus>(non_residue * elt);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::zero()
|
||||
{
|
||||
return Fp6_3over2_model<n, modulus>(my_Fp2::zero(), my_Fp2::zero(), my_Fp2::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::one()
|
||||
{
|
||||
return Fp6_3over2_model<n, modulus>(my_Fp2::one(), my_Fp2::zero(), my_Fp2::zero());
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::random_element()
|
||||
{
|
||||
Fp6_3over2_model<n, modulus> r;
|
||||
r.c0 = my_Fp2::random_element();
|
||||
r.c1 = my_Fp2::random_element();
|
||||
r.c2 = my_Fp2::random_element();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp6_3over2_model<n,modulus>::operator==(const Fp6_3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return (this->c0 == other.c0 && this->c1 == other.c1 && this->c2 == other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
bool Fp6_3over2_model<n,modulus>::operator!=(const Fp6_3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return !(operator==(other));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator+(const Fp6_3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(this->c0 + other.c0,
|
||||
this->c1 + other.c1,
|
||||
this->c2 + other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator-(const Fp6_3over2_model<n,modulus> &other) const
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(this->c0 - other.c0,
|
||||
this->c1 - other.c1,
|
||||
this->c2 - other.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1,
|
||||
lhs*rhs.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs)
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(lhs*rhs.c0,
|
||||
lhs*rhs.c1,
|
||||
lhs*rhs.c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator*(const Fp6_3over2_model<n,modulus> &other) const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (Karatsuba) */
|
||||
|
||||
const my_Fp2 &A = other.c0, &B = other.c1, &C = other.c2,
|
||||
&a = this->c0, &b = this->c1, &c = this->c2;
|
||||
const my_Fp2 aA = a*A;
|
||||
const my_Fp2 bB = b*B;
|
||||
const my_Fp2 cC = c*C;
|
||||
|
||||
return Fp6_3over2_model<n,modulus>(aA + Fp6_3over2_model<n,modulus>::mul_by_non_residue((b+c)*(B+C)-bB-cC),
|
||||
(a+b)*(A+B)-aA-bB+Fp6_3over2_model<n,modulus>::mul_by_non_residue(cC),
|
||||
(a+c)*(A+C)-aA+bB-cC);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator-() const
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(-this->c0,
|
||||
-this->c1,
|
||||
-this->c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::squared() const
|
||||
{
|
||||
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (CH-SQR2) */
|
||||
|
||||
const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2;
|
||||
const my_Fp2 s0 = a.squared();
|
||||
const my_Fp2 ab = a*b;
|
||||
const my_Fp2 s1 = ab + ab;
|
||||
const my_Fp2 s2 = (a - b + c).squared();
|
||||
const my_Fp2 bc = b*c;
|
||||
const my_Fp2 s3 = bc + bc;
|
||||
const my_Fp2 s4 = c.squared();
|
||||
|
||||
return Fp6_3over2_model<n,modulus>(s0 + Fp6_3over2_model<n,modulus>::mul_by_non_residue(s3),
|
||||
s1 + Fp6_3over2_model<n,modulus>::mul_by_non_residue(s4),
|
||||
s1 + s2 + s3 - s0 - s4);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::inverse() const
|
||||
{
|
||||
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 17 */
|
||||
|
||||
const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2;
|
||||
const my_Fp2 t0 = a.squared();
|
||||
const my_Fp2 t1 = b.squared();
|
||||
const my_Fp2 t2 = c.squared();
|
||||
const my_Fp2 t3 = a*b;
|
||||
const my_Fp2 t4 = a*c;
|
||||
const my_Fp2 t5 = b*c;
|
||||
const my_Fp2 c0 = t0 - Fp6_3over2_model<n,modulus>::mul_by_non_residue(t5);
|
||||
const my_Fp2 c1 = Fp6_3over2_model<n,modulus>::mul_by_non_residue(t2) - t3;
|
||||
const my_Fp2 c2 = t1 - t4; // typo in paper referenced above. should be "-" as per Scott, but is "*"
|
||||
const my_Fp2 t6 = (a * c0 + Fp6_3over2_model<n,modulus>::mul_by_non_residue((c * c1 + b * c2))).inverse();
|
||||
return Fp6_3over2_model<n,modulus>(t6 * c0, t6 * c1, t6 * c2);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::Frobenius_map(unsigned long power) const
|
||||
{
|
||||
return Fp6_3over2_model<n,modulus>(c0.Frobenius_map(power),
|
||||
Frobenius_coeffs_c1[power % 6] * c1.Frobenius_map(power),
|
||||
Frobenius_coeffs_c2[power % 6] * c2.Frobenius_map(power));
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
template<mp_size_t m>
|
||||
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator^(const bigint<m> &pow) const
|
||||
{
|
||||
return power<Fp6_3over2_model<n, modulus>, m>(*this, pow);
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream &out, const Fp6_3over2_model<n, modulus> &el)
|
||||
{
|
||||
out << el.c0 << OUTPUT_SEPARATOR << el.c1 << OUTPUT_SEPARATOR << el.c2;
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream &in, Fp6_3over2_model<n, modulus> &el)
|
||||
{
|
||||
in >> el.c0 >> el.c1 >> el.c2;
|
||||
return in;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Fp6_3over2_model<n, modulus> > &v)
|
||||
{
|
||||
out << v.size() << "\n";
|
||||
for (const Fp6_3over2_model<n, modulus>& t : v)
|
||||
{
|
||||
out << t << OUTPUT_NEWLINE;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
template<mp_size_t n, const bigint<n>& modulus>
|
||||
std::istream& operator>>(std::istream& in, std::vector<Fp6_3over2_model<n, modulus> > &v)
|
||||
{
|
||||
v.clear();
|
||||
|
||||
size_t s;
|
||||
in >> s;
|
||||
|
||||
char b;
|
||||
in.read(&b, 1);
|
||||
|
||||
v.reserve(s);
|
||||
|
||||
for (size_t i = 0; i < s; ++i)
|
||||
{
|
||||
Fp6_3over2_model<n, modulus> el;
|
||||
in >> el;
|
||||
v.emplace_back(el);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // FP6_3_OVER_2_TCC_
|
||||
389
src/algebra/fields/fp_aux.tcc
Normal file
389
src/algebra/fields/fp_aux.tcc
Normal file
@@ -0,0 +1,389 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
Assembly code snippets for F[p] finite field arithmetic, used by fp.tcc .
|
||||
Specific to x86-64, and used only if USE_ASM is defined.
|
||||
On other architectures or without USE_ASM, fp.tcc uses a portable
|
||||
C++ implementation instead.
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef FP_AUX_TCC_
|
||||
#define FP_AUX_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
/* addq is faster than adcq, even if preceded by clc */
|
||||
#define ADD_FIRSTADD \
|
||||
"movq (%[B]), %%rax \n\t" \
|
||||
"addq %%rax, (%[A]) \n\t"
|
||||
|
||||
#define ADD_NEXTADD(ofs) \
|
||||
"movq " STR(ofs) "(%[B]), %%rax \n\t" \
|
||||
"adcq %%rax, " STR(ofs) "(%[A]) \n\t"
|
||||
|
||||
#define ADD_CMP(ofs) \
|
||||
"movq " STR(ofs) "(%[mod]), %%rax \n\t" \
|
||||
"cmpq %%rax, " STR(ofs) "(%[A]) \n\t" \
|
||||
"jb done%= \n\t" \
|
||||
"ja subtract%= \n\t"
|
||||
|
||||
#define ADD_FIRSTSUB \
|
||||
"movq (%[mod]), %%rax \n\t" \
|
||||
"subq %%rax, (%[A]) \n\t"
|
||||
|
||||
#define ADD_FIRSTSUB \
|
||||
"movq (%[mod]), %%rax \n\t" \
|
||||
"subq %%rax, (%[A]) \n\t"
|
||||
|
||||
#define ADD_NEXTSUB(ofs) \
|
||||
"movq " STR(ofs) "(%[mod]), %%rax \n\t" \
|
||||
"sbbq %%rax, " STR(ofs) "(%[A]) \n\t"
|
||||
|
||||
#define SUB_FIRSTSUB \
|
||||
"movq (%[B]), %%rax\n\t" \
|
||||
"subq %%rax, (%[A])\n\t"
|
||||
|
||||
#define SUB_NEXTSUB(ofs) \
|
||||
"movq " STR(ofs) "(%[B]), %%rax\n\t" \
|
||||
"sbbq %%rax, " STR(ofs) "(%[A])\n\t"
|
||||
|
||||
#define SUB_FIRSTADD \
|
||||
"movq (%[mod]), %%rax\n\t" \
|
||||
"addq %%rax, (%[A])\n\t"
|
||||
|
||||
#define SUB_NEXTADD(ofs) \
|
||||
"movq " STR(ofs) "(%[mod]), %%rax\n\t" \
|
||||
"adcq %%rax, " STR(ofs) "(%[A])\n\t"
|
||||
|
||||
#define MONT_CMP(ofs) \
|
||||
"movq " STR(ofs) "(%[M]), %%rax \n\t" \
|
||||
"cmpq %%rax, " STR(ofs) "(%[tmp]) \n\t" \
|
||||
"jb done%= \n\t" \
|
||||
"ja subtract%= \n\t"
|
||||
|
||||
#define MONT_FIRSTSUB \
|
||||
"movq (%[M]), %%rax \n\t" \
|
||||
"subq %%rax, (%[tmp]) \n\t"
|
||||
|
||||
#define MONT_NEXTSUB(ofs) \
|
||||
"movq " STR(ofs) "(%[M]), %%rax \n\t" \
|
||||
"sbbq %%rax, " STR(ofs) "(%[tmp]) \n\t"
|
||||
|
||||
/*
|
||||
The x86-64 Montgomery multiplication here is similar
|
||||
to Algorithm 2 (CIOS method) in http://eprint.iacr.org/2012/140.pdf
|
||||
and the PowerPC pseudocode of gmp-ecm library (c) Paul Zimmermann and Alexander Kruppa
|
||||
(see comments on top of powerpc64/mulredc.m4).
|
||||
*/
|
||||
|
||||
#define MONT_PRECOMPUTE \
|
||||
"xorq %[cy], %[cy] \n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq 0(%[B]) \n\t" \
|
||||
"movq %%rax, %[T0] \n\t" \
|
||||
"movq %%rdx, %[T1] # T1:T0 <- A[0] * B[0] \n\t" \
|
||||
"mulq %[inv] \n\t" \
|
||||
"movq %%rax, %[u] # u <- T0 * inv \n\t" \
|
||||
"mulq 0(%[M]) \n\t" \
|
||||
"addq %[T0], %%rax \n\t" \
|
||||
"adcq %%rdx, %[T1] \n\t" \
|
||||
"adcq $0, %[cy] # cy:T1 <- (M[0]*u + T1 * b + T0) / b\n\t"
|
||||
|
||||
#define MONT_FIRSTITER(j) \
|
||||
"xorq %[T0], %[T0] \n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq " STR((j*8)) "(%[B]) \n\t" \
|
||||
"addq %[T1], %%rax \n\t" \
|
||||
"movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
|
||||
"adcq $0, %%rdx \n\t" \
|
||||
"movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[0] * Y[j] + T1\n\t" \
|
||||
"movq " STR((j*8)) "(%[M]), %%rax \n\t" \
|
||||
"mulq %[u] \n\t" \
|
||||
"addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
|
||||
"adcq %[cy], %%rdx \n\t" \
|
||||
"adcq $0, %[T0] \n\t" \
|
||||
"xorq %[cy], %[cy] \n\t" \
|
||||
"addq %%rdx, %[T1] \n\t" \
|
||||
"adcq %[T0], %[cy] # cy:T1:tmp[j-1] <---- (X[0] * Y[j] + T1) + (M[j] * u + cy * b) \n\t"
|
||||
|
||||
#define MONT_ITERFIRST(i) \
|
||||
"xorq %[cy], %[cy] \n\t" \
|
||||
"movq " STR((i*8)) "(%[A]), %%rax \n\t" \
|
||||
"mulq 0(%[B]) \n\t" \
|
||||
"addq 0(%[tmp]), %%rax \n\t" \
|
||||
"adcq 8(%[tmp]), %%rdx \n\t" \
|
||||
"adcq $0, %[cy] \n\t" \
|
||||
"movq %%rax, %[T0] \n\t" \
|
||||
"movq %%rdx, %[T1] # cy:T1:T0 <- A[i] * B[0] + tmp[1] * b + tmp[0]\n\t" \
|
||||
"mulq %[inv] \n\t" \
|
||||
"movq %%rax, %[u] # u <- T0 * inv\n\t" \
|
||||
"mulq 0(%[M]) \n\t" \
|
||||
"addq %[T0], %%rax \n\t" \
|
||||
"adcq %%rdx, %[T1] \n\t" \
|
||||
"adcq $0, %[cy] # cy:T1 <- (M[0]*u + cy * b * b + T1 * b + T0) / b\n\t"
|
||||
|
||||
#define MONT_ITERITER(i, j) \
|
||||
"xorq %[T0], %[T0] \n\t" \
|
||||
"movq " STR((i*8)) "(%[A]), %%rax \n\t" \
|
||||
"mulq " STR((j*8)) "(%[B]) \n\t" \
|
||||
"addq %[T1], %%rax \n\t" \
|
||||
"movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
|
||||
"adcq $0, %%rdx \n\t" \
|
||||
"movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[i] * Y[j] + T1 \n\t" \
|
||||
"movq " STR((j*8)) "(%[M]), %%rax \n\t" \
|
||||
"mulq %[u] \n\t" \
|
||||
"addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
|
||||
"adcq %[cy], %%rdx \n\t" \
|
||||
"adcq $0, %[T0] \n\t" \
|
||||
"xorq %[cy], %[cy] \n\t" \
|
||||
"addq %%rdx, %[T1] \n\t" \
|
||||
"adcq %[T0], %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + cy * b \n\t" \
|
||||
"addq " STR(((j+1)*8)) "(%[tmp]), %[T1] \n\t" \
|
||||
"adcq $0, %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + (tmp[j+1] + cy) * b \n\t"
|
||||
|
||||
#define MONT_FINALIZE(j) \
|
||||
"movq %[T1], " STR((j*8)) "(%[tmp]) \n\t" \
|
||||
"movq %[cy], " STR(((j+1)*8)) "(%[tmp]) \n\t"
|
||||
|
||||
/*
|
||||
Comba multiplication and squaring routines are based on the
|
||||
public-domain tomsfastmath library by Tom St Denis
|
||||
<http://www.libtom.org/>
|
||||
<https://github.com/libtom/tomsfastmath/blob/master/src/sqr/fp_sqr_comba.c
|
||||
<https://github.com/libtom/tomsfastmath/blob/master/src/mul/fp_mul_comba.c>
|
||||
|
||||
Compared to the above, we save 5-20% of cycles by using careful register
|
||||
renaming to implement Comba forward operation.
|
||||
*/
|
||||
|
||||
#define COMBA_3_BY_3_MUL(c0_, c1_, c2_, res_, A_, B_) \
|
||||
asm volatile ( \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq 0(%[B]) \n\t" \
|
||||
"movq %%rax, 0(%[res]) \n\t" \
|
||||
"movq %%rdx, %[c0] \n\t" \
|
||||
\
|
||||
"xorq %[c1], %[c1] \n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq 8(%[B]) \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
\
|
||||
"xorq %[c2], %[c2] \n\t" \
|
||||
"movq 8(%[A]), %%rax \n\t" \
|
||||
"mulq 0(%[B]) \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"movq %[c0], 8(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
"adcq $0, %[c2] \n\t" \
|
||||
\
|
||||
"// register renaming (c1, c2, c0)\n\t" \
|
||||
"xorq %[c0], %[c0] \n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq 16(%[B]) \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
\
|
||||
"movq 8(%[A]), %%rax \n\t" \
|
||||
"mulq 8(%[B]) \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
\
|
||||
"movq 16(%[A]), %%rax \n\t" \
|
||||
"mulq 0(%[B]) \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"movq %[c1], 16(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
\
|
||||
"// register renaming (c2, c0, c1)\n\t" \
|
||||
"xorq %[c1], %[c1] \n\t" \
|
||||
"movq 8(%[A]), %%rax \n\t" \
|
||||
"mulq 16(%[B]) \n\t" \
|
||||
"addq %%rax, %[c2] \n\t" \
|
||||
"adcq %%rdx, %[c0] \n\t" \
|
||||
"adcq $0, %[c1] \n\t" \
|
||||
\
|
||||
"movq 16(%[A]), %%rax \n\t" \
|
||||
"mulq 8(%[B]) \n\t" \
|
||||
"addq %%rax, %[c2] \n\t" \
|
||||
"movq %[c2], 24(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c0] \n\t" \
|
||||
"adcq $0, %[c1] \n\t" \
|
||||
\
|
||||
"// register renaming (c0, c1, c2)\n\t" \
|
||||
"xorq %[c2], %[c2] \n\t" \
|
||||
"movq 16(%[A]), %%rax \n\t" \
|
||||
"mulq 16(%[B]) \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"movq %[c0], 32(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
"movq %[c1], 40(%[res]) \n\t" \
|
||||
: [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \
|
||||
: [res] "r" (res_), [A] "r" (A_), [B] "r" (B_) \
|
||||
: "%rax", "%rdx", "cc", "memory")
|
||||
|
||||
#define COMBA_3_BY_3_SQR(c0_, c1_, c2_, res_, A_) \
|
||||
asm volatile ( \
|
||||
"xorq %[c1], %[c1] \n\t" \
|
||||
"xorq %[c2], %[c2] \n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq %%rax \n\t" \
|
||||
"movq %%rax, 0(%[res]) \n\t" \
|
||||
"movq %%rdx, %[c0] \n\t" \
|
||||
\
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"mulq 8(%[A]) \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"movq %[c0], 8(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
"adcq $0, %[c2] \n\t" \
|
||||
\
|
||||
"// register renaming (c1, c2, c0)\n\t" \
|
||||
"movq 0(%[A]), %%rax \n\t" \
|
||||
"xorq %[c0], %[c0] \n\t" \
|
||||
"mulq 16(%[A]) \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
\
|
||||
"movq 8(%[A]), %%rax \n\t" \
|
||||
"mulq %%rax \n\t" \
|
||||
"addq %%rax, %[c1] \n\t" \
|
||||
"movq %[c1], 16(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c2] \n\t" \
|
||||
"adcq $0, %[c0] \n\t" \
|
||||
\
|
||||
"// register renaming (c2, c0, c1)\n\t" \
|
||||
"movq 8(%[A]), %%rax \n\t" \
|
||||
"xorq %[c1], %[c1] \n\t" \
|
||||
"mulq 16(%[A]) \n\t" \
|
||||
"addq %%rax, %[c2] \n\t" \
|
||||
"adcq %%rdx, %[c0] \n\t" \
|
||||
"adcq $0, %[c1] \n\t" \
|
||||
"addq %%rax, %[c2] \n\t" \
|
||||
"movq %[c2], 24(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c0] \n\t" \
|
||||
"adcq $0, %[c1] \n\t" \
|
||||
\
|
||||
"// register renaming (c0, c1, c2)\n\t" \
|
||||
"movq 16(%[A]), %%rax \n\t" \
|
||||
"mulq %%rax \n\t" \
|
||||
"addq %%rax, %[c0] \n\t" \
|
||||
"movq %[c0], 32(%[res]) \n\t" \
|
||||
"adcq %%rdx, %[c1] \n\t" \
|
||||
"movq %[c1], 40(%[res]) \n\t" \
|
||||
\
|
||||
: [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \
|
||||
: [res] "r" (res_), [A] "r" (A_) \
|
||||
: "%rax", "%rdx", "cc", "memory")
|
||||
|
||||
/*
|
||||
The Montgomery reduction here is based on Algorithm 14.32 in
|
||||
Handbook of Applied Cryptography
|
||||
<http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
*/
|
||||
#define REDUCE_6_LIMB_PRODUCT(k_, tmp1_, tmp2_, tmp3_, inv_, res_, mod_) \
|
||||
__asm__ volatile \
|
||||
("///////////////////////////////////\n\t" \
|
||||
"movq 0(%[res]), %%rax \n\t" \
|
||||
"mulq %[modprime] \n\t" \
|
||||
"movq %%rax, %[k] \n\t" \
|
||||
\
|
||||
"movq (%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"movq %%rax, %[tmp1] \n\t" \
|
||||
"movq %%rdx, %[tmp2] \n\t" \
|
||||
\
|
||||
"xorq %[tmp3], %[tmp3] \n\t" \
|
||||
"movq 8(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp1], 0(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp2] \n\t" \
|
||||
"adcq %%rdx, %[tmp3] \n\t" \
|
||||
\
|
||||
"xorq %[tmp1], %[tmp1] \n\t" \
|
||||
"movq 16(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp2], 8(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp3] \n\t" \
|
||||
"adcq %%rdx, %[tmp1] \n\t" \
|
||||
\
|
||||
"addq %[tmp3], 16(%[res]) \n\t" \
|
||||
"adcq %[tmp1], 24(%[res]) \n\t" \
|
||||
"adcq $0, 32(%[res]) \n\t" \
|
||||
"adcq $0, 40(%[res]) \n\t" \
|
||||
\
|
||||
"///////////////////////////////////\n\t" \
|
||||
"movq 8(%[res]), %%rax \n\t" \
|
||||
"mulq %[modprime] \n\t" \
|
||||
"movq %%rax, %[k] \n\t" \
|
||||
\
|
||||
"movq (%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"movq %%rax, %[tmp1] \n\t" \
|
||||
"movq %%rdx, %[tmp2] \n\t" \
|
||||
\
|
||||
"xorq %[tmp3], %[tmp3] \n\t" \
|
||||
"movq 8(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp1], 8(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp2] \n\t" \
|
||||
"adcq %%rdx, %[tmp3] \n\t" \
|
||||
\
|
||||
"xorq %[tmp1], %[tmp1] \n\t" \
|
||||
"movq 16(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp2], 16(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp3] \n\t" \
|
||||
"adcq %%rdx, %[tmp1] \n\t" \
|
||||
\
|
||||
"addq %[tmp3], 24(%[res]) \n\t" \
|
||||
"adcq %[tmp1], 32(%[res]) \n\t" \
|
||||
"adcq $0, 40(%[res]) \n\t" \
|
||||
\
|
||||
"///////////////////////////////////\n\t" \
|
||||
"movq 16(%[res]), %%rax \n\t" \
|
||||
"mulq %[modprime] \n\t" \
|
||||
"movq %%rax, %[k] \n\t" \
|
||||
\
|
||||
"movq (%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"movq %%rax, %[tmp1] \n\t" \
|
||||
"movq %%rdx, %[tmp2] \n\t" \
|
||||
\
|
||||
"xorq %[tmp3], %[tmp3] \n\t" \
|
||||
"movq 8(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp1], 16(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp2] \n\t" \
|
||||
"adcq %%rdx, %[tmp3] \n\t" \
|
||||
\
|
||||
"xorq %[tmp1], %[tmp1] \n\t" \
|
||||
"movq 16(%[mod]), %%rax \n\t" \
|
||||
"mulq %[k] \n\t" \
|
||||
"addq %[tmp2], 24(%[res]) \n\t" \
|
||||
"adcq %%rax, %[tmp3] \n\t" \
|
||||
"adcq %%rdx, %[tmp1] \n\t" \
|
||||
\
|
||||
"addq %[tmp3], 32(%[res]) \n\t" \
|
||||
"adcq %[tmp1], 40(%[res]) \n\t" \
|
||||
: [k] "=&r" (k_), [tmp1] "=&r" (tmp1_), [tmp2] "=&r" (tmp2_), [tmp3] "=&r" (tmp3_) \
|
||||
: [modprime] "r" (inv_), [res] "r" (res_), [mod] "r" (mod_) \
|
||||
: "%rax", "%rdx", "cc", "memory")
|
||||
|
||||
} // libsnark
|
||||
#endif // FP_AUX_TCC_
|
||||
107
src/algebra/fields/tests/test_bigint.cpp
Normal file
107
src/algebra/fields/tests/test_bigint.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "algebra/fields/bigint.hpp"
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
void test_bigint()
|
||||
{
|
||||
static_assert(ULONG_MAX == 0xFFFFFFFFFFFFFFFFul, "unsigned long not 64-bit");
|
||||
static_assert(GMP_NUMB_BITS == 64, "GMP limb not 64-bit");
|
||||
|
||||
const char *b1_decimal = "76749407";
|
||||
const char *b2_decimal = "435020359732196472065729437602";
|
||||
const char *b3_decimal = "33387554642372758038536799358397002014";
|
||||
const char *b2_binary = "0000000000000000000000000000010101111101101000000110100001011010"
|
||||
"1101101010001001000001101000101000100110011001110001111110100010";
|
||||
|
||||
bigint<1> b0 = bigint<1>(0ul);
|
||||
bigint<1> b1 = bigint<1>(b1_decimal);
|
||||
bigint<2> b2 = bigint<2>(b2_decimal);
|
||||
|
||||
assert(b0.as_ulong() == 0ul);
|
||||
assert(b0.is_zero());
|
||||
assert(b1.as_ulong() == 76749407ul);
|
||||
assert(!(b1.is_zero()));
|
||||
assert(b2.as_ulong() == 15747124762497195938ul);
|
||||
assert(!(b2.is_zero()));
|
||||
assert(b0 != b1);
|
||||
assert(!(b0 == b1));
|
||||
|
||||
assert(b2.max_bits() == 128);
|
||||
assert(b2.num_bits() == 99);
|
||||
for (size_t i = 0; i < 128; i++) {
|
||||
assert(b2.test_bit(i) == (b2_binary[127-i] == '1'));
|
||||
}
|
||||
|
||||
bigint<3> b3 = b2 * b1;
|
||||
|
||||
assert(b3 == bigint<3>(b3_decimal));
|
||||
assert(!(b3.is_zero()));
|
||||
|
||||
bigint<3> b3a { b3 };
|
||||
assert(b3a == bigint<3>(b3_decimal));
|
||||
assert(b3a == b3);
|
||||
assert(!(b3a.is_zero()));
|
||||
|
||||
mpz_t m3;
|
||||
mpz_init(m3);
|
||||
b3.to_mpz(m3);
|
||||
bigint<3> b3b { m3 };
|
||||
assert(b3b == b3);
|
||||
|
||||
bigint<2> quotient;
|
||||
bigint<2> remainder;
|
||||
bigint<3>::div_qr(quotient, remainder, b3, b2);
|
||||
assert(quotient.num_bits() < GMP_NUMB_BITS);
|
||||
assert(quotient.as_ulong() == b1.as_ulong());
|
||||
bigint<1> b1inc = bigint<1>("76749408");
|
||||
bigint<1> b1a = quotient.shorten(b1inc, "test");
|
||||
assert(b1a == b1);
|
||||
assert(remainder.is_zero());
|
||||
remainder.limit(b2, "test");
|
||||
|
||||
try {
|
||||
(void)(quotient.shorten(b1, "test"));
|
||||
assert(false);
|
||||
} catch (std::domain_error) {}
|
||||
try {
|
||||
remainder.limit(remainder, "test");
|
||||
assert(false);
|
||||
} catch (std::domain_error) {}
|
||||
|
||||
bigint<1> br = bigint<1>("42");
|
||||
b3 += br;
|
||||
assert(b3 != b3a);
|
||||
assert(b3 > b3a);
|
||||
assert(!(b3a > b3));
|
||||
|
||||
bigint<3>::div_qr(quotient, remainder, b3, b2);
|
||||
assert(quotient.num_bits() < GMP_NUMB_BITS);
|
||||
assert(quotient.as_ulong() == b1.as_ulong());
|
||||
assert(remainder.num_bits() < GMP_NUMB_BITS);
|
||||
assert(remainder.as_ulong() == 42);
|
||||
|
||||
b3a.clear();
|
||||
assert(b3a.is_zero());
|
||||
assert(b3a.num_bits() == 0);
|
||||
assert(!(b3.is_zero()));
|
||||
|
||||
bigint<4> bx = bigint<4>().randomize();
|
||||
bigint<4> by = bigint<4>().randomize();
|
||||
assert(!(bx == by));
|
||||
|
||||
// TODO: test serialization
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_bigint();
|
||||
return 0;
|
||||
}
|
||||
|
||||
245
src/algebra/fields/tests/test_fields.cpp
Normal file
245
src/algebra/fields/tests/test_fields.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
#include "common/profiling.hpp"
|
||||
#include "algebra/curves/edwards/edwards_pp.hpp"
|
||||
#include "algebra/curves/mnt/mnt4/mnt4_pp.hpp"
|
||||
#include "algebra/curves/mnt/mnt6/mnt6_pp.hpp"
|
||||
#ifdef CURVE_BN128
|
||||
#include "algebra/curves/bn128/bn128_pp.hpp"
|
||||
#endif
|
||||
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
|
||||
#include "algebra/fields/fp6_3over2.hpp"
|
||||
#include "algebra/fields/fp12_2over3over2.hpp"
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
template<typename FieldT>
|
||||
void test_field()
|
||||
{
|
||||
bigint<1> rand1 = bigint<1>("76749407");
|
||||
bigint<1> rand2 = bigint<1>("44410867");
|
||||
bigint<1> randsum = bigint<1>("121160274");
|
||||
|
||||
FieldT zero = FieldT::zero();
|
||||
FieldT one = FieldT::one();
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT a_ser;
|
||||
a_ser = reserialize<FieldT>(a);
|
||||
assert(a_ser == a);
|
||||
|
||||
FieldT b = FieldT::random_element();
|
||||
FieldT c = FieldT::random_element();
|
||||
FieldT d = FieldT::random_element();
|
||||
|
||||
assert(a != zero);
|
||||
assert(a != one);
|
||||
|
||||
assert(a * a == a.squared());
|
||||
assert((a + b).squared() == a.squared() + a*b + b*a + b.squared());
|
||||
assert((a + b)*(c + d) == a*c + a*d + b*c + b*d);
|
||||
assert(a - b == a + (-b));
|
||||
assert(a - b == (-b) + a);
|
||||
|
||||
assert((a ^ rand1) * (a ^ rand2) == (a^randsum));
|
||||
|
||||
assert(a * a.inverse() == one);
|
||||
assert((a + b) * c.inverse() == a * c.inverse() + (b.inverse() * c).inverse());
|
||||
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_sqrt()
|
||||
{
|
||||
for (size_t i = 0; i < 100; ++i)
|
||||
{
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT asq = a.squared();
|
||||
assert(asq.sqrt() == a || asq.sqrt() == -a);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_two_squarings()
|
||||
{
|
||||
FieldT a = FieldT::random_element();
|
||||
assert(a.squared() == a * a);
|
||||
assert(a.squared() == a.squared_complex());
|
||||
assert(a.squared() == a.squared_karatsuba());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_Frobenius()
|
||||
{
|
||||
FieldT a = FieldT::random_element();
|
||||
assert(a.Frobenius_map(0) == a);
|
||||
FieldT a_q = a ^ FieldT::base_field_char();
|
||||
for (size_t power = 1; power < 10; ++power)
|
||||
{
|
||||
const FieldT a_qi = a.Frobenius_map(power);
|
||||
assert(a_qi == a_q);
|
||||
|
||||
a_q = a_q ^ FieldT::base_field_char();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_unitary_inverse()
|
||||
{
|
||||
assert(FieldT::extension_degree() % 2 == 0);
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT aqcubed_minus1 = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
|
||||
assert(aqcubed_minus1.inverse() == aqcubed_minus1.unitary_inverse());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_cyclotomic_squaring();
|
||||
|
||||
template<>
|
||||
void test_cyclotomic_squaring<Fqk<edwards_pp> >()
|
||||
{
|
||||
typedef Fqk<edwards_pp> FieldT;
|
||||
assert(FieldT::extension_degree() % 2 == 0);
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
|
||||
// beta = a^((q^(k/2)-1)*(q+1))
|
||||
FieldT beta = a_unitary.Frobenius_map(1) * a_unitary;
|
||||
assert(beta.cyclotomic_squared() == beta.squared());
|
||||
}
|
||||
|
||||
template<>
|
||||
void test_cyclotomic_squaring<Fqk<mnt4_pp> >()
|
||||
{
|
||||
typedef Fqk<mnt4_pp> FieldT;
|
||||
assert(FieldT::extension_degree() % 2 == 0);
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
|
||||
// beta = a^(q^(k/2)-1)
|
||||
FieldT beta = a_unitary;
|
||||
assert(beta.cyclotomic_squared() == beta.squared());
|
||||
}
|
||||
|
||||
template<>
|
||||
void test_cyclotomic_squaring<Fqk<mnt6_pp> >()
|
||||
{
|
||||
typedef Fqk<mnt6_pp> FieldT;
|
||||
assert(FieldT::extension_degree() % 2 == 0);
|
||||
FieldT a = FieldT::random_element();
|
||||
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
|
||||
// beta = a^((q^(k/2)-1)*(q+1))
|
||||
FieldT beta = a_unitary.Frobenius_map(1) * a_unitary;
|
||||
assert(beta.cyclotomic_squared() == beta.squared());
|
||||
}
|
||||
|
||||
template<typename ppT>
|
||||
void test_all_fields()
|
||||
{
|
||||
test_field<Fr<ppT> >();
|
||||
test_field<Fq<ppT> >();
|
||||
test_field<Fqe<ppT> >();
|
||||
test_field<Fqk<ppT> >();
|
||||
|
||||
test_sqrt<Fr<ppT> >();
|
||||
test_sqrt<Fq<ppT> >();
|
||||
test_sqrt<Fqe<ppT> >();
|
||||
|
||||
test_Frobenius<Fqe<ppT> >();
|
||||
test_Frobenius<Fqk<ppT> >();
|
||||
|
||||
test_unitary_inverse<Fqk<ppT> >();
|
||||
}
|
||||
|
||||
template<typename Fp4T>
|
||||
void test_Fp4_tom_cook()
|
||||
{
|
||||
typedef typename Fp4T::my_Fp FieldT;
|
||||
for (size_t i = 0; i < 100; ++i)
|
||||
{
|
||||
const Fp4T a = Fp4T::random_element();
|
||||
const Fp4T b = Fp4T::random_element();
|
||||
const Fp4T correct_res = a * b;
|
||||
|
||||
Fp4T res;
|
||||
|
||||
const FieldT
|
||||
&a0 = a.c0.c0,
|
||||
&a1 = a.c1.c0,
|
||||
&a2 = a.c0.c1,
|
||||
&a3 = a.c1.c1;
|
||||
|
||||
const FieldT
|
||||
&b0 = b.c0.c0,
|
||||
&b1 = b.c1.c0,
|
||||
&b2 = b.c0.c1,
|
||||
&b3 = b.c1.c1;
|
||||
|
||||
FieldT
|
||||
&c0 = res.c0.c0,
|
||||
&c1 = res.c1.c0,
|
||||
&c2 = res.c0.c1,
|
||||
&c3 = res.c1.c1;
|
||||
|
||||
const FieldT v0 = a0 * b0;
|
||||
const FieldT v1 = (a0 + a1 + a2 + a3) * (b0 + b1 + b2 + b3);
|
||||
const FieldT v2 = (a0 - a1 + a2 - a3) * (b0 - b1 + b2 - b3);
|
||||
const FieldT v3 = (a0 + FieldT(2)*a1 + FieldT(4)*a2 + FieldT(8)*a3) * (b0 + FieldT(2)*b1 + FieldT(4)*b2 + FieldT(8)*b3);
|
||||
const FieldT v4 = (a0 - FieldT(2)*a1 + FieldT(4)*a2 - FieldT(8)*a3) * (b0 - FieldT(2)*b1 + FieldT(4)*b2 - FieldT(8)*b3);
|
||||
const FieldT v5 = (a0 + FieldT(3)*a1 + FieldT(9)*a2 + FieldT(27)*a3) * (b0 + FieldT(3)*b1 + FieldT(9)*b2 + FieldT(27)*b3);
|
||||
const FieldT v6 = a3 * b3;
|
||||
|
||||
const FieldT beta = Fp4T::non_residue;
|
||||
|
||||
c0 = v0 + beta*(FieldT(4).inverse()*v0 - FieldT(6).inverse()*(v1 + v2) + FieldT(24).inverse() * (v3 + v4) - FieldT(5) * v6);
|
||||
c1 = - FieldT(3).inverse()*v0 + v1 - FieldT(2).inverse()*v2 - FieldT(4).inverse()*v3 + FieldT(20).inverse() * v4 + FieldT(30).inverse() * v5 - FieldT(12) * v6 + beta * ( - FieldT(12).inverse() * (v0 - v1) + FieldT(24).inverse()*(v2 - v3) - FieldT(120).inverse() * (v4 - v5) - FieldT(3) * v6);
|
||||
c2 = - (FieldT(5)*(FieldT(4).inverse()))* v0 + (FieldT(2)*(FieldT(3).inverse()))*(v1 + v2) - FieldT(24).inverse()*(v3 + v4) + FieldT(4)*v6 + beta*v6;
|
||||
c3 = FieldT(12).inverse() * (FieldT(5)*v0 - FieldT(7)*v1) - FieldT(24).inverse()*(v2 - FieldT(7)*v3 + v4 + v5) + FieldT(15)*v6;
|
||||
|
||||
assert(res == correct_res);
|
||||
|
||||
// {v0, v3, v4, v5}
|
||||
const FieldT u = (FieldT::one() - beta).inverse();
|
||||
assert(v0 == u * c0 + beta * u * c2 - beta * u * FieldT(2).inverse() * v1 - beta * u * FieldT(2).inverse() * v2 + beta * v6);
|
||||
assert(v3 == - FieldT(15) * u * c0 - FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 - FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v1 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v2
|
||||
- FieldT(3) * (-FieldT(16) + beta) * v6);
|
||||
assert(v4 == - FieldT(15) * u * c0 + FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 + FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v2 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v1
|
||||
- FieldT(3) * (-FieldT(16) + beta) * v6);
|
||||
assert(v5 == - FieldT(80) * u * c0 - FieldT(240) * u * c1 - FieldT(8) * (FieldT(9) + beta) * u * c2 - FieldT(24) * (FieldT(9) + beta) * u * c3 - FieldT(2) * (-FieldT(81) + beta) * u * v1 + (-FieldT(81) + beta) * u * v2
|
||||
- FieldT(8) * (-FieldT(81) + beta) * v6);
|
||||
|
||||
// c0 + beta c2 - (beta v1)/2 - (beta v2)/ 2 - (-1 + beta) beta v6,
|
||||
// -15 c0 - 30 c1 - 3 (4 + beta) c2 - 6 (4 + beta) c3 + (24 - (3 beta)/2) v1 + (-8 + beta/2) v2 + 3 (-16 + beta) (-1 + beta) v6,
|
||||
// -15 c0 + 30 c1 - 3 (4 + beta) c2 + 6 (4 + beta) c3 + (-8 + beta/2) v1 + (24 - (3 beta)/2) v2 + 3 (-16 + beta) (-1 + beta) v6,
|
||||
// -80 c0 - 240 c1 - 8 (9 + beta) c2 - 24 (9 + beta) c3 - 2 (-81 + beta) v1 + (-81 + beta) v2 + 8 (-81 + beta) (-1 + beta) v6
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
edwards_pp::init_public_params();
|
||||
test_all_fields<edwards_pp>();
|
||||
test_cyclotomic_squaring<Fqk<edwards_pp> >();
|
||||
|
||||
mnt4_pp::init_public_params();
|
||||
test_all_fields<mnt4_pp>();
|
||||
test_Fp4_tom_cook<mnt4_Fq4>();
|
||||
test_two_squarings<Fqe<mnt4_pp> >();
|
||||
test_cyclotomic_squaring<Fqk<mnt4_pp> >();
|
||||
|
||||
mnt6_pp::init_public_params();
|
||||
test_all_fields<mnt6_pp>();
|
||||
test_cyclotomic_squaring<Fqk<mnt6_pp> >();
|
||||
|
||||
alt_bn128_pp::init_public_params();
|
||||
test_field<alt_bn128_Fq6>();
|
||||
test_Frobenius<alt_bn128_Fq6>();
|
||||
test_all_fields<alt_bn128_pp>();
|
||||
|
||||
#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled
|
||||
bn128_pp::init_public_params();
|
||||
test_field<Fr<bn128_pp> >();
|
||||
test_field<Fq<bn128_pp> >();
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user