19#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
20#if defined(__clang__) || defined(__GNUC__)
21#pragma GCC diagnostic push
22#pragma GCC diagnostic ignored "-Wpedantic"
31#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
32 return UInt128((
static_cast<unsigned __int128
>(hi) << 64) | lo);
39#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
40 return UInt128(
static_cast<unsigned __int128
>(lhs) * rhs);
42 constexpr std::uint64_t kMask32 = 0xffffffffULL;
44 std::uint64_t lhs_lo = lhs & kMask32;
45 std::uint64_t lhs_hi = lhs >> 32;
46 std::uint64_t rhs_lo = rhs & kMask32;
47 std::uint64_t rhs_hi = rhs >> 32;
49 std::uint64_t lo_lo = lhs_lo * rhs_lo;
50 std::uint64_t hi_lo = lhs_hi * rhs_lo;
51 std::uint64_t lo_hi = lhs_lo * rhs_hi;
52 std::uint64_t hi_hi = lhs_hi * rhs_hi;
54 std::uint64_t cross = (lo_lo >> 32) + (hi_lo & kMask32) + (lo_hi & kMask32);
55 return UInt128(hi_hi + (hi_lo >> 32) + (lo_hi >> 32) + (cross >> 32),
56 (lo_lo & kMask32) | (cross << 32));
61#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
62 return UInt128(value_ + addend);
66 out.hi_ += (out.lo_ < addend) ? 1U : 0U;
71 [[nodiscard]] std::pair<UInt128, std::uint32_t>
divmod_u32(std::uint32_t divisor)
const {
72 assert(divisor != 0 &&
"UInt128::divmod_u32 requires a non-zero divisor");
73#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
74 unsigned __int128 quotient = value_ / divisor;
75 return std::make_pair(
UInt128(quotient),
static_cast<std::uint32_t
>(value_ % divisor));
77 constexpr std::uint64_t kMask32 = 0xffffffffULL;
79 std::uint64_t rem = 0;
80 auto step = [&](std::uint32_t word) {
81 std::uint64_t cur = (rem << 32) | word;
82 std::uint32_t qword =
static_cast<std::uint32_t
>(cur / divisor);
90 const std::uint32_t q3 = step(
static_cast<std::uint32_t
>(hi_ >> 32));
91 const std::uint32_t q2 = step(
static_cast<std::uint32_t
>(hi_ & kMask32));
92 const std::uint32_t q1 = step(
static_cast<std::uint32_t
>(lo_ >> 32));
93 const std::uint32_t q0 = step(
static_cast<std::uint32_t
>(lo_ & kMask32));
95 UInt128 quotient((
static_cast<std::uint64_t
>(q3) << 32) | q2, (
static_cast<std::uint64_t
>(q1) << 32) | q0);
96 return std::make_pair(quotient,
static_cast<std::uint32_t
>(rem));
100 [[nodiscard]] std::uint64_t
low64()
const {
101#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
102 return static_cast<std::uint64_t
>(value_);
108 [[nodiscard]] std::uint64_t
high64()
const {
109#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
110 return static_cast<std::uint64_t
>(value_ >> 64);
117#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
118 explicit UInt128(
unsigned __int128 value) : value_(value) {}
119 unsigned __int128 value_ = 0;
121 UInt128(std::uint64_t hi, std::uint64_t lo) : lo_(lo), hi_(hi) {}
122 std::uint64_t lo_ = 0;
123 std::uint64_t hi_ = 0;
127#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
128#if defined(__clang__) || defined(__GNUC__)
129#pragma GCC diagnostic pop
134 return value == 0 ? 0U : 64U -
static_cast<std::size_t
>(std::countl_zero(value));
137template <std::
size_t Words>
142#define PURIFY_DEFINE_BIGUINT_C_BRIDGE(words, bits) \
144struct BigUIntCBridge<words> { \
145 static constexpr bool kAvailable = true; \
146 static void set_zero(std::uint64_t* out) { purify_u##bits##_set_zero(out); } \
147 static void set_u64(std::uint64_t* out, std::uint64_t value) { purify_u##bits##_set_u64(out, value); } \
148 static void from_bytes_be(std::uint64_t* out, const unsigned char* data, std::size_t size) { \
149 purify_u##bits##_from_bytes_be(out, data, size); \
151 static bool is_zero(const std::uint64_t* value) { return purify_u##bits##_is_zero(value) != 0; } \
152 static int compare(const std::uint64_t* lhs, const std::uint64_t* rhs) { return purify_u##bits##_compare(lhs, rhs); } \
153 static bool try_add_small(std::uint64_t* value, std::uint32_t addend) { \
154 return purify_u##bits##_try_add_small(value, addend) != 0; \
156 static bool try_mul_small(std::uint64_t* value, std::uint32_t factor) { \
157 return purify_u##bits##_try_mul_small(value, factor) != 0; \
159 static bool try_add(std::uint64_t* value, const std::uint64_t* addend) { \
160 return purify_u##bits##_try_add(value, addend) != 0; \
162 static bool try_sub(std::uint64_t* value, const std::uint64_t* subtrahend) { \
163 return purify_u##bits##_try_sub(value, subtrahend) != 0; \
165 static std::size_t bit_length(const std::uint64_t* value) { return purify_u##bits##_bit_length(value); } \
166 static bool bit(const std::uint64_t* value, std::size_t index) { return purify_u##bits##_bit(value, index) != 0; } \
167 static bool try_set_bit(std::uint64_t* value, std::size_t index) { \
168 return purify_u##bits##_try_set_bit(value, index) != 0; \
170 static void shifted_left(std::uint64_t* out, const std::uint64_t* value, std::size_t bits_value) { \
171 purify_u##bits##_shifted_left(out, value, bits_value); \
173 static void shifted_right(std::uint64_t* out, const std::uint64_t* value, std::size_t bits_value) { \
174 purify_u##bits##_shifted_right(out, value, bits_value); \
176 static void shift_right_one(std::uint64_t* value) { purify_u##bits##_shift_right_one(value); } \
177 static void mask_bits(std::uint64_t* value, std::size_t bits_value) { purify_u##bits##_mask_bits(value, bits_value); } \
178 static std::uint32_t divmod_small(std::uint64_t* value, std::uint32_t divisor) { \
179 return purify_u##bits##_divmod_small(value, divisor); \
181 static void to_bytes_be(unsigned char* out, const std::uint64_t* value) { purify_u##bits##_to_bytes_be(out, value); } \
188#undef PURIFY_DEFINE_BIGUINT_C_BRIDGE
190struct FieldElementAccess;
199template <std::
size_t Words>
201 std::array<std::uint64_t, Words>
limbs{};
229 out.
limbs[0] = value;
240 for (std::size_t i = 0; i < size; ++i) {
251 if (hex.size() >= 2 && hex[0] ==
'0' && (hex[1] ==
'x' || hex[1] ==
'X')) {
252 hex.remove_prefix(2);
254 for (
char ch : hex) {
255 if (std::isspace(
static_cast<unsigned char>(ch)) != 0) {
259 if (ch >=
'0' && ch <=
'9') {
260 value =
static_cast<unsigned>(ch -
'0');
261 }
else if (ch >=
'a' && ch <=
'f') {
262 value =
static_cast<unsigned>(10 + ch -
'a');
263 }
else if (ch >=
'A' && ch <=
'F') {
264 value =
static_cast<unsigned>(10 + ch -
'A');
282 assert(out.
has_value() &&
"BigUInt::from_hex() requires valid in-range input");
283 return std::move(*out);
291 for (std::uint64_t limb :
limbs) {
305 for (std::size_t i = Words; i != 0; --i) {
306 const std::size_t idx = i - 1;
323 return !(*
this == other);
342 std::uint64_t carry = value;
343 for (std::size_t i = 0; i < Words && carry != 0; ++i) {
344 std::uint64_t sum = out.
limbs[i] + carry;
345 carry = (sum < out.
limbs[i]) ? 1U : 0U;
363 assert(ok &&
"BigUInt::add_small() overflow");
375 std::uint64_t carry = 0;
376 for (std::size_t i = 0; i < Words; ++i) {
397 assert(ok &&
"BigUInt::mul_small() overflow");
409 std::uint64_t carry = 0;
410 for (std::size_t i = 0; i < Words; ++i) {
411 std::uint64_t sum = out.
limbs[i] + other.
limbs[i];
412 std::uint64_t carry1 = (sum < out.
limbs[i]) ? 1U : 0U;
413 std::uint64_t next = sum + carry;
414 std::uint64_t carry2 = (next < sum) ? 1U : 0U;
416 carry = carry1 | carry2;
433 assert(ok &&
"BigUInt::add_assign() overflow");
445 std::uint64_t borrow = 0;
446 for (std::size_t i = 0; i < Words; ++i) {
447 std::uint64_t rhs = other.
limbs[i] + borrow;
448 std::uint64_t rhs_overflow = (rhs < other.
limbs[i]) ? 1U : 0U;
449 std::uint64_t next = out.
limbs[i] - rhs;
450 std::uint64_t needs_borrow = (out.
limbs[i] < rhs) ? 1U : 0U;
452 borrow = rhs_overflow | needs_borrow;
469 assert(ok &&
"BigUInt::sub_assign() underflow");
478 for (std::size_t i = Words; i != 0; --i) {
479 const std::size_t idx = i - 1;
480 if (
limbs[idx] != 0) {
489 bool bit(std::size_t index)
const {
493 std::size_t word = index / 64;
494 std::size_t shift = index % 64;
498 return ((
limbs[word] >> shift) & 1U) != 0;
507 std::size_t word = index / 64;
508 std::size_t shift = index % 64;
512 limbs[word] |= (
static_cast<std::uint64_t
>(1) << shift);
524 assert(ok &&
"BigUInt::set_bit() index out of range");
534 std::size_t word_shift = bits / 64;
535 std::size_t bit_shift = bits % 64;
536 for (std::size_t i = Words; i != 0; --i) {
537 const std::size_t idx = i - 1;
538 if (idx < word_shift) {
541 std::size_t src = idx - word_shift;
543 if (bit_shift != 0 && src > 0) {
544 out.
limbs[idx] |=
limbs[src - 1] >> (64 - bit_shift);
557 std::size_t word_shift = bits / 64;
558 std::size_t bit_shift = bits % 64;
559 for (std::size_t i = 0; i < Words; ++i) {
560 std::size_t src = i + word_shift;
565 if (bit_shift != 0 && src + 1 < Words) {
566 out.
limbs[i] |=
limbs[src + 1] << (64 - bit_shift);
578 for (std::size_t i = 0; i < Words; ++i) {
579 std::uint64_t next = (i + 1 < Words) ?
limbs[i + 1] : 0;
590 std::size_t full_words = bits / 64;
591 std::size_t extra_bits = bits % 64;
592 for (std::size_t i = full_words + (extra_bits != 0 ? 1 : 0); i < Words; ++i) {
595 if (extra_bits != 0 && full_words < Words) {
597 (extra_bits == 64) ? ~
static_cast<std::uint64_t
>(0) : ((
static_cast<std::uint64_t
>(1) << extra_bits) - 1);
598 limbs[full_words] &= mask;
608 std::uint64_t rem = 0;
609 for (std::size_t i = Words; i != 0; --i) {
610 const std::size_t idx = i - 1;
612 assert(quotient.high64() == 0 &&
"BigUInt::divmod_small() quotient must fit in 64 bits");
613 limbs[idx] = quotient.low64();
616 return static_cast<std::uint32_t
>(rem);
622 std::array<unsigned char, Words * 8> out{};
626 for (std::size_t i = 0; i < Words; ++i) {
627 std::uint64_t limb =
limbs[i];
628 for (std::size_t j = 0; j < 8; ++j) {
629 out[out.size() - 1 - (i * 8 + j)] =
static_cast<unsigned char>(limb & 0xffU);
640 std::ostringstream out;
641 bool started =
false;
642 out << std::hex << std::nouppercase;
643 for (
unsigned char byte : bytes) {
648 out << static_cast<unsigned>(
byte);
651 out << std::setw(2) << std::setfill('0') << static_cast<unsigned>(
byte);
654 return started ? out.str() :
"0";
663 std::vector<std::uint32_t> parts;
667 std::ostringstream out;
669 for (std::size_t i = parts.size(); i > 1; --i) {
670 out << std::setw(9) << std::setfill(
'0') << parts[i - 2];
677template <std::
size_t OutWords, std::
size_t InWords>
679 static_assert(OutWords >= InWords,
"Cannot narrow with widen");
681 if constexpr (OutWords == 5 && InWords == 4) {
683 }
else if constexpr (OutWords == 8 && InWords == 4) {
686 for (std::size_t i = 0; i < InWords; ++i) {
694template <std::
size_t OutWords, std::
size_t InWords>
696 static_assert(OutWords <= InWords,
"Cannot widen with narrow");
698 if constexpr (OutWords == 4 && InWords == 5) {
702 }
else if constexpr (OutWords == 4 && InWords == 8) {
707 for (std::size_t i = 0; i < OutWords; ++i) {
710 for (std::size_t i = OutWords; i < InWords; ++i) {
711 if (value.
limbs[i] != 0) {
720template <std::
size_t OutWords, std::
size_t InWords>
723 assert(out.
has_value() &&
"narrow() requires the source value to fit");
724 return std::move(*out);
728template <std::
size_t Words>
732 if constexpr (Words == 8) {
734 numerator.
limbs.data(), denominator.
limbs.data())) {
742 std::size_t d_bits = denominator.
bit_length();
743 if (n_bits < d_bits) {
744 return std::make_pair(quotient, remainder);
746 std::size_t shift = n_bits - d_bits;
748 for (std::size_t i = shift + 1; i != 0; --i) {
749 const std::size_t idx = i - 1;
750 if (remainder.
compare(shifted) >= 0) {
753 assert(sub_ok &&
"divmod_same() subtraction should stay in range");
754 assert(bit_ok &&
"divmod_same() quotient bit index should stay in range");
755 if (!sub_ok || !bit_ok) {
764 return std::make_pair(quotient, remainder);
768template <std::
size_t Words>
771 assert(out.
has_value() &&
"divmod_same() requires a non-zero denominator");
772 return std::move(*out);
776template <std::
size_t LeftWords, std::
size_t RightWords>
779 if constexpr (LeftWords == 4 && RightWords == 4) {
782 for (std::size_t i = 0; i < LeftWords; ++i) {
783 std::uint64_t carry = 0;
784 for (std::size_t j = 0; j < RightWords; ++j) {
791 out.
limbs[i + RightWords] += carry;
861 std::string
to_hex()
const;
891 std::optional<FieldElement>
sqrt()
const;
920struct FieldElementAccess {
921 static const purify_scalar& raw(
const FieldElement& value)
noexcept {
925 static FieldElement from_raw(
const purify_scalar& raw)
noexcept {
926 return FieldElement(raw);
933FieldElement
square(
const FieldElement& value);
Purify result carrier that either holds a value or an error.
bool has_value() const noexcept
Field element modulo the backend scalar field used by this implementation.
bool is_square() const
Returns true when the element is a quadratic residue in the field.
bool is_one() const
Returns true when the element is one.
std::array< unsigned char, 32 > to_bytes_le() const
Serializes the field element in little-endian form.
static FieldElement from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a 32-byte big-endian field element.
static FieldElement from_uint256(const UInt256 &value)
Converts a 256-bit unsigned integer into the scalar field representation.
friend bool operator!=(const FieldElement &lhs, const FieldElement &rhs)
Compares two field elements for inequality.
FieldElement inverse() const
Returns the multiplicative inverse modulo the field prime using the faster variable-time backend.
friend FieldElement operator+(const FieldElement &lhs, const FieldElement &rhs)
Adds two field elements modulo the field prime.
FieldElement pow(const UInt256 &exponent) const
Raises the element to an unsigned exponent via square-and-multiply.
bool is_odd() const
Returns true when the canonical representative is odd.
static FieldElement from_u64(std::uint64_t value)
Constructs a field element from an unsigned 64-bit integer.
std::string to_hex() const
Formats the field element as lowercase hexadecimal.
static Result< FieldElement > try_from_uint256(const UInt256 &value)
Converts a canonical 256-bit unsigned integer into the scalar field representation.
std::optional< FieldElement > sqrt() const
Computes a square root when one exists, otherwise returns std::nullopt.
friend bool operator==(const FieldElement &lhs, const FieldElement &rhs)
Compares two field elements for exact equality.
std::string to_decimal() const
Formats the field element as an unsigned decimal string.
static FieldElement one()
Returns the multiplicative identity of the scalar field.
void conditional_assign(const FieldElement &other, bool flag)
Conditionally assigns other into *this when flag is true.
static Result< FieldElement > try_from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a canonical 32-byte big-endian field element.
FieldElement negate() const
Returns the additive inverse modulo the field prime.
static FieldElement from_int(std::int64_t value)
Constructs a field element from a signed integer, reducing negatives modulo the field.
UInt256 to_uint256() const
Exports the field element as a canonical 256-bit unsigned integer.
friend struct detail::FieldElementAccess
bool is_zero() const
Returns true when the element is zero.
static FieldElement zero()
Returns the additive identity of the scalar field.
friend FieldElement operator-(const FieldElement &lhs, const FieldElement &rhs)
Subtracts two field elements modulo the field prime.
std::array< unsigned char, 32 > to_bytes_be() const
Serializes the field element in big-endian form.
friend FieldElement operator*(const FieldElement &lhs, const FieldElement &rhs)
Multiplies two field elements modulo the field prime.
FieldElement inverse_consttime() const
Returns the multiplicative inverse modulo the field prime in constant time.
static UInt128 mul_u64(std::uint64_t lhs, std::uint64_t rhs)
std::pair< UInt128, std::uint32_t > divmod_u32(std::uint32_t divisor) const
std::uint64_t low64() const
std::uint64_t high64() const
static UInt128 from_words(std::uint64_t hi, std::uint64_t lo)
UInt128 add_u64(std::uint64_t addend) const
Shared includes and foundational aliases for the Purify C++ implementation.
std::size_t bit_length_u64(std::uint64_t value)
BigUInt< LeftWords+RightWords > multiply(const BigUInt< LeftWords > &lhs, const BigUInt< RightWords > &rhs)
Multiplies two fixed-width integers and returns the full-width product.
BigUInt< OutWords > widen(const BigUInt< InWords > &value)
Widens an integer to a larger limb count by zero-extending high limbs.
constexpr Unexpected< Error > unexpected_error(ErrorCode code, const char *context=nullptr)
Constructs an unexpected Error value from a machine-readable code.
const UInt256 & prime_p()
Returns the Purify base-field modulus.
Result< std::pair< BigUInt< Words >, BigUInt< Words > > > try_divmod_same(const BigUInt< Words > &numerator, const BigUInt< Words > &denominator)
Performs long division where numerator and denominator have the same width.
BigUInt< OutWords > narrow(const BigUInt< InWords > &value)
Narrows an integer to a smaller limb count, requiring that no high bits are lost.
FieldElement square(const FieldElement &value)
Squares a field element.
Result< BigUInt< OutWords > > try_narrow(const BigUInt< InWords > &value)
Narrows an integer to a smaller limb count, rejecting truncated high bits.
std::pair< BigUInt< Words >, BigUInt< Words > > divmod_same(const BigUInt< Words > &numerator, const BigUInt< Words > &denominator)
Performs long division where numerator and denominator have the same width.
int legendre_symbol(const FieldElement &value)
Returns 0 for zero, 1 for quadratic residues, and -1 for non-residues.
#define PURIFY_DEFINE_BIGUINT_C_BRIDGE(words, bits)
Little-endian fixed-width unsigned integer with simple arithmetic utilities.
std::size_t bit_length() const
Returns the index of the highest set bit plus one.
bool bit(std::size_t index) const
Returns the bit at the given little-endian bit index.
static BigUInt zero()
Returns the additive identity.
static BigUInt from_bytes_be(const unsigned char *data, std::size_t size)
Parses a big-endian byte string into the fixed-width integer.
std::array< std::uint64_t, Words > limbs
std::uint32_t divmod_small(std::uint32_t divisor)
Divides by a small unsigned value in place and returns the remainder.
bool is_zero() const
Returns true when all limbs are zero.
bool try_sub_assign(const BigUInt &other)
Subtracts another fixed-width integer when the minuend is large enough.
bool try_mul_small(std::uint32_t value)
Multiplies by a small unsigned value in place when the result fits.
void sub_assign(const BigUInt &other)
Subtracts another fixed-width integer in place.
static BigUInt from_u64(std::uint64_t value)
Constructs a value from a single 64-bit limb.
int compare(const BigUInt &other) const
Compares two fixed-width integers using unsigned ordering.
void add_small(std::uint32_t value)
Adds a small unsigned value in place.
bool operator==(const BigUInt &other) const
void mul_small(std::uint32_t value)
Multiplies by a small unsigned value in place.
void set_bit(std::size_t index)
Sets the bit at the given little-endian bit index.
static BigUInt from_hex(std::string_view hex)
Parses a hexadecimal string with the precondition that the value fits exactly.
static BigUInt one()
Returns the multiplicative identity.
bool try_set_bit(std::size_t index)
Sets the bit at the given little-endian bit index when it is in range.
bool operator!=(const BigUInt &other) const
bool try_add_assign(const BigUInt &other)
Adds another fixed-width integer when the result fits.
static Result< BigUInt > try_from_hex(std::string_view hex)
Parses a hexadecimal string, ignoring optional 0x and whitespace.
BigUInt shifted_right(std::size_t bits) const
Returns a copy shifted right by the requested bit count.
bool operator>=(const BigUInt &other) const
void shift_right_one()
Shifts the value right by one bit in place.
std::array< unsigned char, Words *8 > to_bytes_be() const
Serializes the value to a fixed-width big-endian byte array.
bool operator<(const BigUInt &other) const
void mask_bits(std::size_t bits)
Clears all bits above the requested width.
BigUInt shifted_left(std::size_t bits) const
Returns a copy shifted left by the requested bit count.
std::string to_decimal() const
Formats the value as an unsigned decimal string.
void add_assign(const BigUInt &other)
Adds another fixed-width integer in place.
std::string to_hex() const
Formats the value as lowercase hexadecimal without leading zero padding.
bool try_add_small(std::uint32_t value)
Adds a small unsigned value in place when the result fits.
static constexpr bool kAvailable
Opaque scalar storage compatible with secp256k1-zkp internal scalar storage.
Fixed-width unsigned integer helpers implemented in C for Purify.
int purify_u256_try_narrow_u512(uint64_t out[4], const uint64_t value[8])
void purify_u512_widen_u256(uint64_t out[8], const uint64_t value[4])
void purify_u320_widen_u256(uint64_t out[5], const uint64_t value[4])
void purify_u512_multiply_u256(uint64_t out[8], const uint64_t lhs[4], const uint64_t rhs[4])
int purify_u512_try_divmod_same(uint64_t quotient[8], uint64_t remainder[8], const uint64_t numerator[8], const uint64_t denominator[8])
int purify_u256_try_narrow_u320(uint64_t out[4], const uint64_t value[5])