purify
C++ Purify implementation with native circuit and BPP support
Loading...
Searching...
No Matches
numeric.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Judica, Inc.
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/license/mit/.
4
10#include "purify/numeric.hpp"
11
12#include "field.h"
13
14namespace purify {
15
16namespace {
17
18purify_fe to_core(const FieldElement& value) {
19 return purify_fe{detail::FieldElementAccess::raw(value)};
20}
21
22FieldElement from_core(const purify_fe& value) {
23 return detail::FieldElementAccess::from_raw(value.value);
24}
25
26} // namespace
27
31
35
39
41 FieldElement out;
42 purify_scalar_set_u64(&out.value_, value);
43 return out;
44}
45
47 if (value >= 0) {
48 return from_u64(static_cast<std::uint64_t>(value));
49 }
50 return from_u64(static_cast<std::uint64_t>(-value)).negate();
51}
52
53Result<FieldElement> FieldElement::try_from_bytes32(const std::array<unsigned char, 32>& bytes) {
54 FieldElement out;
55 int overflow = 0;
56 purify_scalar_set_b32(&out.value_, bytes.data(), &overflow);
57 if (overflow != 0) {
58 return unexpected_error(ErrorCode::RangeViolation, "FieldElement::try_from_bytes32:out_of_range");
59 }
60 return out;
61}
62
63FieldElement FieldElement::from_bytes32(const std::array<unsigned char, 32>& bytes) {
65 assert(out.has_value() && "FieldElement::from_bytes32() requires a canonical field element");
66 return std::move(*out);
67}
68
72
75 assert(out.has_value() && "FieldElement::from_uint256() requires a canonical field element");
76 return std::move(*out);
77}
78
80 std::array<unsigned char, 32> bytes = to_bytes_be();
81 return UInt256::from_bytes_be(bytes.data(), bytes.size());
82}
83
84std::array<unsigned char, 32> FieldElement::to_bytes_be() const {
85 std::array<unsigned char, 32> bytes{};
86 purify_scalar_get_b32(bytes.data(), &value_);
87 return bytes;
88}
89
90std::array<unsigned char, 32> FieldElement::to_bytes_le() const {
91 std::array<unsigned char, 32> bytes = to_bytes_be();
92 std::reverse(bytes.begin(), bytes.end());
93 return bytes;
94}
95
96std::string FieldElement::to_hex() const {
97 return to_uint256().to_hex();
98}
99
100std::string FieldElement::to_decimal() const {
101 return to_uint256().to_decimal();
102}
103
105 return purify_scalar_is_zero(&value_) != 0;
106}
107
109 return purify_scalar_is_one(&value_) != 0;
110}
111
113 return purify_scalar_is_even(&value_) == 0;
114}
115
117 const purify_fe input = to_core(*this);
118 return purify_fe_is_square(&input) != 0;
119}
120
122 FieldElement out;
123 purify_scalar_negate(&out.value_, &value_);
124 return out;
125}
126
127void FieldElement::conditional_assign(const FieldElement& other, bool flag) {
128 purify_scalar_cmov(&value_, &other.value_, flag ? 1 : 0);
129}
130
132 FieldElement out;
133 purify_scalar_inverse(&out.value_, &value_);
134 return out;
135}
136
138 FieldElement out;
139 purify_scalar_inverse_var(&out.value_, &value_);
140 return out;
141}
142
143std::optional<FieldElement> FieldElement::sqrt() const {
144 const purify_fe input = to_core(*this);
145 purify_fe output{};
146 if (purify_fe_sqrt(&output, &input) == 0) {
147 return std::nullopt;
148 }
149 return from_core(output);
150}
151
152FieldElement FieldElement::pow(const UInt256& exponent) const {
153 const purify_fe input = to_core(*this);
154 purify_fe output{};
155 purify_fe_pow(&output, &input, exponent.limbs.data());
156 return from_core(output);
157}
158
159bool operator==(const FieldElement& lhs, const FieldElement& rhs) {
160 return purify_scalar_eq(&lhs.value_, &rhs.value_) != 0;
161}
162
163bool operator!=(const FieldElement& lhs, const FieldElement& rhs) {
164 return !(lhs == rhs);
165}
166
168 FieldElement out;
169 purify_scalar_add(&out.value_, &lhs.value_, &rhs.value_);
170 return out;
171}
172
174 return lhs + rhs.negate();
175}
176
178 FieldElement out;
179 purify_scalar_mul(&out.value_, &lhs.value_, &rhs.value_);
180 return out;
181}
182
184 const purify_fe input = to_core(value);
185 purify_fe output{};
186 purify_fe_square(&output, &input);
187 return from_core(output);
188}
189
190int legendre_symbol(const FieldElement& value) {
191 const purify_fe input = to_core(value);
192 return purify_fe_legendre_symbol(&input);
193}
194
195} // namespace purify
Purify result carrier that either holds a value or an error.
Definition expected.hpp:64
bool has_value() const noexcept
Definition expected.hpp:170
Field element modulo the backend scalar field used by this implementation.
Definition numeric.hpp:815
bool is_square() const
Returns true when the element is a quadratic residue in the field.
Definition numeric.cpp:116
bool is_one() const
Returns true when the element is one.
Definition numeric.cpp:108
std::array< unsigned char, 32 > to_bytes_le() const
Serializes the field element in little-endian form.
Definition numeric.cpp:90
static FieldElement from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a 32-byte big-endian field element.
Definition numeric.cpp:63
static FieldElement from_uint256(const UInt256 &value)
Converts a 256-bit unsigned integer into the scalar field representation.
Definition numeric.cpp:73
FieldElement inverse() const
Returns the multiplicative inverse modulo the field prime using the faster variable-time backend.
Definition numeric.cpp:137
FieldElement pow(const UInt256 &exponent) const
Raises the element to an unsigned exponent via square-and-multiply.
Definition numeric.cpp:152
bool is_odd() const
Returns true when the canonical representative is odd.
Definition numeric.cpp:112
static FieldElement from_u64(std::uint64_t value)
Constructs a field element from an unsigned 64-bit integer.
Definition numeric.cpp:40
std::string to_hex() const
Formats the field element as lowercase hexadecimal.
Definition numeric.cpp:96
static Result< FieldElement > try_from_uint256(const UInt256 &value)
Converts a canonical 256-bit unsigned integer into the scalar field representation.
Definition numeric.cpp:69
std::optional< FieldElement > sqrt() const
Computes a square root when one exists, otherwise returns std::nullopt.
Definition numeric.cpp:143
std::string to_decimal() const
Formats the field element as an unsigned decimal string.
Definition numeric.cpp:100
static FieldElement one()
Returns the multiplicative identity of the scalar field.
Definition numeric.cpp:36
void conditional_assign(const FieldElement &other, bool flag)
Conditionally assigns other into *this when flag is true.
Definition numeric.cpp:127
static Result< FieldElement > try_from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a canonical 32-byte big-endian field element.
Definition numeric.cpp:53
FieldElement negate() const
Returns the additive inverse modulo the field prime.
Definition numeric.cpp:121
static FieldElement from_int(std::int64_t value)
Constructs a field element from a signed integer, reducing negatives modulo the field.
Definition numeric.cpp:46
UInt256 to_uint256() const
Exports the field element as a canonical 256-bit unsigned integer.
Definition numeric.cpp:79
bool is_zero() const
Returns true when the element is zero.
Definition numeric.cpp:104
static FieldElement zero()
Returns the additive identity of the scalar field.
Definition numeric.cpp:32
std::array< unsigned char, 32 > to_bytes_be() const
Serializes the field element in big-endian form.
Definition numeric.cpp:84
FieldElement inverse_consttime() const
Returns the multiplicative inverse modulo the field prime in constant time.
Definition numeric.cpp:131
int purify_fe_is_square(const purify_fe *value)
Definition field.c:139
int purify_fe_legendre_symbol(const purify_fe *value)
Definition field.c:154
int purify_fe_sqrt(purify_fe *out, const purify_fe *value)
Definition field.c:161
void purify_fe_pow(purify_fe *out, const purify_fe *value, const uint64_t exponent[4])
Definition field.c:122
void purify_fe_square(purify_fe *out, const purify_fe *value)
Definition field.c:118
Definition api.hpp:21
bool operator!=(const FieldElement &lhs, const FieldElement &rhs)
Definition numeric.cpp:163
constexpr Unexpected< Error > unexpected_error(ErrorCode code, const char *context=nullptr)
Constructs an unexpected Error value from a machine-readable code.
Definition error.hpp:293
Expr operator*(const Expr &expr, const FieldElement &scalar)
Definition expr.cpp:311
FieldElement square(const FieldElement &value)
Squares a field element.
Definition numeric.cpp:183
bool operator==(const Expr &lhs, const Expr &rhs)
Definition expr.cpp:335
Expr operator-(const Expr &lhs, const Expr &rhs)
Definition expr.cpp:295
int legendre_symbol(const FieldElement &value)
Returns 0 for zero, 1 for quadratic residues, and -1 for non-residues.
Definition numeric.cpp:190
Bytes operator+(Bytes lhs, const Bytes &rhs)
Concatenates two byte vectors.
Definition curve.cpp:167
Fixed-width integer and field arithmetic helpers used throughout Purify.
void purify_scalar_mul(purify_scalar *out, const purify_scalar *lhs, const purify_scalar *rhs)
Multiplies two scalars modulo the backend field.
int purify_scalar_is_zero(const purify_scalar *value)
Returns nonzero when the scalar is zero.
int purify_scalar_add(purify_scalar *out, const purify_scalar *lhs, const purify_scalar *rhs)
Adds two scalars modulo the backend field.
void purify_scalar_set_int(purify_scalar *out, unsigned int value)
Initializes a scalar from an unsigned integer.
void purify_scalar_cmov(purify_scalar *dst, const purify_scalar *src, int flag)
Conditionally assigns src into dst when flag is nonzero.
int purify_scalar_is_even(const purify_scalar *value)
Returns nonzero when the scalar is even.
int purify_scalar_is_one(const purify_scalar *value)
Returns nonzero when the scalar is one.
void purify_scalar_inverse(purify_scalar *out, const purify_scalar *value)
Computes the multiplicative inverse of a scalar in constant time.
void purify_scalar_inverse_var(purify_scalar *out, const purify_scalar *value)
Computes the multiplicative inverse of a scalar.
int purify_scalar_eq(const purify_scalar *lhs, const purify_scalar *rhs)
Returns nonzero when two scalars are equal.
void purify_scalar_negate(purify_scalar *out, const purify_scalar *value)
Computes the additive inverse of a scalar.
void purify_scalar_set_u64(purify_scalar *out, uint64_t value)
Initializes a scalar from a 64-bit unsigned integer.
void purify_scalar_get_b32(unsigned char output32[32], const purify_scalar *value)
Serializes a scalar as 32 big-endian bytes.
void purify_scalar_set_b32(purify_scalar *out, const unsigned char input32[32], int *overflow)
Parses a big-endian 32-byte scalar.
static BigUInt from_bytes_be(const unsigned char *data, std::size_t size)
Parses a big-endian byte string into the fixed-width integer.
Definition numeric.hpp:235
std::array< std::uint64_t, Words > limbs
Definition numeric.hpp:201
std::array< unsigned char, Words *8 > to_bytes_be() const
Serializes the value to a fixed-width big-endian byte array.
Definition numeric.hpp:621
std::string to_decimal() const
Formats the value as an unsigned decimal string.
Definition numeric.hpp:658
std::string to_hex() const
Formats the value as lowercase hexadecimal without leading zero padding.
Definition numeric.hpp:638
purify_scalar value
Definition field.h:17