17template <
class Fq,
class Fr,
class T>
24template <
class Fq,
class Fr,
class T>
31template <
class Fq,
class Fr,
class T>
38template <
class Fq,
class Fr,
class T>
45template <
class Fq,
class Fr,
class T>
57template <
class Fq,
class Fr,
class T>
68 if (is_point_at_infinity()) {
76 Fq zz_inv = z_inv.
sqr();
77 Fq zzz_inv = zz_inv * z_inv;
85 if (is_point_at_infinity()) {
89 if (x.is_msb_set_word()) {
121 if constexpr (T::has_a) {
122 T3 += (T::a * z.sqr().sqr());
158template <
class Fq,
class Fr,
class T>
160 const uint64_t predicate)
noexcept
163 if (is_point_at_infinity()) {
169 const bool edge_case_trigger = x.is_msb_set() || other.x.is_msb_set();
170 if (edge_case_trigger) {
171 if (x.is_msb_set()) {
183 Fq T1 = other.x * T0;
193 if (__builtin_expect(T1.
is_zero(), 0)) {
251template <
class Fq,
class Fr,
class T>
255 if (is_point_at_infinity()) {
256 *
this = { other.
x, other.y,
Fq::one() };
260 const bool edge_case_trigger = x.
is_msb_set() || other.x.is_msb_set();
261 if (edge_case_trigger) {
262 if (x.is_msb_set()) {
263 *
this = { other.x, other.y,
Fq::one() };
273 Fq T1 = other.x * T0;
282 if (__builtin_expect(T1.
is_zero(), 0)) {
340template <
class Fq,
class Fr,
class T>
344 return (result += other);
347template <
class Fq,
class Fr,
class T>
351 return operator+=(to_add);
354template <
class Fq,
class Fr,
class T>
358 return (result -= other);
361template <
class Fq,
class Fr,
class T>
365 bool p1_zero = is_point_at_infinity();
366 bool p2_zero = other.is_point_at_infinity();
367 if (__builtin_expect((p1_zero || p2_zero), 0)) {
368 if (p1_zero && !p2_zero) {
372 if (p2_zero && !p1_zero) {
379 bool p1_zero = x.is_msb_set();
380 bool p2_zero = other.x.is_msb_set();
381 if (__builtin_expect((p1_zero || p2_zero), 0)) {
382 if (p1_zero && !p2_zero) {
386 if (p2_zero && !p1_zero) {
394 Fq Z2Z2(other.z.sqr());
396 Fq U2(Z1Z1 * other.x);
399 Fq S1(Z2Z2 * other.z);
406 if (__builtin_expect(H.
is_zero(), 0)) {
450template <
class Fq,
class Fr,
class T>
454 return (result += other);
457template <
class Fq,
class Fr,
class T>
460 const element to_add{ other.
x, -other.y, other.z };
461 return operator+=(to_add);
464template <
class Fq,
class Fr,
class T>
468 return (result -= other);
476template <
class Fq,
class Fr,
class T>
479 if constexpr (T::USE_ENDOMORPHISM) {
480 return mul_with_endomorphism(exponent);
482 return mul_without_endomorphism(exponent);
534 return (x.is_msb_set());
540 if (is_point_at_infinity()) {
549 Fq bz_6 = zzzz * zz * T::b;
550 if constexpr (T::has_a) {
551 bz_6 += (x * T::a) * zzzz;
553 Fq xxx = x.
sqr() * x + bz_6;
558template <
class Fq,
class Fr,
class T>
562 if ((!on_curve()) || (!other.on_curve())) {
565 bool am_infinity = is_point_at_infinity();
566 bool is_infinity = other.is_point_at_infinity();
567 bool both_infinity = am_infinity && is_infinity;
569 if ((!both_infinity) && (am_infinity || is_infinity)) {
572 const Fq lhs_zz = z.
sqr();
573 const Fq lhs_zzz = lhs_zz * z;
574 const Fq rhs_zz = other.z.
sqr();
575 const Fq rhs_zzz = rhs_zz * other.z;
577 const Fq lhs_x = x * rhs_zz;
578 const Fq lhs_y = y * rhs_zzz;
580 const Fq rhs_x = other.x * lhs_zz;
581 const Fq rhs_y = other.y * lhs_zzz;
582 return both_infinity || ((lhs_x == rhs_x) && (lhs_y == rhs_y));
585template <
class Fq,
class Fr,
class T>
588 if constexpr (T::can_hash_to_curve) {
592 Fq zzz = zz * result.
z;
602template <
class Fq,
class Fr,
class T>
605 const uint256_t converted_scalar(scalar);
607 if (converted_scalar == 0) {
612 const uint64_t maximum_set_bit = converted_scalar.
get_msb();
615 for (uint64_t i = maximum_set_bit - 1; i < maximum_set_bit; --i) {
617 if (converted_scalar.
get_bit(i)) {
618 accumulator += *
this;
640 std::array<uint64_t, NUM_ROUNDS * 2>
table;
657template <
class Fq,
class Fr,
class T>
661 if (is_point_at_infinity()) {
664 constexpr size_t NUM_ROUNDS = 32;
667 if (converted_scalar.
is_zero()) {
670 static constexpr size_t LOOKUP_SIZE = 8;
674 lookup_table[0] =
element(*
this);
675 for (
size_t i = 1; i < LOOKUP_SIZE; ++i) {
676 lookup_table[i] = lookup_table[i - 1] + d2;
682 accumulator.self_set_infinity();
685 for (
size_t i = 0; i < NUM_ROUNDS * 2; ++i) {
686 uint64_t wnaf_entry = wnaf.table[i];
687 uint64_t index = wnaf_entry & 0x0fffffffU;
688 bool sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
689 const bool is_odd = ((i & 1) == 1);
690 auto to_add = lookup_table[
static_cast<size_t>(index)];
691 to_add.y.self_conditional_negate(sign ^ is_odd);
695 accumulator += to_add;
697 if (i != ((2 * NUM_ROUNDS) - 1) && is_odd) {
698 for (
size_t j = 0; j < 4; ++j) {
699 accumulator.self_dbl();
705 accumulator += -lookup_table[0];
707 if (wnaf.endo_skew) {
708 accumulator +=
element{ lookup_table[0].
x * beta, lookup_table[0].y, lookup_table[0].z };
721template <
class Fq,
class Fr,
class T>
727 const size_t num_points = first_group.size();
743 const auto batch_affine_add_chunked =
745 Fq batch_inversion_accumulator =
Fq::one();
747 for (
size_t i = 0; i < point_count; i += 1) {
748 personal_scratch_space[i] = lhs[i].
x + rhs[i].x;
749 rhs[i].x -= lhs[i].
x;
750 rhs[i].y -= lhs[i].
y;
751 rhs[i].y *= batch_inversion_accumulator;
752 batch_inversion_accumulator *= (rhs[i].x);
754 batch_inversion_accumulator = batch_inversion_accumulator.
invert();
756 for (
size_t i = (point_count)-1; i < point_count; i -= 1) {
757 rhs[i].y *= batch_inversion_accumulator;
758 batch_inversion_accumulator *= rhs[i].x;
759 rhs[i].x = rhs[i].y.
sqr();
760 rhs[i].x = rhs[i].x - (personal_scratch_space[i]);
762 personal_scratch_space[i] = lhs[i].
x - rhs[i].x;
763 personal_scratch_space[i] *= rhs[i].y;
764 rhs[i].y = personal_scratch_space[i] - lhs[i].
y;
775 [&](
size_t start,
size_t end,
BB_UNUSED size_t chunk_index) {
776 batch_affine_add_chunked(lhs + start, rhs + start, end - start, &scratch_space[0] + start);
780 batch_affine_add_internal(&second_group[0], &results[0]);
793template <
class Fq,
class Fr,
class T>
799 const size_t num_points = points.size();
811 const auto batch_affine_add_chunked =
813 Fq batch_inversion_accumulator =
Fq::one();
815 for (
size_t i = 0; i < point_count; i += 1) {
816 personal_scratch_space[i] = lhs[i].
x + rhs[i].x;
817 rhs[i].x -= lhs[i].
x;
818 rhs[i].y -= lhs[i].
y;
819 rhs[i].y *= batch_inversion_accumulator;
820 batch_inversion_accumulator *= (rhs[i].x);
822 batch_inversion_accumulator = batch_inversion_accumulator.
invert();
824 for (
size_t i = (point_count)-1; i < point_count; i -= 1) {
825 rhs[i].y *= batch_inversion_accumulator;
826 batch_inversion_accumulator *= rhs[i].x;
827 rhs[i].x = rhs[i].y.
sqr();
828 rhs[i].x = rhs[i].x - (personal_scratch_space[i]);
830 personal_scratch_space[i] = lhs[i].
x - rhs[i].x;
831 personal_scratch_space[i] *= rhs[i].y;
832 rhs[i].y = personal_scratch_space[i] - lhs[i].
y;
840 const auto batch_affine_add_internal =
844 [&](
size_t start,
size_t end,
BB_UNUSED size_t chunk_index) {
845 batch_affine_add_chunked(lhs + start, rhs + start, end - start, &scratch_space[0] + start);
854 const auto batch_affine_double_chunked =
855 [](
affine_element* lhs,
const size_t point_count,
Fq* personal_scratch_space) {
856 Fq batch_inversion_accumulator =
Fq::one();
858 for (
size_t i = 0; i < point_count; i += 1) {
860 personal_scratch_space[i] = lhs[i].
x.sqr();
861 personal_scratch_space[i] =
862 personal_scratch_space[i] + personal_scratch_space[i] + personal_scratch_space[i];
864 personal_scratch_space[i] *= batch_inversion_accumulator;
866 batch_inversion_accumulator *= (lhs[i].
y + lhs[i].
y);
868 batch_inversion_accumulator = batch_inversion_accumulator.
invert();
871 for (
size_t i = (point_count)-1; i < point_count; i -= 1) {
873 personal_scratch_space[i] *= batch_inversion_accumulator;
874 batch_inversion_accumulator *= (lhs[i].
y + lhs[i].
y);
877 lhs[i].
x = personal_scratch_space[i].sqr() - (lhs[i].
x + lhs[i].
x);
878 lhs[i].
y = personal_scratch_space[i] * (temp - lhs[i].
x) - lhs[i].y;
885 const auto batch_affine_double = [num_points, &scratch_space, &batch_affine_double_chunked](
affine_element* lhs) {
888 [&](
size_t start,
size_t end,
BB_UNUSED size_t chunk_index) {
889 batch_affine_double_chunked(lhs + start, end - start, &scratch_space[0] + start);
909 if (converted_scalar.
is_zero()) {
917 constexpr size_t LOOKUP_SIZE = 8;
918 constexpr size_t NUM_ROUNDS = 32;
920 for (
auto& table : lookup_table) {
921 table.resize(num_points);
930 temp_point_vector[i] = points[i].is_point_at_infinity() ?
affine_element::one() : points[i];
936 batch_affine_double(&temp_point_vector[0]);
937 for (
size_t j = 1; j < LOOKUP_SIZE; ++j) {
940 [&](
size_t i) { lookup_table[j][i] = lookup_table[j - 1][i]; },
942 batch_affine_add_internal(&temp_point_vector[0], &lookup_table[j][0]);
951 uint64_t wnaf_entry = 0;
955 for (
size_t j = 0; j < 2; ++j) {
956 wnaf_entry = wnaf.table[j];
957 index = wnaf_entry & 0x0fffffffU;
958 sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
959 const bool is_odd = ((j & 1) == 1);
963 auto to_add = lookup_table[
static_cast<size_t>(index)][i];
964 to_add.y.self_conditional_negate(sign ^ is_odd);
969 work_elements[i] = to_add;
971 temp_point_vector[i] = to_add;
978 batch_affine_add_internal(&temp_point_vector[0], &work_elements[0]);
980 for (
size_t j = 2; j < NUM_ROUNDS * 2; ++j) {
981 wnaf_entry = wnaf.table[j];
982 index = wnaf_entry & 0x0fffffffU;
983 sign =
static_cast<bool>((wnaf_entry >> 31) & 1);
984 const bool is_odd = ((j & 1) == 1);
986 for (
size_t k = 0; k < 4; ++k) {
987 batch_affine_double(&work_elements[0]);
993 auto to_add = lookup_table[
static_cast<size_t>(index)][i];
994 to_add.y.self_conditional_negate(sign ^ is_odd);
998 temp_point_vector[i] = to_add;
1002 batch_affine_add_internal(&temp_point_vector[0], &work_elements[0]);
1009 [&](
size_t i) { temp_point_vector[i] = -lookup_table[0][i]; },
1011 batch_affine_add_internal(&temp_point_vector[0], &work_elements[0]);
1014 if (wnaf.endo_skew) {
1018 temp_point_vector[i] = lookup_table[0][i];
1019 temp_point_vector[i].x *= beta;
1022 batch_affine_add_internal(&temp_point_vector[0], &work_elements[0]);
1028 work_elements[i] = points[i].is_point_at_infinity() ? work_elements[i].set_infinity() : work_elements[i];
1032 return work_elements;
1035template <
typename Fq,
typename Fr,
typename T>
1038 const uint64_t predicate)
noexcept
1040 out = { in.x, predicate ? -in.y : in.y };
1043template <
typename Fq,
typename Fr,
typename T>
1047 temporaries.reserve(num_elements * 2);
1052 for (
size_t i = 0; i < num_elements; ++i) {
1053 temporaries.emplace_back(accumulator);
1054 if (!elements[i].is_point_at_infinity()) {
1055 accumulator *= elements[i].z;
1060 accumulator = accumulator.
invert();
1084 for (
size_t i = num_elements - 1; i < num_elements; --i) {
1085 if (!elements[i].is_point_at_infinity()) {
1086 Fq z_inv = accumulator * temporaries[i];
1087 Fq zz_inv = z_inv.
sqr();
1088 elements[i].x *= zz_inv;
1089 elements[i].y *= (zz_inv * z_inv);
1090 accumulator *= elements[i].z;
1096template <
typename Fq,
typename Fr,
typename T>
1100 bool found_one =
false;
1104 while (!found_one) {
1106 yy = x.
sqr() * x + T::b;
1107 if constexpr (T::has_a) {
1110 auto [found_root, y1] = yy.
sqrt();
1112 found_one = found_root;
#define BB_ASSERT_EQ(actual, expected,...)
constexpr void self_set_infinity() noexcept
static constexpr affine_element one() noexcept
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
element operator*=(const Fr &exponent) noexcept
BB_INLINE constexpr element set_infinity() const noexcept
element mul_with_endomorphism(const Fr &scalar) const noexcept
static element infinity()
static std::vector< affine_element< Fq, Fr, Params > > batch_mul_with_endomorphism(const std::span< const affine_element< Fq, Fr, Params > > &points, const Fr &scalar) noexcept
Multiply each point by the same scalar.
constexpr element operator-=(const element &other) noexcept
constexpr element operator-() const noexcept
friend constexpr element operator+(const affine_element< Fq, Fr, Params > &left, const element &right) noexcept
constexpr element dbl() const noexcept
constexpr element normalize() const noexcept
constexpr void self_dbl() noexcept
static element random_element(numeric::RNG *engine=nullptr) noexcept
static void batch_normalize(element *elements, size_t num_elements) noexcept
constexpr element operator+=(const element &other) noexcept
static void batch_affine_add(const std::span< affine_element< Fq, Fr, Params > > &first_group, const std::span< affine_element< Fq, Fr, Params > > &second_group, const std::span< affine_element< Fq, Fr, Params > > &results) noexcept
Pairwise affine add points in first and second group.
BB_INLINE constexpr bool on_curve() const noexcept
BB_INLINE constexpr bool operator==(const element &other) const noexcept
element operator*(const Fr &exponent) const noexcept
constexpr void self_mixed_add_or_sub(const affine_element< Fq, Fr, Params > &other, uint64_t predicate) noexcept
element() noexcept=default
static void conditional_negate_affine(const affine_element< Fq, Fr, Params > &in, affine_element< Fq, Fr, Params > &out, uint64_t predicate) noexcept
static element random_coordinates_on_curve(numeric::RNG *engine=nullptr) noexcept
element mul_without_endomorphism(const Fr &scalar) const noexcept
constexpr element & operator=(const element &other) noexcept
BB_INLINE constexpr void self_set_infinity() noexcept
BB_INLINE constexpr bool is_point_at_infinity() const noexcept
constexpr bool get_bit(uint64_t bit_index) const
constexpr uint64_t get_msb() const
std::pair< std::array< uint64_t, 2 >, std::array< uint64_t, 2 > > EndoScalars
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
constexpr size_t FF_COPY_COST
constexpr size_t FF_ADDITION_COST
constexpr size_t FF_MULTIPLICATION_COST
void fixed_wnaf(const uint64_t *scalar, uint64_t *wnaf, bool &skew_map, const uint64_t point_index, const uint64_t num_points, const size_t wnaf_bits) noexcept
Performs fixed-window non-adjacent form (WNAF) computation for scalar multiplication.
Univariate< Fr, domain_end, domain_start, skip_count > operator*(const Fr &ff, const Univariate< Fr, domain_end, domain_start, skip_count > &uv)
void parallel_for_heuristic(size_t num_points, const std::function< void(size_t, size_t, size_t)> &func, size_t heuristic_cost)
Split a loop into several loops running in parallel based on operations in 1 iteration.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
static constexpr field cube_root_of_unity()
static constexpr field one()
static constexpr uint256_t modulus
BB_INLINE constexpr void self_conditional_negate(uint64_t predicate) &noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
BB_INLINE constexpr void self_sqr() &noexcept
constexpr field invert() const noexcept
BB_INLINE constexpr bool is_msb_set() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr bool is_zero() const noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
static constexpr field zero()
Handles the WNAF computation for scalars that are split using an endomorphism, achieved through split...
EndomorphismWnaf(const EndoScalars &scalars)
std::array< uint64_t, NUM_ROUNDS *2 > table
static constexpr size_t NUM_WNAF_BITS