1use core::ops::{Add, Mul, Neg, Sub};
5use std::fmt;
6
7use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom};
8use crypto_bigint::{
9 modular::{ConstMontyForm, ConstPrimeMontyParams},
10 NonZero, RandomMod, Uint,
11};
12use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct FpElement<MOD, const LIMBS: usize>
22where
23 MOD: ConstPrimeMontyParams<LIMBS>,
24{
25 pub(crate) value: ConstMontyForm<MOD, LIMBS>,
26}
27
28impl<MOD, const LIMBS: usize> FpElement<MOD, LIMBS>
33where
34 MOD: ConstPrimeMontyParams<LIMBS>,
35{
36 pub fn from_uint(x: Uint<LIMBS>) -> Self {
46 Self {
47 value: ConstMontyForm::<MOD, LIMBS>::new(&x),
48 }
49 }
50
51 pub fn from_words(words: [u64; LIMBS]) -> Self {
61 Self::from_uint(Uint::<LIMBS>::from_words(words))
62 }
63
64 pub fn from_limbs(limbs: &[u64]) -> Self {
74 assert_eq!(limbs.len(), LIMBS, "wrong number of limbs");
75 let mut words = [0u64; LIMBS];
76 words.copy_from_slice(limbs);
77 Self::from_words(words)
78 }
79
80 pub fn from_u64(val: u64) -> Self {
90 Self::from_uint(Uint::<LIMBS>::from_u64(val))
91 }
92
93 pub fn as_uint(&self) -> Uint<LIMBS> {
103 self.value.retrieve()
104 }
105
106 pub fn as_limbs(&self) -> [u64; LIMBS] {
116 self.value.retrieve().to_words()
117 }
118
119 pub fn to_montgomery(&self) -> Uint<LIMBS> {
130 self.value.to_montgomery()
131 }
132
133 pub fn from_montgomery(mont: Uint<LIMBS>) -> Self {
144 Self {
145 value: ConstMontyForm::<MOD, LIMBS>::from_montgomery(mont),
146 }
147 }
148}
149
150impl<MOD, const LIMBS: usize> Default for FpElement<MOD, LIMBS>
155where
156 MOD: ConstPrimeMontyParams<LIMBS>,
157{
158 fn default() -> Self {
159 Self::zero()
160 }
161}
162
163impl<MOD, const LIMBS: usize> ConditionallySelectable for FpElement<MOD, LIMBS>
164where
165 MOD: ConstPrimeMontyParams<LIMBS>,
166{
167 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
168 Self {
169 value: ConstMontyForm::conditional_select(&a.value, &b.value, choice),
170 }
171 }
172
173 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
174 self.value.conditional_assign(&other.value, choice)
175 }
176
177 fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
178 ConstMontyForm::conditional_swap(&mut a.value, &mut b.value, choice)
179 }
180}
181
182impl<MOD, const LIMBS: usize> ConstantTimeEq for FpElement<MOD, LIMBS>
183where
184 MOD: ConstPrimeMontyParams<LIMBS>,
185{
186 fn ct_eq(&self, other: &Self) -> Choice {
187 ConstMontyForm::ct_eq(&self.value, &other.value)
188 }
189
190 fn ct_ne(&self, other: &Self) -> Choice {
191 ConstMontyForm::ct_ne(&self.value, &other.value)
192 }
193}
194
195impl<MOD, const LIMBS: usize> Add for FpElement<MOD, LIMBS>
200where
201 MOD: ConstPrimeMontyParams<LIMBS>,
202{
203 type Output = Self;
204 fn add(self, rhs: Self) -> Self {
205 Self {
206 value: self.value + rhs.value,
207 }
208 }
209}
210
211impl<MOD, const LIMBS: usize> Sub for FpElement<MOD, LIMBS>
212where
213 MOD: ConstPrimeMontyParams<LIMBS>,
214{
215 type Output = Self;
216 fn sub(self, rhs: Self) -> Self {
217 Self {
218 value: self.value - rhs.value,
219 }
220 }
221}
222
223impl<MOD, const LIMBS: usize> Mul for FpElement<MOD, LIMBS>
224where
225 MOD: ConstPrimeMontyParams<LIMBS>,
226{
227 type Output = Self;
228 fn mul(self, rhs: Self) -> Self {
229 Self {
230 value: self.value * rhs.value,
231 }
232 }
233}
234
235impl<MOD, const LIMBS: usize> Neg for FpElement<MOD, LIMBS>
236where
237 MOD: ConstPrimeMontyParams<LIMBS>,
238{
239 type Output = Self;
240 fn neg(self) -> Self {
241 Self { value: -self.value }
242 }
243}
244
245impl<MOD, const LIMBS: usize> FieldOps for FpElement<MOD, LIMBS>
250where
251 MOD: ConstPrimeMontyParams<LIMBS>,
252{
253 fn zero() -> Self {
254 Self {
255 value: ConstMontyForm::<MOD, LIMBS>::ZERO,
256 }
257 }
258 fn one() -> Self {
259 Self {
260 value: ConstMontyForm::<MOD, LIMBS>::ONE,
261 }
262 }
263
264 fn from_u64(x: u64) -> Self {
265 Self::from_u64(x)
266 }
267
268 fn is_zero(&self) -> Choice {
269 Self::ct_eq(self, &Self::zero())
270 }
271 fn is_one(&self) -> Choice {
272 Self::ct_eq(self, &Self::one())
273 }
274
275 fn negate(&self) -> Self {
276 Self { value: -self.value }
277 }
278 fn add(&self, rhs: &Self) -> Self {
279 Self {
280 value: self.value + rhs.value,
281 }
282 }
283 fn sub(&self, rhs: &Self) -> Self {
284 Self {
285 value: self.value - rhs.value,
286 }
287 }
288 fn mul(&self, rhs: &Self) -> Self {
289 Self {
290 value: self.value * rhs.value,
291 }
292 }
293 fn square(&self) -> Self {
294 Self {
295 value: self.value.square(),
296 }
297 }
298 fn double(&self) -> Self {
299 Self {
300 value: self.value.double(),
301 }
302 }
303
304 fn invert(&self) -> CtOption<Self> {
305 self.value.invert().map(|inv| Self { value: inv }).into()
306 }
307
308 fn frobenius(&self) -> Self {
309 *self
310 }
311 fn norm(&self) -> Self {
312 *self
313 }
314 fn trace(&self) -> Self {
315 *self
316 }
317
318 fn sqrt(&self) -> CtOption<Self> {
319 self.value.sqrt().map(|sqrt| Self { value: sqrt }).into()
320 }
321
322 fn legendre(&self) -> i8 {
323 i8::from(self.value.jacobi_symbol())
324 }
325
326 fn characteristic() -> Vec<u64> {
327 let minus_one = ConstMontyForm::<MOD, LIMBS>::ZERO - ConstMontyForm::<MOD, LIMBS>::ONE;
328 let p_minus_1: Uint<LIMBS> = minus_one.retrieve();
329 let p = p_minus_1.wrapping_add(&Uint::<LIMBS>::ONE);
330 p.to_words().to_vec()
331 }
332
333 fn degree() -> u32 {
334 1
335 }
336}
337
338impl<MOD, const LIMBS: usize> fmt::Display for FpElement<MOD, LIMBS>
343where
344 MOD: ConstPrimeMontyParams<LIMBS>,
345{
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 let val = self.as_uint();
348 if LIMBS == 1 {
349 write!(f, "{}", val.to_words()[0])
350 } else {
351 write!(f, "0x{val:x}")
352 }
353 }
354}
355
356impl<MOD, const LIMBS: usize> FieldRandom for FpElement<MOD, LIMBS>
361where
362 MOD: ConstPrimeMontyParams<LIMBS>,
363{
364 fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self {
366 let minus_one = ConstMontyForm::<MOD, LIMBS>::ZERO - ConstMontyForm::<MOD, LIMBS>::ONE;
367 let p_minus_1: Uint<LIMBS> = minus_one.retrieve();
368 let p = p_minus_1.wrapping_add(&Uint::<LIMBS>::ONE);
369 let modulus = NonZero::new(p).expect("prime modulus must be nonzero");
370 let val = Uint::<LIMBS>::random_mod_vartime(rng, &modulus);
371 Self::from_uint(val)
372 }
373}
374
375impl<MOD, const LIMBS: usize> FieldFromRepr for FpElement<MOD, LIMBS>
376where
377 MOD: ConstPrimeMontyParams<LIMBS>,
378{
379 type Repr = Uint<LIMBS>;
380
381 fn from_repr(x: Self::Repr) -> Self {
382 Self::from_uint(x)
383 }
384}