2#include "barretenberg/ecc/groups/element.hpp"
6namespace barretenberg::group_elements {
7template <
class Fq,
class Fr,
class T>
8constexpr element<Fq, Fr, T>::element(
const Fq& a,
const Fq& b,
const Fq& c) noexcept
14template <
class Fq,
class Fr,
class T>
15constexpr element<Fq, Fr, T>::element(
const element& other) noexcept
21template <
class Fq,
class Fr,
class T>
22constexpr element<Fq, Fr, T>::element(element&& other) noexcept
28template <
class Fq,
class Fr,
class T>
35template <
class Fq,
class Fr,
class T>
36constexpr element<Fq, Fr, T>& element<Fq, Fr, T>::operator=(
const element& other)
noexcept
47template <
class Fq,
class Fr,
class T>
48constexpr element<Fq, Fr, T>& element<Fq, Fr, T>::operator=(element&& other)
noexcept
58 if (is_point_at_infinity()) {
62 result.self_set_infinity();
65 Fq z_inv = z.invert();
66 Fq zz_inv = z_inv.
sqr();
67 Fq zzz_inv = zz_inv * z_inv;
69 if (is_point_at_infinity()) {
70 result.self_set_infinity();
75template <
class Fq,
class Fr,
class T>
constexpr void element<Fq, Fr, T>::self_dbl() noexcept
77 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
78 if (is_point_at_infinity()) {
82 if (x.is_msb_set_word()) {
114 if constexpr (T::has_a) {
115 T3 += (T::a * z.sqr().sqr());
144template <
class Fq,
class Fr,
class T>
constexpr element<Fq, Fr, T> element<Fq, Fr, T>::dbl() const noexcept
146 element result(*
this);
151template <
class Fq,
class Fr,
class T>
153 const uint64_t predicate)
noexcept
155 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
156 if (is_point_at_infinity()) {
162 const bool edge_case_trigger = x.is_msb_set() || other.x.is_msb_set();
163 if (edge_case_trigger) {
164 if (x.is_msb_set()) {
176 Fq T1 = other.x * T0;
183 T2.self_conditional_negate(predicate);
186 if (__builtin_expect(T1.is_zero(), 0)) {
244template <
class Fq,
class Fr,
class T>
247 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
248 if (is_point_at_infinity()) {
249 *
this = { other.x, other.y, Fq::one() };
253 const bool edge_case_trigger = x.is_msb_set() || other.x.is_msb_set();
254 if (edge_case_trigger) {
255 if (x.is_msb_set()) {
256 *
this = { other.x, other.y, Fq::one() };
266 Fq T1 = other.x * T0;
275 if (__builtin_expect(T1.is_zero(), 0)) {
333template <
class Fq,
class Fr,
class T>
336 element result(*
this);
337 return (result += other);
340template <
class Fq,
class Fr,
class T>
344 return operator+=(to_add);
347template <
class Fq,
class Fr,
class T>
350 element result(*
this);
351 return (result -= other);
354template <
class Fq,
class Fr,
class T>
355constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+=(
const element& other)
noexcept
357 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
358 bool p1_zero = is_point_at_infinity();
359 bool p2_zero = other.is_point_at_infinity();
360 if (__builtin_expect((p1_zero || p2_zero), 0)) {
361 if (p1_zero && !p2_zero) {
365 if (p2_zero && !p1_zero) {
372 bool p1_zero = x.is_msb_set();
373 bool p2_zero = other.x.is_msb_set();
374 if (__builtin_expect((p1_zero || p2_zero), 0)) {
375 if (p1_zero && !p2_zero) {
379 if (p2_zero && !p1_zero) {
387 Fq Z2Z2(other.z.sqr());
389 Fq U2(Z1Z1 * other.x);
392 Fq S1(Z2Z2 * other.z);
399 if (__builtin_expect(H.is_zero(), 0)) {
443template <
class Fq,
class Fr,
class T>
444constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+(
const element& other)
const noexcept
446 element result(*
this);
447 return (result += other);
450template <
class Fq,
class Fr,
class T>
451constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-=(
const element& other)
noexcept
453 const element to_add{ other.x, -other.y, other.z };
454 return operator+=(to_add);
457template <
class Fq,
class Fr,
class T>
458constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-(
const element& other)
const noexcept
460 element result(*
this);
461 return (result -= other);
464template <
class Fq,
class Fr,
class T>
constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-() const noexcept
469template <
class Fq,
class Fr,
class T>
470element<Fq, Fr, T> element<Fq, Fr, T>::operator*(
const Fr& exponent)
const noexcept
472 if constexpr (T::USE_ENDOMORPHISM) {
473 return mul_with_endomorphism(exponent);
475 return mul_without_endomorphism(exponent);
478template <
class Fq,
class Fr,
class T> element<Fq, Fr, T> element<Fq, Fr, T>::operator*=(
const Fr& exponent)
noexcept
480 *
this = operator*(exponent);
484template <
class Fq,
class Fr,
class T>
constexpr element<Fq, Fr, T> element<Fq, Fr, T>::normalize() const noexcept
487 return element(converted);
490template <
class Fq,
class Fr,
class T> element<Fq, Fr, T> element<Fq, Fr, T>::infinity()
492 element<Fq, Fr, T> e;
493 e.self_set_infinity();
497template <
class Fq,
class Fr,
class T>
constexpr element<Fq, Fr, T> element<Fq, Fr, T>::set_infinity() const noexcept
499 element result(*
this);
500 result.self_set_infinity();
504template <
class Fq,
class Fr,
class T>
constexpr void element<Fq, Fr, T>::self_set_infinity() noexcept
506 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
508 x.data[0] = Fq::modulus.data[0];
509 x.data[1] = Fq::modulus.data[1];
510 x.data[2] = Fq::modulus.data[2];
511 x.data[3] = Fq::modulus.data[3];
517template <
class Fq,
class Fr,
class T>
constexpr bool element<Fq, Fr, T>::is_point_at_infinity() const noexcept
519 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
521 return ((x.data[0] ^ Fq::modulus.data[0]) | (x.data[1] ^ Fq::modulus.data[1]) |
522 (x.data[2] ^ Fq::modulus.data[2]) | (x.data[3] ^ Fq::modulus.data[3])) == 0;
524 return (x.is_msb_set());
528template <
class Fq,
class Fr,
class T>
constexpr bool element<Fq, Fr, T>::on_curve() const noexcept
530 if (is_point_at_infinity()) {
539 Fq bz_6 = zzzz * zz * T::b;
540 if constexpr (T::has_a) {
541 bz_6 += (x * T::a) * zzzz;
543 Fq xxx = x.
sqr() * x + bz_6;
548template <
class Fq,
class Fr,
class T>
549constexpr bool element<Fq, Fr, T>::operator==(
const element& other)
const noexcept
552 if ((!on_curve()) || (!other.on_curve())) {
555 bool am_infinity = is_point_at_infinity();
556 bool is_infinity = other.is_point_at_infinity();
557 bool both_infinity = am_infinity && is_infinity;
559 if ((!both_infinity) && (am_infinity || is_infinity)) {
562 const Fq lhs_zz = z.sqr();
563 const Fq lhs_zzz = lhs_zz * z;
564 const Fq rhs_zz = other.z.
sqr();
565 const Fq rhs_zzz = rhs_zz * other.z;
567 const Fq lhs_x = x * rhs_zz;
568 const Fq lhs_y = y * rhs_zzz;
570 const Fq rhs_x = other.x * lhs_zz;
571 const Fq rhs_y = other.y * lhs_zzz;
572 return both_infinity || ((lhs_x == rhs_x) && (lhs_y == rhs_y));
575template <
class Fq,
class Fr,
class T>
578 if constexpr (T::can_hash_to_curve) {
579 element result = random_coordinates_on_curve(engine);
580 result.z = Fq::random_element(engine);
581 Fq zz = result.z.
sqr();
582 Fq zzz = zz * result.z;
587 Fr scalar = Fr::random_element(engine);
588 return (element{ T::one_x, T::one_y, Fq::one() } * scalar);
592template <
class Fq,
class Fr,
class T>
593element<Fq, Fr, T> element<Fq, Fr, T>::mul_without_endomorphism(
const Fr& exponent)
const noexcept
595 const uint256_t converted_scalar(exponent);
597 if (converted_scalar == 0) {
598 element result{ Fq::zero(), Fq::zero(), Fq::zero() };
599 result.self_set_infinity();
603 element work_element(*
this);
604 const uint64_t maximum_set_bit = converted_scalar.get_msb();
607 for (uint64_t i = maximum_set_bit - 1; i < maximum_set_bit; --i) {
608 work_element.self_dbl();
609 if (converted_scalar.get_bit(i)) {
610 work_element += *
this;
616template <
class Fq,
class Fr,
class T>
617element<Fq, Fr, T> element<Fq, Fr, T>::mul_with_endomorphism(
const Fr& exponent)
const noexcept
619 const Fr converted_scalar = exponent.from_montgomery_form();
621 if (converted_scalar.is_zero()) {
622 element result{ Fq::zero(), Fq::zero(), Fq::zero() };
623 result.self_set_infinity();
627 constexpr size_t lookup_size = 8;
628 constexpr size_t num_rounds = 32;
629 constexpr size_t num_wnaf_bits = 4;
630 std::array<element, lookup_size> lookup_table;
632 element d2 = element(*
this);
634 lookup_table[0] = element(*
this);
635 for (
size_t i = 1; i < lookup_size; ++i) {
636 lookup_table[i] = lookup_table[i - 1] + d2;
639 uint64_t wnaf_table[num_rounds * 2];
644 bool endo_skew =
false;
646 wnaf::fixed_wnaf(&endo_scalar.data[0], &wnaf_table[0], skew, 0, 2, num_wnaf_bits);
647 wnaf::fixed_wnaf(&endo_scalar.data[2], &wnaf_table[1], endo_skew, 0, 2, num_wnaf_bits);
649 element work_element{ T::one_x, T::one_y, Fq::one() };
650 work_element.self_set_infinity();
652 uint64_t wnaf_entry = 0;
655 Fq beta = Fq::cube_root_of_unity();
657 for (
size_t i = 0; i < num_rounds * 2; ++i) {
658 wnaf_entry = wnaf_table[i];
659 index = wnaf_entry & 0x0fffffffU;
660 sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
661 const bool is_odd = ((i & 1) == 1);
662 auto to_add = lookup_table[
static_cast<size_t>(index)];
663 to_add.y.self_conditional_negate(sign ^ is_odd);
667 work_element += to_add;
669 if (i != ((2 * num_rounds) - 1) && is_odd) {
670 for (
size_t j = 0; j < 4; ++j) {
671 work_element.self_dbl();
676 auto temporary = -lookup_table[0];
678 work_element += temporary;
681 temporary = { lookup_table[0].x * beta, lookup_table[0].y, lookup_table[0].z };
684 work_element += temporary;
690template <
class Fq,
class Fr,
class T>
691std::vector<affine_element<Fq, Fr, T>> element<Fq, Fr, T>::batch_mul_with_endomorphism(
695 const size_t num_points = points.size();
696 std::vector<Fq> scratch_space(num_points);
701 Fq batch_inversion_accumulator = Fq::one();
703 for (
size_t i = 0; i < num_points; i += 1) {
704 scratch_space[i] = lhs[i].x + rhs[i].x;
705 rhs[i].x -= lhs[i].x;
706 rhs[i].y -= lhs[i].y;
707 rhs[i].y *= batch_inversion_accumulator;
708 batch_inversion_accumulator *= (rhs[i].x);
710 batch_inversion_accumulator = batch_inversion_accumulator.invert();
712 for (
size_t i = (num_points)-1; i < num_points; i -= 1) {
713 rhs[i].y *= batch_inversion_accumulator;
714 batch_inversion_accumulator *= rhs[i].x;
715 rhs[i].x = rhs[i].y.
sqr();
716 rhs[i].x = rhs[i].x - (scratch_space[i]);
718 scratch_space[i] = lhs[i].x - rhs[i].x;
719 scratch_space[i] *= rhs[i].y;
720 rhs[i].y = scratch_space[i] - lhs[i].y;
725 const auto batch_affine_double = [num_points, &scratch_space](
affine_element* lhs) {
726 Fq batch_inversion_accumulator = Fq::one();
728 for (
size_t i = 0; i < num_points; i += 1) {
730 scratch_space[i] = lhs[i].x.sqr();
731 scratch_space[i] = scratch_space[i] + scratch_space[i] + scratch_space[i];
733 scratch_space[i] *= batch_inversion_accumulator;
735 batch_inversion_accumulator *= (lhs[i].y + lhs[i].y);
737 batch_inversion_accumulator = batch_inversion_accumulator.invert();
740 for (
size_t i = (num_points)-1; i < num_points; i -= 1) {
742 scratch_space[i] *= batch_inversion_accumulator;
743 batch_inversion_accumulator *= (lhs[i].y + lhs[i].y);
746 lhs[i].x = scratch_space[i].
sqr() - (lhs[i].x + lhs[i].x);
747 lhs[i].y = scratch_space[i] * (temp - lhs[i].x) - lhs[i].y;
752 const Fr converted_scalar = exponent.from_montgomery_form();
754 if (converted_scalar.is_zero()) {
756 result.self_set_infinity();
757 std::vector<affine_element> results;
758 for (
size_t i = 0; i < num_points; ++i) {
759 results.emplace_back(result);
764 constexpr size_t lookup_size = 8;
765 constexpr size_t num_rounds = 32;
766 constexpr size_t num_wnaf_bits = 4;
767 std::array<std::vector<affine_element>, lookup_size> lookup_table;
768 for (
auto& table : lookup_table) {
769 table.resize(num_points);
771 std::vector<affine_element> temp_point_vector(num_points);
772 for (
size_t i = 0; i < num_points; ++i) {
773 temp_point_vector[i] = points[i];
774 lookup_table[0][i] = points[i];
776 batch_affine_double(&temp_point_vector[0]);
777 for (
size_t j = 1; j < lookup_size; ++j) {
779 for (
size_t i = 0; i < num_points; ++i) {
780 lookup_table[j][i] = lookup_table[j - 1][i];
782 batch_affine_add(&temp_point_vector[0], &lookup_table[j][0]);
785 uint64_t wnaf_table[num_rounds * 2];
790 bool endo_skew =
false;
792 wnaf::fixed_wnaf(&endo_scalar.data[0], &wnaf_table[0], skew, 0, 2, num_wnaf_bits);
793 wnaf::fixed_wnaf(&endo_scalar.data[2], &wnaf_table[1], endo_skew, 0, 2, num_wnaf_bits);
795 std::vector<affine_element> work_elements(num_points);
797 uint64_t wnaf_entry = 0;
800 Fq beta = Fq::cube_root_of_unity();
802 for (
size_t i = 0; i < 2; ++i) {
803 for (
size_t j = 0; j < num_points; ++j) {
804 wnaf_entry = wnaf_table[i];
805 index = wnaf_entry & 0x0fffffffU;
806 sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
807 const bool is_odd = ((i & 1) == 1);
808 auto to_add = lookup_table[
static_cast<size_t>(index)][j];
809 to_add.y.self_conditional_negate(sign ^ is_odd);
814 work_elements[j] = to_add;
816 temp_point_vector[j] = to_add;
820 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
822 for (
size_t i = 2; i < num_rounds * 2; ++i) {
823 wnaf_entry = wnaf_table[i];
824 index = wnaf_entry & 0x0fffffffU;
825 sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
826 const bool is_odd = ((i & 1) == 1);
828 for (
size_t k = 0; k < 4; ++k) {
829 batch_affine_double(&work_elements[0]);
832 for (
size_t j = 0; j < num_points; ++j) {
833 auto to_add = lookup_table[
static_cast<size_t>(index)][j];
834 to_add.y.self_conditional_negate(sign ^ is_odd);
838 temp_point_vector[j] = to_add;
840 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
844 for (
size_t j = 0; j < num_points; ++j) {
845 temp_point_vector[j] = -lookup_table[0][j];
847 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
851 for (
size_t j = 0; j < num_points; ++j) {
852 temp_point_vector[j] = lookup_table[0][j];
853 temp_point_vector[j].x *= beta;
855 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
858 return work_elements;
861template <
typename Fq,
typename Fr,
typename T>
864 const uint64_t predicate)
noexcept
866 out = { in.x, predicate ? -in.y : in.y };
869template <
typename Fq,
typename Fr,
typename T>
872 std::vector<Fq> temporaries;
873 temporaries.reserve(num_elements * 2);
874 Fq accumulator = Fq::one();
878 for (
size_t i = 0; i < num_elements; ++i) {
879 temporaries.emplace_back(accumulator);
880 if (!elements[i].is_point_at_infinity()) {
881 accumulator *= elements[i].z;
886 accumulator = accumulator.invert();
910 for (
size_t i = num_elements - 1; i < num_elements; --i) {
911 if (!elements[i].is_point_at_infinity()) {
912 Fq z_inv = accumulator * temporaries[i];
913 Fq zz_inv = z_inv.
sqr();
914 elements[i].x *= zz_inv;
915 elements[i].y *= (zz_inv * z_inv);
916 accumulator *= elements[i].z;
918 elements[i].z = Fq::one();
922template <
typename Fq,
typename Fr,
typename T>
926 bool found_one =
false;
931 x = Fq::random_element(engine);
932 yy = x.
sqr() * x + T::b;
933 if constexpr (T::has_a) {
936 auto [found_root, y1] = yy.
sqrt();
938 found_one = found_root;
940 return { x, y, Fq::one() };
Definition: affine_element.hpp:11
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition: element.hpp:27
static void batch_normalize(element *elements, size_t num_elements) noexcept
Definition: element_impl.hpp:870
Definition: engine.hpp:10
bool_t< Builder > is_zero() const
Definition: field.cpp:588
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
Definition: field_impl.hpp:507
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Definition: field_declarations.hpp:320