18template <
class Fq,
class Fr,
class T>
25template <
class Fq,
class Fr,
class T>
32template <
class Fq,
class Fr,
class T>
39template <
class Fq,
class Fr,
class T>
46template <
class Fq,
class Fr,
class T>
58template <
class Fq,
class Fr,
class T>
73 if (is_point_at_infinity()) {
81 Fq zz_inv = z_inv.
sqr();
82 Fq zzz_inv = zz_inv * z_inv;
83 affine_element<Fq, Fr, T> result(x * zz_inv, y * zzz_inv);
87template <
class Fq,
class Fr,
class T>
90 if (is_point_at_infinity()) {
98 Fq zz_inv = z_inv.
sqr();
99 Fq zzz_inv = zz_inv * z_inv;
106 if constexpr (
Fq::modulus.
data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
107 if (is_point_at_infinity()) {
111 if (x.is_msb_set_word()) {
143 if constexpr (T::has_a) {
144 T3 += (T::a * z.sqr().sqr());
180template <
class Fq,
class Fr,
class T>
183 if constexpr (
Fq::modulus.
data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
185 if (other.is_point_at_infinity()) {
188 if (is_point_at_infinity()) {
189 *
this = { other.
x, other.y,
Fq::one() };
193 const bool edge_case_trigger = x.
is_msb_set() || other.x.is_msb_set();
194 if (edge_case_trigger) {
195 if (x.is_msb_set()) {
196 *
this = { other.x, other.y,
Fq::one() };
206 Fq T1 = other.x * T0;
215 if (__builtin_expect(T1.
is_zero(), 0)) {
273template <
class Fq,
class Fr,
class T>
277 return (result += other);
280template <
class Fq,
class Fr,
class T>
283 const affine_element<Fq, Fr, T> to_add{ other.
x, -other.y };
284 return operator+=(to_add);
287template <
class Fq,
class Fr,
class T>
291 return (result -= other);
294template <
class Fq,
class Fr,
class T>
297 if constexpr (
Fq::modulus.
data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
298 bool p1_zero = is_point_at_infinity();
299 bool p2_zero = other.is_point_at_infinity();
300 if (__builtin_expect((p1_zero || p2_zero), 0)) {
301 if (p1_zero && !p2_zero) {
305 if (p2_zero && !p1_zero) {
312 bool p1_zero = x.is_msb_set();
313 bool p2_zero = other.x.is_msb_set();
314 if (__builtin_expect((p1_zero || p2_zero), 0)) {
315 if (p1_zero && !p2_zero) {
319 if (p2_zero && !p1_zero) {
327 Fq Z2Z2(other.z.sqr());
329 Fq U2(Z1Z1 * other.x);
332 Fq S1(Z2Z2 * other.z);
339 if (__builtin_expect(H.is_zero(), 0)) {
383template <
class Fq,
class Fr,
class T>
387 return (result += other);
390template <
class Fq,
class Fr,
class T>
393 const element to_add{ other.
x, -other.y, other.z };
394 return operator+=(to_add);
397template <
class Fq,
class Fr,
class T>
401 return (result -= other);
409template <
class Fq,
class Fr,
class T>
412 if constexpr (T::USE_ENDOMORPHISM) {
413 return mul_with_endomorphism(exponent);
415 return mul_without_endomorphism(exponent);
424template <
class Fq,
class Fr,
class T>
444 const uint64_t r =
engine->get_random_uint64() | (UINT64_C(1) << 63);
456 auto cs_fq = [](
Fq&
a,
Fq&
b, uint64_t mask) {
457 constexpr size_t NUM_LIMBS =
sizeof(
Fq) /
sizeof(uint64_t);
458 for (
size_t i = 0; i < NUM_LIMBS; ++i) {
465 cs_fq(
a.x,
b.x, mask);
466 cs_fq(
a.y,
b.y, mask);
467 cs_fq(
a.z,
b.z, mask);
476 for (
size_t i = NUM_BITS; i-- > 0;) {
477 const uint64_t mask = 0ULL -
static_cast<uint64_t
>(k_blinded.
get_bit(i));
494template <
class Fq,
class Fr,
class T>
497 return element(to_affine_const_time());
516 if constexpr (
Fq::modulus.
data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
536 if constexpr (
Fq::modulus.
data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
541 return (x.is_msb_set());
547 if (is_point_at_infinity()) {
556 Fq bz_6 = zzzz * zz * T::b;
557 if constexpr (T::has_a) {
558 bz_6 += (x * T::a) * zzzz;
560 Fq xxx = x.
sqr() * x + bz_6;
565template <
class Fq,
class Fr,
class T>
569 if ((!on_curve()) || (!other.on_curve())) {
572 bool am_infinity = is_point_at_infinity();
573 bool is_infinity = other.is_point_at_infinity();
574 bool both_infinity = am_infinity && is_infinity;
576 if ((!both_infinity) && (am_infinity || is_infinity)) {
579 const Fq lhs_zz = z.sqr();
580 const Fq lhs_zzz = lhs_zz * z;
581 const Fq rhs_zz = other.z.sqr();
582 const Fq rhs_zzz = rhs_zz * other.z;
584 const Fq lhs_x = x * rhs_zz;
585 const Fq lhs_y = y * rhs_zzz;
587 const Fq rhs_x = other.x * lhs_zz;
588 const Fq rhs_y = other.y * lhs_zzz;
589 return both_infinity || ((lhs_x == rhs_x) && (lhs_y == rhs_y));
592template <
class Fq,
class Fr,
class T>
595 if constexpr (T::can_hash_to_curve) {
598 Fq zz = result.
z.sqr();
599 Fq zzz = zz * result.
z;
609template <
class Fq,
class Fr,
class T>
612 const uint256_t converted_scalar(scalar);
614 if (converted_scalar == 0) {
619 const uint64_t maximum_set_bit = converted_scalar.
get_msb();
623 for (uint64_t i = maximum_set_bit - 1; i < maximum_set_bit; --i) {
625 if (converted_scalar.
get_bit(i)) {
668template <
class Fq,
class Fr,
class T>
671 if (is_point_at_infinity()) {
675 if (converted_scalar.
is_zero()) {
683 lookup_table[0] =
element(*
this);
684 for (
size_t i = 1; i < LOOKUP_SIZE; ++i) {
685 lookup_table[i] = lookup_table[i - 1] + *
this;
692 const uint64_t* k1 = endo_scalars.first.data();
693 const uint64_t* k2 = endo_scalars.second.data();
705 for (
size_t h = 0; h < 2; ++h) {
706 const uint64_t* s = (h == 0) ? k1 : k2;
708 const uint32_t magnitude = digit & 0x7FFFFFFFU;
709 if (magnitude == 0) {
712 const bool sign = (digit >> 31) != 0;
713 element to_add = lookup_table[magnitude - 1];
714 to_add.
y.self_conditional_negate(sign ^ (h == 1));
729template <
class Fq,
class Fr,
class T>
734 const size_t n = std::min(points.size(), scalars.size());
739 if constexpr (T::USE_ENDOMORPHISM) {
750 struct ActiveScalar {
758 for (
size_t i = 0; i < n; ++i) {
759 if (points[i].is_point_at_infinity()) {
769 for (
size_t k = 1; k < LOOKUP_SIZE; ++k) {
770 e.lookup[k] = e.lookup[k - 1] + pt;
777 if (active.empty()) {
785 for (
size_t w = NUM_WINDOWS; w-- > 0;) {
786 for (
size_t h = 0; h < 2; ++h) {
787 for (
auto&
a : active) {
788 const uint64_t* s = (h == 0) ?
a.k1.
data() :
a.k2.
data();
789 const uint32_t digit = detail::booth_packed_digit(s, slice_params[w], WINDOW_BITS);
790 const uint32_t magnitude = digit & 0x7FFFFFFFU;
791 if (magnitude == 0) {
794 const bool sign = (digit >> 31) != 0;
795 element to_add =
a.lookup[magnitude - 1];
796 to_add.
y.self_conditional_negate(sign ^ (h == 1));
804 for (
size_t d = 0; d < WINDOW_BITS; ++d) {
814 active_points.reserve(n);
815 active_scalars.reserve(n);
816 uint64_t max_set_bit = 0;
817 for (
size_t i = 0; i < n; ++i) {
818 if (points[i].is_point_at_infinity()) {
826 active_points.push_back(points[i]);
827 active_scalars.push_back(s);
829 if (active_points.empty()) {
834 for (uint64_t bit = max_set_bit + 1; bit-- > 0;) {
836 for (
size_t i = 0; i < active_points.size(); ++i) {
837 if (active_scalars[i].get_bit(bit)) {
860template <
typename AffineElement,
typename Fq>
861__attribute__((always_inline))
inline void batch_affine_add_impl(
const AffineElement* lhs,
864 Fq* scratch_space)
noexcept
870 scratch_space[i] = lhs[i].x +
rhs[i].x;
871 rhs[i].x -= lhs[i].x;
872 rhs[i].y -= lhs[i].y;
878 throw_or_abort(
"attempted to invert zero in batch_affine_add_impl");
887 rhs[i].x =
rhs[i].y.sqr();
888 rhs[i].x -= scratch_space[i];
891 Fq temp = lhs[i].x -
rhs[i].x;
893 rhs[i].y = temp - lhs[i].y;
906template <
typename AffineElement,
typename Fq>
907__attribute__((always_inline))
inline void batch_affine_add_interleaved(AffineElement* points,
909 Fq* scratch_space)
noexcept
915 scratch_space[i >> 1] = points[i].x + points[i + 1].x;
916 points[i + 1].x -= points[i].x;
917 points[i + 1].y -= points[i].y;
923 throw_or_abort(
"attempted to invert zero in batch_affine_add_interleaved");
932 points[i + 1].x = points[i + 1].y.sqr();
934 points[(i +
num_points) >> 1].x = points[i + 1].x - scratch_space[i >> 1];
937 __builtin_prefetch(points + i - 2);
938 __builtin_prefetch(points + i - 1);
939 __builtin_prefetch(points + ((i +
num_points - 2) >> 1));
940 __builtin_prefetch(scratch_space + ((i - 2) >> 1));
944 points[i].x -= points[(i +
num_points) >> 1].x;
945 points[i].x *= points[i + 1].y;
946 points[(i +
num_points) >> 1].y = points[i].x - points[i].y;
964template <
typename AffineElement,
typename Fq,
typename T>
965__attribute__((always_inline))
inline void batch_affine_double_impl(AffineElement* points,
967 Fq* scratch_space)
noexcept
973 scratch_space[i] = points[i].x.sqr();
974 if constexpr (T::has_a) {
975 scratch_space[i] += T::a;
977 scratch_space[i] = scratch_space[i] + scratch_space[i] + scratch_space[i];
983 throw_or_abort(
"attempted to invert zero in batch_affine_double_impl");
989 for (
size_t i_plus_1 =
num_points; i_plus_1 > 0; --i_plus_1) {
990 size_t i = i_plus_1 - 1;
996 points[i].x = scratch_space[i].
sqr() - (points[i].x + points[i].x);
997 points[i].y = scratch_space[i] * (
temp_x - points[i].x) - points[i].y;
1029template <
typename AffineElement,
typename Fq>
1030__attribute__((always_inline))
inline void batch_affine_combined_double_add_impl(
const AffineElement* to_add,
1035 Fq* scratch_c)
noexcept
1039 for (
size_t i = 0; i <
num_pairs; ++i) {
1050 throw_or_abort(
"attempted to invert zero in batch_affine_combined_double_add_impl phase 1");
1058 Fq x3 = scratch_c[k].
sqr();
1066 for (
size_t i = 0; i <
num_pairs; ++i) {
1073 throw_or_abort(
"attempted to invert zero in batch_affine_combined_double_add_impl phase 2");
1081 Fq lambda2 = -scratch_c[k];
1084 Fq x4 = lambda2.
sqr();
1121template <
typename AffineElement,
typename Fq>
1122__attribute__((always_inline))
inline void batch_affine_add_indexed_impl(AffineElement* buckets,
1125 Fq* scratch_space)
noexcept
1133 constexpr size_t PREFETCH_AHEAD = 4;
1139 for (
size_t i = 0; i <
num_pairs; ++i) {
1141 __builtin_prefetch(buckets +
pairs[i + PREFETCH_AHEAD].first, 1, 3);
1142 __builtin_prefetch(buckets +
pairs[i + PREFETCH_AHEAD].second, 0, 3);
1144 AffineElement& dst = buckets[
pairs[i].first];
1145 const AffineElement& src = buckets[
pairs[i].second];
1146 scratch_space[i] = src.x + dst.x;
1154 throw_or_abort(
"attempted to invert zero in batch_affine_add_indexed_impl");
1159 for (
size_t j =
num_pairs; j > 0; --j) {
1160 const size_t i = j - 1;
1161 if (i >= PREFETCH_AHEAD) {
1162 __builtin_prefetch(buckets +
pairs[i - PREFETCH_AHEAD].first, 1, 3);
1163 __builtin_prefetch(buckets +
pairs[i - PREFETCH_AHEAD].second, 0, 3);
1164 __builtin_prefetch(scratch_space + (i - PREFETCH_AHEAD), 0, 3);
1166 AffineElement& dst = buckets[
pairs[i].first];
1167 const AffineElement& src = buckets[
pairs[i].second];
1172 dst.x = dst.y.sqr();
1173 dst.x -= scratch_space[i];
1176 Fq temp = src.x - dst.x;
1178 dst.y = temp - src.y;
1198template <
typename AffineElement,
typename Fq>
1199__attribute__((always_inline))
inline void batch_affine_double_indexed_impl(AffineElement* buckets,
1202 Fq* scratch_space)
noexcept
1208 constexpr size_t PREFETCH_AHEAD = 4;
1215 __builtin_prefetch(buckets +
indices[i + PREFETCH_AHEAD], 1, 3);
1217 AffineElement& p = buckets[
indices[i]];
1218 scratch_space[i] = p.x.sqr();
1219 scratch_space[i] = scratch_space[i] + scratch_space[i] + scratch_space[i];
1225 throw_or_abort(
"attempted to invert zero in batch_affine_double_indexed_impl");
1232 const size_t i = j - 1;
1233 if (i >= PREFETCH_AHEAD) {
1234 __builtin_prefetch(buckets +
indices[i - PREFETCH_AHEAD], 1, 3);
1235 __builtin_prefetch(scratch_space + (i - PREFETCH_AHEAD), 0, 3);
1237 AffineElement& p = buckets[
indices[i]];
1243 p.x = scratch_space[i].
sqr() - (p.x + p.x);
1244 p.y = scratch_space[i] * (
temp_x - p.x) - p.y;
1259template <
class Fq,
class Fr,
class T>
1265 const size_t num_points = first_group.size();
1277 [&](
size_t start,
size_t end,
BB_UNUSED size_t chunk_index) {
1278 batch_affine_add_impl<affine_element, Fq>(
1279 &second_group[start], &results[start], end - start, &scratch_space[start]);
1294template <
class Fq,
class Fr,
class T>
1323 if (converted_scalar.
is_zero()) {
1350 const uint64_t* k1 = endo_scalars.first.data();
1351 const uint64_t* k2 = endo_scalars.second.data();
1352 BB_ASSERT((k2[1] >> 63) == 0,
"GLV K2 split must fit below 2^127 for the offset Booth window schedule");
1356 for (
size_t w = 0; w < NUM_WINDOWS; ++w) {
1357 k1_digits[w] = detail::booth_packed_digit(k1, slice_params[w], WINDOW_BITS);
1359 k2_digits[0] = detail::booth_packed_digit(k2, k2_slice_params[0], K2_LOW_WINDOW_BITS);
1360 for (
size_t w = 1; w < K2_NUM_WINDOWS; ++w) {
1361 k2_digits[w] = detail::booth_packed_digit(k2, k2_slice_params[w], WINDOW_BITS);
1378 auto compute_safe_mask = [&]() -> uint64_t {
1382 bool initialised =
false;
1384 const auto signed_digit = [](uint32_t packed) -> int64_t {
1385 const int64_t mag =
static_cast<int64_t
>(packed & 0x7FFFFFFFU);
1386 return ((packed >> 31) != 0) ? -mag : mag;
1394 const auto edge_for_combined = [&](int64_t d,
bool is_k1) ->
bool {
1399 if ((d % 2 == 0) && (2 *
a == d || 2 *
a == -d)) {
1402 return (d % 4 == 0) && (4 *
a == -d);
1407 if ((d % 2 == 0) && (2 *
b == d || 2 *
b == -d)) {
1410 return (d % 4 == 0) && (4 *
b == -d);
1416 const auto edge_for_add = [&](int64_t d,
bool is_k1) ->
bool {
1418 return (
b == 0) && (
a == d ||
a == -d);
1420 return (
a == 0) && (
b == d ||
b == -d);
1425 const uint32_t d126 = k2_digits[K2_NUM_WINDOWS - 1];
1426 if ((d126 & 0x7FFFFFFFU) != 0) {
1427 b = signed_digit(d126);
1433 for (
size_t step = 0; step < 62; ++step) {
1440 const size_t pos = 124 - 2 * step;
1441 const bool is_k1 = (pos % 4 == 0);
1442 const uint32_t digit = is_k1 ? k1_digits[pos / 4] : k2_digits[(pos + 2) / 4];
1443 const uint32_t m = digit & 0x7FFFFFFFU;
1444 const int64_t d = signed_digit(digit);
1462 if (edge_for_combined(d, is_k1)) {
1463 mask |= (uint64_t{ 1 } << step);
1477 const uint32_t d0 = k1_digits[0];
1478 const uint32_t d1 = k2_digits[0];
1479 const uint32_t m0 = d0 & 0x7FFFFFFFU;
1480 const uint32_t m1 = d1 & 0x7FFFFFFFU;
1481 const int64_t s0 = signed_digit(d0);
1482 const int64_t s1 = signed_digit(d1);
1491 }
else if (m1 != 0) {
1495 }
else if (m0 == 0 && m1 == 0) {
1499 const bool fuse_with_h1 = (m0 == 0);
1500 const int64_t fused_d = fuse_with_h1 ? s1 : s0;
1501 if (edge_for_combined(fused_d, !fuse_with_h1)) {
1502 mask |= (uint64_t{ 1 } << 62);
1511 if (m0 != 0 && m1 != 0) {
1514 if (edge_for_add(s1,
false)) {
1515 mask |= (uint64_t{ 1 } << 63);
1524 const uint64_t safe_mask = compute_safe_mask();
1528 for (
auto& table : lookup_table) {
1533 auto execute_range = [&](
size_t start,
size_t end) {
1536 batch_affine_add_impl<affine_element, Fq>(&lhs[start], &
rhs[start], end - start, &
scratch_a[start]);
1539 for (
size_t i = start; i < end; ++i) {
1546 batch_affine_double_impl<affine_element, Fq, T>(&lhs[start], end - start, &
scratch_a[start]);
1550 batch_affine_combined_double_add_impl<affine_element, Fq>(
1551 &to_add[start], &accum[start], end - start, &
scratch_a[start], &
scratch_b[start], &scratch_c[start]);
1554 for (
size_t i = start; i < end; ++i) {
1564 for (
size_t i = start; i < end; ++i) {
1565 if (points[i].is_point_at_infinity()) {
1569 lookup_table[0][i] = points[i];
1570 temp_point_vector[i] = points[i];
1575 for (
size_t i = start; i < end; ++i) {
1576 lookup_table[1][i] = lookup_table[0][i];
1578 double_chunked(&lookup_table[1][0]);
1580 for (
size_t j = 2; j < LOOKUP_SIZE; ++j) {
1581 for (
size_t i = start; i < end; ++i) {
1582 lookup_table[j][i] = lookup_table[j - 1][i];
1584 add_chunked(&temp_point_vector[0], &lookup_table[j][0]);
1592 auto fill_to_add = [&](uint32_t digit,
bool half_idx,
affine_element* dst) {
1593 const uint32_t magnitude = digit & 0x7FFFFFFFU;
1594 const bool sign = (digit >> 31) != 0;
1595 const bool flip_y = sign ^ half_idx;
1596 for (
size_t i = start; i < end; ++i) {
1598 pt.
y.self_conditional_negate(flip_y);
1614 bool initialised =
false;
1615 const auto update_initialised_from_work = [&]() { initialised = !work_elements[start].
is_point_at_infinity(); };
1616 auto seed_or_skip = [&](uint32_t digit,
bool half_idx) {
1618 if ((digit & 0x7FFFFFFFU) != 0) {
1619 fill_to_add(digit, half_idx, &work_elements[0]);
1625 seed_or_skip(k2_digits[K2_NUM_WINDOWS - 1],
true);
1628 for (
size_t step = 0; step < 62; ++step) {
1629 const size_t pos = 124 - 2 * step;
1630 const bool is_k1 = (pos % 4 == 0);
1631 const uint32_t digit = is_k1 ? k1_digits[pos / 4] : k2_digits[(pos + 2) / 4];
1632 const bool half_idx = !is_k1;
1633 const uint32_t m = digit & 0x7FFFFFFFU;
1637 fill_to_add(digit, half_idx, &work_elements[0]);
1645 double_chunked(&work_elements[0]);
1646 double_chunked(&work_elements[0]);
1651 double_chunked(&work_elements[0]);
1652 fill_to_add(digit, half_idx, &temp_point_vector[0]);
1653 if ((safe_mask >> step) & uint64_t{ 1 }) {
1654 combined_safe_chunked(&temp_point_vector[0], &work_elements[0]);
1655 update_initialised_from_work();
1657 combined_chunked(&temp_point_vector[0], &work_elements[0]);
1665 const uint32_t d0 = k1_digits[0];
1666 const uint32_t d1 = k2_digits[0];
1667 const uint32_t m0 = d0 & 0x7FFFFFFFU;
1668 const uint32_t m1 = d1 & 0x7FFFFFFFU;
1672 fill_to_add(d0,
false, &work_elements[0]);
1678 fill_to_add(d1,
true, &temp_point_vector[0]);
1679 add_chunked(&temp_point_vector[0], &work_elements[0]);
1681 }
else if (m1 != 0) {
1682 fill_to_add(d1,
true, &work_elements[0]);
1685 }
else if (m0 == 0 && m1 == 0) {
1686 double_chunked(&work_elements[0]);
1687 double_chunked(&work_elements[0]);
1689 double_chunked(&work_elements[0]);
1690 const bool fuse_with_h1 = (m0 == 0);
1691 const uint32_t fused_digit = fuse_with_h1 ? d1 : d0;
1692 fill_to_add(fused_digit, fuse_with_h1, &temp_point_vector[0]);
1693 if ((safe_mask >> 62) & uint64_t{ 1 }) {
1694 combined_safe_chunked(&temp_point_vector[0], &work_elements[0]);
1695 update_initialised_from_work();
1697 combined_chunked(&temp_point_vector[0], &work_elements[0]);
1699 if (m0 != 0 && m1 != 0) {
1701 fill_to_add(d1,
true, &work_elements[0]);
1704 fill_to_add(d1,
true, &temp_point_vector[0]);
1705 if ((safe_mask >> 63) & uint64_t{ 1 }) {
1706 add_safe_chunked(&temp_point_vector[0], &work_elements[0]);
1707 update_initialised_from_work();
1709 add_chunked(&temp_point_vector[0], &work_elements[0]);
1716 BB_ASSERT(initialised,
"non-zero scalar must produce at least one non-zero Booth digit");
1719 for (
size_t i = start; i < end; ++i) {
1720 if (points[i].is_point_at_infinity()) {
1721 work_elements[i].self_set_infinity();
1727 return work_elements;
1730template <
typename Fq,
typename Fr,
typename T>
1734 temporaries.reserve(num_elements * 2);
1739 for (
size_t i = 0; i < num_elements; ++i) {
1741 if (!elements[i].is_point_at_infinity()) {
1771 for (
size_t i = num_elements - 1; i < num_elements; --i) {
1772 if (!elements[i].is_point_at_infinity()) {
1774 Fq zz_inv = z_inv.
sqr();
1775 elements[i].x *= zz_inv;
1776 elements[i].y *= (zz_inv * z_inv);
1783template <
typename Fq,
typename Fr,
typename T>
1787 bool found_one =
false;
1791 while (!found_one) {
1793 yy = x.sqr() * x + T::b;
1794 if constexpr (T::has_a) {
1797 auto [found_root, y1] = yy.sqrt();
1799 found_one = found_root;
#define BB_ASSERT(expression,...)
#define BB_ASSERT_EQ(actual, expected,...)
#define BB_BENCH_NAME(name)
#define BB_BENCH_TRACY_NAME(name)
constexpr bool is_point_at_infinity() const noexcept
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
constexpr affine_element< Fq, Fr, Params > to_affine_const_time() 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.
element mul_const_time(const Fr &scalar, numeric::RNG *engine=nullptr) const noexcept
Constant-time scalar multiplication intended for secret scalars (e.g. ECDSA / Schnorr nonces).
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
static element straus_msm(std::span< const affine_element< Fq, Fr, Params > > points, std::span< const Fr > scalars) noexcept
Straus-style multi-scalar multiplication.
element() noexcept=default
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
constexpr element normalize_const_time() const 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
bool get_bit(uint64_t bit_index) const
constexpr std::array< BoothSliceParams, NUM_WINDOWS > make_offset_booth_slice_params() noexcept
constexpr std::array< BoothSliceParams, NUM_WINDOWS > make_booth_slice_params() noexcept
uint32_t booth_packed_digit(const uint64_t *s, const BoothSliceParams &sp, size_t window_bits) noexcept
Read a (window_bits+1)-bit window from s[] (uint64 limbs) and apply Constantine's signedWindowEncodin...
constexpr size_t BOOTH_ENDO_K2_NUM_WINDOWS
std::pair< std::array< uint64_t, 2 >, std::array< uint64_t, 2 > > EndoScalars
constexpr size_t BOOTH_ENDO_K2_LOW_WINDOW_BITS
constexpr size_t BOOTH_ENDO_WINDOW_BITS
constexpr size_t BOOTH_ENDO_LOOKUP_SIZE
constexpr size_t BOOTH_ENDO_NUM_WINDOWS
constexpr size_t BOOTH_ENDO_NUM_LIMBS_U64
AffineElement const size_t Fq *scratch_space noexcept
AffineElement const size_t num_pairs
__attribute__((always_inline)) inline void batch_affine_add_impl(const AffineElement *lhs
Batch affine addition for parallel arrays: (lhs[i], rhs[i]) → rhs[i].
batch_inversion_accumulator
AffineElement const size_t Fq Fq * scratch_b
AffineElement * accumulator
AffineElement const size_t Fq * scratch_a
const std::pair< uint32_t, uint32_t > * pairs
uintx< uint256_t > uint512_t
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
Univariate< Fr, domain_end > operator*(const Fr &ff, const Univariate< Fr, domain_end > &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.
void parallel_for_range(size_t num_points, const std::function< void(size_t, size_t)> &func, size_t no_multhreading_if_less_or_equal)
Split a loop into several loops running in parallel.
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
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and 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
BB_INLINE constexpr bool is_zero() const noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
static constexpr field zero()
constexpr field invert_const_time() const noexcept
void throw_or_abort(std::string const &err)