acir_field/
field_element.rs

1use ark_ff::PrimeField;
2use ark_ff::Zero;
3use num_bigint::BigUint;
4use serde::{Deserialize, Serialize};
5use std::borrow::Cow;
6use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
7
8use crate::AcirField;
9
10/// The value 2^127, which represents the boundary between positive and negative
11/// values in i128 representation. Values greater this are treated as negative when
12/// converting to signed integers.
13const I128_SIGN_BOUNDARY: u128 = 1_u128 << 127;
14
15// XXX: Include a trait-based design with field-specific implementations.
16#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct FieldElement<F: PrimeField>(F);
18
19impl<F: PrimeField> std::fmt::Display for FieldElement<F> {
20    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21        // First check if the number is zero
22        //
23        let number = BigUint::from_bytes_be(&self.to_be_bytes());
24        if number == BigUint::zero() {
25            return write!(f, "0");
26        }
27        // Check if the negative version is smaller to represent
28        //
29        let minus_number = BigUint::from_bytes_be(&(self.neg()).to_be_bytes());
30        let (smaller_repr, is_negative) =
31            if minus_number.to_string().len() < number.to_string().len() {
32                (minus_number, true)
33            } else {
34                (number, false)
35            };
36        if is_negative {
37            write!(f, "-")?;
38        }
39
40        write!(f, "{smaller_repr}")
41    }
42}
43
44impl<F: PrimeField> std::fmt::Debug for FieldElement<F> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        std::fmt::Display::fmt(self, f)
47    }
48}
49
50impl<F: PrimeField> From<i128> for FieldElement<F> {
51    fn from(a: i128) -> FieldElement<F> {
52        // Optimized: Convert directly without string conversion
53        if a >= 0 {
54            // Positive case: convert via u128
55            FieldElement(F::from(a as u128))
56        } else {
57            // Negative case: handle i128::MIN specially to avoid overflow
58            let abs_value = a.wrapping_neg() as u128;
59            FieldElement(-F::from(abs_value))
60        }
61    }
62}
63
64impl<F: PrimeField> From<i64> for FieldElement<F> {
65    fn from(a: i64) -> Self {
66        // Optimized: Convert directly without string conversion
67        if a >= 0 {
68            FieldElement(F::from(a as u64))
69        } else {
70            // Negative case: handle i64::MIN specially to avoid overflow
71            let abs_value = a.wrapping_neg() as u64;
72            FieldElement(-F::from(abs_value))
73        }
74    }
75}
76
77impl<F: PrimeField> From<i32> for FieldElement<F> {
78    fn from(a: i32) -> Self {
79        // Optimized: Convert directly without string conversion
80        if a >= 0 {
81            FieldElement(F::from(a as u32))
82        } else {
83            // Negative case: handle i32::MIN specially to avoid overflow
84            let abs_value = a.wrapping_neg() as u32;
85            FieldElement(-F::from(abs_value))
86        }
87    }
88}
89
90impl<F: PrimeField> From<i16> for FieldElement<F> {
91    fn from(a: i16) -> Self {
92        // Optimized: Convert directly without string conversion
93        if a >= 0 {
94            FieldElement(F::from(a as u16))
95        } else {
96            // Negative case: handle i16::MIN specially to avoid overflow
97            let abs_value = a.wrapping_neg() as u16;
98            FieldElement(-F::from(abs_value))
99        }
100    }
101}
102
103impl<F: PrimeField> From<i8> for FieldElement<F> {
104    fn from(a: i8) -> Self {
105        // Optimized: Convert directly without string conversion
106        if a >= 0 {
107            FieldElement(F::from(a as u8))
108        } else {
109            // Negative case: handle i8::MIN specially to avoid overflow
110            let abs_value = a.wrapping_neg() as u8;
111            FieldElement(-F::from(abs_value))
112        }
113    }
114}
115
116impl<T: PrimeField> Serialize for FieldElement<T> {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: serde::Serializer,
120    {
121        self.to_be_bytes().serialize(serializer)
122    }
123}
124
125impl<'de, T: PrimeField> Deserialize<'de> for FieldElement<T> {
126    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127    where
128        D: serde::Deserializer<'de>,
129    {
130        let s: Cow<'de, [u8]> = Deserialize::deserialize(deserializer)?;
131        Ok(Self::from_be_bytes_reduce(&s))
132    }
133}
134
135impl<F: PrimeField> From<u128> for FieldElement<F> {
136    fn from(a: u128) -> FieldElement<F> {
137        FieldElement(F::from(a))
138    }
139}
140
141impl<F: PrimeField> From<usize> for FieldElement<F> {
142    fn from(a: usize) -> FieldElement<F> {
143        FieldElement::from(a as u64)
144    }
145}
146
147impl<F: PrimeField> From<u64> for FieldElement<F> {
148    fn from(a: u64) -> FieldElement<F> {
149        FieldElement(F::from(a))
150    }
151}
152
153impl<F: PrimeField> From<u32> for FieldElement<F> {
154    fn from(a: u32) -> FieldElement<F> {
155        FieldElement(F::from(a))
156    }
157}
158
159impl<F: PrimeField> From<u16> for FieldElement<F> {
160    fn from(a: u16) -> FieldElement<F> {
161        FieldElement(F::from(a))
162    }
163}
164
165impl<F: PrimeField> From<u8> for FieldElement<F> {
166    fn from(a: u8) -> FieldElement<F> {
167        FieldElement(F::from(a))
168    }
169}
170
171impl<F: PrimeField> From<bool> for FieldElement<F> {
172    fn from(boolean: bool) -> FieldElement<F> {
173        if boolean { FieldElement::one() } else { FieldElement::zero() }
174    }
175}
176
177impl<F: PrimeField> TryFrom<FieldElement<F>> for u128 {
178    type Error = ();
179
180    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
181        value.try_into_u128().ok_or(())
182    }
183}
184
185impl<F: PrimeField> TryFrom<FieldElement<F>> for u64 {
186    type Error = ();
187
188    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
189        value.try_to_u64().ok_or(())
190    }
191}
192
193impl<F: PrimeField> TryFrom<FieldElement<F>> for u32 {
194    type Error = ();
195
196    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
197        value.try_to_u32().ok_or(())
198    }
199}
200
201impl<F: PrimeField> TryFrom<FieldElement<F>> for u16 {
202    type Error = ();
203
204    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
205        value.try_to_u32().and_then(|x| x.try_into().ok()).ok_or(())
206    }
207}
208
209impl<F: PrimeField> TryFrom<FieldElement<F>> for u8 {
210    type Error = ();
211
212    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
213        value.try_to_u32().and_then(|x| x.try_into().ok()).ok_or(())
214    }
215}
216
217impl<F: PrimeField> TryFrom<FieldElement<F>> for i128 {
218    type Error = ();
219
220    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
221        value.try_into_i128().ok_or(())
222    }
223}
224
225impl<F: PrimeField> TryFrom<FieldElement<F>> for i64 {
226    type Error = ();
227
228    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
229        value.try_into_i128().and_then(|x| x.try_into().ok()).ok_or(())
230    }
231}
232
233impl<F: PrimeField> TryFrom<FieldElement<F>> for i32 {
234    type Error = ();
235
236    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
237        value.try_into_i128().and_then(|x| x.try_into().ok()).ok_or(())
238    }
239}
240
241impl<F: PrimeField> TryFrom<FieldElement<F>> for i16 {
242    type Error = ();
243
244    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
245        value.try_into_i128().and_then(|x| x.try_into().ok()).ok_or(())
246    }
247}
248
249impl<F: PrimeField> TryFrom<FieldElement<F>> for i8 {
250    type Error = ();
251
252    fn try_from(value: FieldElement<F>) -> Result<Self, Self::Error> {
253        value.try_into_i128().and_then(|x| x.try_into().ok()).ok_or(())
254    }
255}
256
257impl<F: PrimeField> From<FieldElement<F>> for bool {
258    fn from(field: FieldElement<F>) -> bool {
259        !field.is_zero()
260    }
261}
262
263impl<F: PrimeField> FieldElement<F> {
264    /// Constructs a `FieldElement` from the underlying prime field representation.
265    ///
266    /// This wraps an `ark_ff::PrimeField` element into a `FieldElement`.
267    pub fn from_repr(field: F) -> Self {
268        Self(field)
269    }
270
271    /// Extracts the underlying prime field representation.
272    ///
273    /// This returns the wrapped `ark_ff::PrimeField` element.
274    pub fn into_repr(self) -> F {
275        self.0
276    }
277
278    /// Returns true if this field element can be represented as a u128.
279    ///
280    /// A field element fits in u128 if it requires at most 128 bits to represent,
281    /// i.e., if its value is in the range [0, 2^128 - 1].
282    pub fn fits_in_u128(&self) -> bool {
283        self.num_bits() <= 128
284    }
285
286    /// Returns true if this field element can be represented as an i128.
287    ///
288    /// An i128 can represent values in the range [i128::MIN, i128::MAX], which corresponds
289    /// to field elements in [0, 2^127 - 1] (positive) and [p - 2^127, p - 1] (negative),
290    /// where p is the field modulus. Note that 2^127 itself cannot be represented as i128
291    /// since it is not in the negative range.
292    pub fn fits_in_i128(&self) -> bool {
293        let num_bits = u32::min(self.neg().num_bits(), self.num_bits());
294        num_bits <= 127 && self != &FieldElement::from(I128_SIGN_BOUNDARY)
295    }
296
297    /// Returns None, if the string is not a canonical
298    /// representation of a field element; less than the order
299    /// or if the hex string is invalid.
300    /// This method can be used for both hex and decimal representations.
301    pub fn try_from_str(input: &str) -> Option<FieldElement<F>> {
302        if input.contains('x') {
303            return FieldElement::from_hex(input);
304        }
305
306        let fr = F::from_str(input).ok()?;
307        Some(FieldElement(fr))
308    }
309
310    /// Assume this field element holds a signed integer of the given `bit_size` and format
311    /// it as a string. The range of valid values for this field element is `0..2^bit_size`
312    /// with `0..2^(bit_size - 1)` representing positive values and `2^(bit_size - 1)..2^bit_size`
313    /// representing negative values (as is commonly done for signed integers).
314    /// `2^(bit_size - 1)` is the lowest negative value, so for example if bit_size is 8 then
315    /// `0..127` map to `0..127`, `128` maps to `-128`, `129` maps to `-127` and `255` maps to `-1`.
316    /// If `self` falls outside of the valid range it's formatted as-is.
317    pub fn to_string_as_signed_integer(self, bit_size: u32) -> String {
318        assert!(bit_size <= 128);
319        if self.num_bits() > bit_size {
320            return self.to_string();
321        }
322
323        // Compute the maximum value that is considered a positive value
324        let max = if bit_size == 128 { i128::MAX as u128 } else { (1 << (bit_size - 1)) - 1 };
325        if self.to_u128() > max {
326            let f = FieldElement::from(2u32).pow(&bit_size.into()) - self;
327            format!("-{f}")
328        } else {
329            self.to_string()
330        }
331    }
332}
333
334impl<F: PrimeField> AcirField for FieldElement<F> {
335    fn one() -> FieldElement<F> {
336        FieldElement(F::one())
337    }
338    fn zero() -> FieldElement<F> {
339        FieldElement(F::zero())
340    }
341
342    fn is_zero(&self) -> bool {
343        self == &Self::zero()
344    }
345    fn is_one(&self) -> bool {
346        self == &Self::one()
347    }
348
349    fn pow(&self, exponent: &Self) -> Self {
350        FieldElement(self.0.pow(exponent.0.into_bigint()))
351    }
352
353    /// Maximum number of bits needed to represent a field element
354    /// This is not the amount of bits being used to represent a field element
355    /// Example, you only need 254 bits to represent a field element in BN256
356    /// But the representation uses 256 bits, so the top two bits are always zero
357    /// This method would return 254
358    fn max_num_bits() -> u32 {
359        F::MODULUS_BIT_SIZE
360    }
361
362    /// Maximum numbers of bytes needed to represent a field element
363    /// We are not guaranteed that the number of bits being used to represent a field element
364    /// will always be divisible by 8. If the case that it is not, we add one to the max number of bytes
365    /// For example, a max bit size of 254 would give a max byte size of 32.
366    fn max_num_bytes() -> u32 {
367        let num_bytes = Self::max_num_bits() / 8;
368        if Self::max_num_bits() % 8 == 0 { num_bytes } else { num_bytes + 1 }
369    }
370
371    fn modulus() -> BigUint {
372        F::MODULUS.into()
373    }
374
375    /// This is the number of bits required to represent this specific field element
376    fn num_bits(&self) -> u32 {
377        let bigint = self.0.into_bigint();
378        let limbs = bigint.as_ref();
379        for (i, &limb) in limbs.iter().enumerate().rev() {
380            if limb != 0 {
381                return (i as u32) * 64 + (64 - limb.leading_zeros());
382            }
383        }
384        0
385    }
386
387    fn to_u128(self) -> u128 {
388        if !self.fits_in_u128() {
389            panic!("field element too large for u128");
390        }
391        let as_bigint = self.0.into_bigint();
392        let limbs = as_bigint.as_ref();
393
394        let mut result = u128::from(limbs[0]);
395        if limbs.len() > 1 {
396            let high_limb = u128::from(limbs[1]);
397            result += high_limb << 64;
398        }
399
400        result
401    }
402
403    fn try_into_u128(self) -> Option<u128> {
404        self.fits_in_u128().then(|| self.to_u128())
405    }
406
407    fn to_i128(self) -> i128 {
408        if !self.fits_in_i128() {
409            panic!("field element too large for i128");
410        }
411        // Negative integers are represented by the range [p + i128::MIN, p) while
412        // positive integers are represented by the range [0, i128::MAX).
413        // We can then differentiate positive from negative values by their MSB.
414        if self.neg().num_bits() < self.num_bits() {
415            let bytes = self.neg().to_be_bytes();
416            i128::from_be_bytes(bytes[16..32].try_into().unwrap()).neg()
417        } else {
418            let bytes = self.to_be_bytes();
419            i128::from_be_bytes(bytes[16..32].try_into().unwrap())
420        }
421    }
422
423    fn try_into_i128(self) -> Option<i128> {
424        self.fits_in_i128().then(|| self.to_i128())
425    }
426
427    fn try_to_u64(&self) -> Option<u64> {
428        (self.num_bits() <= 64).then(|| self.to_u128() as u64)
429    }
430
431    fn try_to_u32(&self) -> Option<u32> {
432        (self.num_bits() <= 32).then(|| self.to_u128() as u32)
433    }
434
435    /// Computes the inverse or returns zero if the inverse does not exist
436    /// Before using this FieldElement, please ensure that this behavior is necessary
437    fn inverse(&self) -> FieldElement<F> {
438        let inv = self.0.inverse().unwrap_or_else(F::zero);
439        FieldElement(inv)
440    }
441
442    fn to_hex(self) -> String {
443        let bytes = self.to_be_bytes();
444        hex::encode(bytes)
445    }
446
447    fn to_short_hex(self) -> String {
448        if self.is_zero() {
449            return "0x00".to_owned();
450        }
451
452        // Work directly with bytes
453        let bytes = self.to_be_bytes();
454
455        // Find the first non-zero byte
456        let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len());
457        let trimmed = &bytes[first_nonzero..];
458
459        // Build the hex string directly
460        // Pre-allocate: "0x" + at least 2 chars per byte
461        let mut result = String::with_capacity(2 + trimmed.len() * 2);
462        result.push_str("0x");
463
464        // Format the first byte - use {:x} to avoid leading zero if byte >= 0x10
465        use std::fmt::Write;
466        write!(&mut result, "{:x}", trimmed[0]).unwrap();
467
468        // Ensure even length by padding if necessary
469        if !result.len().is_multiple_of(2) {
470            // Insert '0' after "0x" to make it even
471            result.insert(2, '0');
472        }
473
474        // Format remaining bytes with padding
475        for byte in &trimmed[1..] {
476            write!(&mut result, "{byte:02x}").unwrap();
477        }
478
479        result
480    }
481
482    fn from_hex(hex_str: &str) -> Option<FieldElement<F>> {
483        let value = hex_str.strip_prefix("0x").unwrap_or(hex_str);
484
485        // Decode directly, handling even length efficiently
486        let hex_as_bytes = if value.len().is_multiple_of(2) {
487            hex::decode(value).ok()?
488        } else {
489            // For odd length, prepend '0' to the string view only for decoding
490            let mut padded = String::with_capacity(value.len() + 1);
491            padded.push('0');
492            padded.push_str(value);
493            hex::decode(padded).ok()?
494        };
495
496        Some(FieldElement::from_be_bytes_reduce(&hex_as_bytes))
497    }
498
499    fn to_be_bytes(self) -> Vec<u8> {
500        let mut bytes = self.to_le_bytes();
501        bytes.reverse();
502        bytes
503    }
504
505    /// Converts the field element to a vector of bytes in little-endian order
506    fn to_le_bytes(self) -> Vec<u8> {
507        let mut bytes = Vec::new();
508        self.0.serialize_uncompressed(&mut bytes).unwrap();
509        bytes
510    }
511
512    /// Converts bytes into a FieldElement and applies a
513    /// reduction if needed.
514    fn from_be_bytes_reduce(bytes: &[u8]) -> FieldElement<F> {
515        FieldElement(F::from_be_bytes_mod_order(bytes))
516    }
517
518    /// Converts bytes in little-endian order into a FieldElement and applies a
519    /// reduction if needed.
520    fn from_le_bytes_reduce(bytes: &[u8]) -> FieldElement<F> {
521        FieldElement(F::from_le_bytes_mod_order(bytes))
522    }
523
524    /// Returns the closest number of bytes to the bits specified
525    /// This method truncates
526    fn fetch_nearest_bytes(&self, num_bits: usize) -> Vec<u8> {
527        fn nearest_bytes(num_bits: usize) -> usize {
528            num_bits.div_ceil(8) * 8
529        }
530
531        let num_bytes = nearest_bytes(num_bits);
532        let num_elements = num_bytes / 8;
533
534        let bytes = self.to_le_bytes();
535
536        bytes[0..num_elements].to_vec()
537    }
538}
539
540impl<F: PrimeField> Neg for FieldElement<F> {
541    type Output = FieldElement<F>;
542
543    fn neg(self) -> Self::Output {
544        FieldElement(-self.0)
545    }
546}
547
548impl<F: PrimeField> Mul for FieldElement<F> {
549    type Output = FieldElement<F>;
550    fn mul(mut self, rhs: FieldElement<F>) -> Self::Output {
551        self.0.mul_assign(&rhs.0);
552        FieldElement(self.0)
553    }
554}
555impl<F: PrimeField> Div for FieldElement<F> {
556    type Output = FieldElement<F>;
557    #[allow(clippy::suspicious_arithmetic_impl)]
558    fn div(self, rhs: FieldElement<F>) -> Self::Output {
559        self * rhs.inverse()
560    }
561}
562impl<F: PrimeField> Add for FieldElement<F> {
563    type Output = FieldElement<F>;
564    fn add(mut self, rhs: FieldElement<F>) -> Self::Output {
565        self.add_assign(rhs);
566        FieldElement(self.0)
567    }
568}
569impl<F: PrimeField> AddAssign for FieldElement<F> {
570    fn add_assign(&mut self, rhs: FieldElement<F>) {
571        self.0.add_assign(&rhs.0);
572    }
573}
574
575impl<F: PrimeField> Sub for FieldElement<F> {
576    type Output = FieldElement<F>;
577    fn sub(mut self, rhs: FieldElement<F>) -> Self::Output {
578        self.sub_assign(rhs);
579        FieldElement(self.0)
580    }
581}
582impl<F: PrimeField> SubAssign for FieldElement<F> {
583    fn sub_assign(&mut self, rhs: FieldElement<F>) {
584        self.0.sub_assign(&rhs.0);
585    }
586}
587
588#[cfg(test)]
589mod tests {
590    use super::{AcirField, FieldElement};
591    use proptest::prelude::*;
592    use std::ops::Neg;
593
594    #[test]
595    fn requires_zero_bit_to_hold_zero() {
596        let field = FieldElement::<ark_bn254::Fr>::zero();
597        assert_eq!(field.num_bits(), 0);
598    }
599
600    #[test]
601    fn requires_one_bit_to_hold_one() {
602        let field = FieldElement::<ark_bn254::Fr>::one();
603        assert_eq!(field.num_bits(), 1);
604    }
605
606    proptest! {
607        #[test]
608        fn num_bits_agrees_with_ilog2(num in 1u128..) {
609            let field = FieldElement::<ark_bn254::Fr>::from(num);
610            prop_assert_eq!(field.num_bits(), num.ilog2() + 1);
611        }
612    }
613
614    #[test]
615    fn test_fits_in_u128() {
616        let field = FieldElement::<ark_bn254::Fr>::from(u128::MAX);
617        assert_eq!(field.num_bits(), 128);
618        assert!(field.fits_in_u128());
619        let big_field = field + FieldElement::one();
620        assert_eq!(big_field.num_bits(), 129);
621        assert!(!big_field.fits_in_u128());
622    }
623
624    #[test]
625    fn test_to_u128_basic() {
626        type F = FieldElement<ark_bn254::Fr>;
627
628        // Test zero
629        assert_eq!(F::zero().to_u128(), 0);
630
631        // Test small values
632        assert_eq!(F::from(1_u128).to_u128(), 1);
633        assert_eq!(F::from(42_u128).to_u128(), 42);
634        assert_eq!(F::from(1000_u128).to_u128(), 1000);
635
636        // Test u128::MAX
637        assert_eq!(F::from(u128::MAX).to_u128(), u128::MAX);
638
639        // Test power of 2 boundaries
640        assert_eq!(F::from(1_u128 << 127).to_u128(), 1_u128 << 127);
641        assert_eq!(F::from((1_u128 << 127) - 1).to_u128(), (1_u128 << 127) - 1);
642    }
643
644    #[test]
645    #[should_panic(expected = "field element too large for u128")]
646    fn test_to_u128_panics_on_overflow() {
647        type F = FieldElement<ark_bn254::Fr>;
648
649        // Create a field element larger than u128::MAX
650        let too_large = F::from(u128::MAX) + F::one();
651        too_large.to_u128(); // Should panic
652    }
653
654    #[test]
655    fn test_try_into_u128() {
656        type F = FieldElement<ark_bn254::Fr>;
657
658        // Valid conversions
659        assert_eq!(F::zero().try_into_u128(), Some(0));
660        assert_eq!(F::from(42_u128).try_into_u128(), Some(42));
661        assert_eq!(F::from(u128::MAX).try_into_u128(), Some(u128::MAX));
662
663        // Invalid conversion
664        let too_large = F::from(u128::MAX) + F::one();
665        assert_eq!(too_large.try_into_u128(), None);
666    }
667
668    #[test]
669    fn test_fits_in_i128() {
670        type F = FieldElement<ark_bn254::Fr>;
671
672        // Positive values that fit
673        assert!(F::zero().fits_in_i128());
674        assert!(F::from(1_i128).fits_in_i128());
675        assert!(F::from(42_i128).fits_in_i128());
676        assert!(F::from(i128::MAX).fits_in_i128());
677
678        // Negative values that fit (except i128::MIN)
679        assert!(F::from(-1_i128).fits_in_i128());
680        assert!(F::from(-42_i128).fits_in_i128());
681        assert!(F::from(i128::MIN + 1).fits_in_i128());
682
683        // Boundary: 2^127 - 1 fits (i128::MAX)
684        assert!(F::from((1_u128 << 127) - 1).fits_in_i128());
685
686        // Boundary: 2^127 does NOT fit (exceeds i128::MAX, not negative)
687        // Note: This also means i128::MIN doesn't fit, as it converts to a field element
688        // that when interpreted as unsigned equals 2^127
689        assert!(!F::from(1_u128 << 127).fits_in_i128());
690        assert!(!F::from(i128::MIN).fits_in_i128());
691
692        // Values that don't fit
693        let too_large = F::from(u128::MAX);
694        assert!(!too_large.fits_in_i128());
695    }
696
697    #[test]
698    fn test_to_i128_positive() {
699        type F = FieldElement<ark_bn254::Fr>;
700
701        // Test positive values
702        assert_eq!(F::zero().to_i128(), 0);
703        assert_eq!(F::from(1_i128).to_i128(), 1);
704        assert_eq!(F::from(42_i128).to_i128(), 42);
705        assert_eq!(F::from(1000_i128).to_i128(), 1000);
706        assert_eq!(F::from(i128::MAX).to_i128(), i128::MAX);
707    }
708
709    #[test]
710    fn test_to_i128_negative() {
711        type F = FieldElement<ark_bn254::Fr>;
712
713        // Test negative values
714        assert_eq!(F::from(-1_i128).to_i128(), -1);
715        assert_eq!(F::from(-42_i128).to_i128(), -42);
716        assert_eq!(F::from(-1000_i128).to_i128(), -1000);
717
718        // Test boundary values
719        assert_eq!(F::from(-i128::MAX).to_i128(), -i128::MAX);
720        assert_eq!(F::from(i128::MIN + 1).to_i128(), i128::MIN + 1);
721
722        // i128::MIN doesn't fit
723    }
724
725    #[test]
726    fn test_to_i128_roundtrip() {
727        type F = FieldElement<ark_bn254::Fr>;
728
729        // Test roundtrip for various values
730        // i128::MIN doesn't fit
731        let test_values =
732            vec![0_i128, 1, -1, 42, -42, i128::MAX, i128::MAX - 1, i128::MIN + 1, -i128::MAX];
733
734        for value in test_values {
735            let field = F::from(value);
736            assert!(field.fits_in_i128(), "Value {value} should fit in i128");
737            assert_eq!(field.to_i128(), value, "Roundtrip failed for {value}");
738        }
739    }
740
741    #[test]
742    #[should_panic(expected = "field element too large for i128")]
743    fn test_to_i128_panics_on_positive_overflow() {
744        type F = FieldElement<ark_bn254::Fr>;
745
746        // 2^127 is too large (exceeds i128::MAX)
747        let too_large = F::from(1_u128 << 127);
748        too_large.to_i128(); // Should panic
749    }
750
751    #[test]
752    #[should_panic(expected = "field element too large for i128")]
753    fn test_to_i128_panics_on_large_value() {
754        type F = FieldElement<ark_bn254::Fr>;
755
756        // Large positive value that doesn't fit
757        let too_large = F::from(u128::MAX);
758        too_large.to_i128(); // Should panic
759    }
760
761    #[test]
762    fn test_try_into_i128() {
763        type F = FieldElement<ark_bn254::Fr>;
764
765        // Valid positive conversions
766        assert_eq!(F::zero().try_into_i128(), Some(0));
767        assert_eq!(F::from(42_i128).try_into_i128(), Some(42));
768        assert_eq!(F::from(i128::MAX).try_into_i128(), Some(i128::MAX));
769        assert_eq!(F::from(-i128::MAX).try_into_i128(), Some(-i128::MAX));
770
771        // Valid negative conversions
772        assert_eq!(F::from(-1_i128).try_into_i128(), Some(-1));
773        assert_eq!(F::from(-42_i128).try_into_i128(), Some(-42));
774        assert_eq!(F::from(i128::MIN + 1).try_into_i128(), Some(i128::MIN + 1));
775        assert_eq!(F::from(i128::MAX - 1).try_into_i128(), Some(i128::MAX - 1));
776        assert_eq!(F::from(1_i128 << 126).try_into_i128(), Some(1_i128 << 126));
777        assert_eq!(F::from(-((1_i128 << 126) - 1)).try_into_i128(), Some(-((1_i128 << 126) - 1)));
778        // Invalid conversions
779        assert_eq!(F::from(1_u128 << 127).try_into_i128(), None);
780        assert_eq!(F::from(u128::MAX).try_into_i128(), None);
781        // i128::MIN doesn't fit due to implementation
782        assert_eq!(F::from(i128::MIN).try_into_i128(), None);
783        // A few other invalid values
784        assert_eq!(F::from((1_u128 << 127) + 1).try_into_i128(), None);
785        assert_eq!(F::from((1_u128 << 127) + 1000).try_into_i128(), None);
786        assert_eq!(F::from(1_u128 << 127).neg().try_into_i128(), None);
787        assert_eq!(F::from((1_u128 << 127) + 1).neg().try_into_i128(), None);
788        assert_eq!(F::from((1_u128 << 127) + 100).try_into_i128(), None);
789        assert_eq!(F::from((1_u128 << 127) + 100).neg().try_into_i128(), None);
790    }
791
792    #[test]
793    fn serialize_fixed_test_vectors() {
794        // Serialized field elements from of 0, -1, -2, -3
795        let hex_strings = vec![
796            "0000000000000000000000000000000000000000000000000000000000000000",
797            "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000",
798            "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff",
799            "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffffe",
800        ];
801
802        for (i, string) in hex_strings.into_iter().enumerate() {
803            let minus_i_field_element = -FieldElement::<ark_bn254::Fr>::from(i as i128);
804            assert_eq!(minus_i_field_element.to_hex(), string);
805        }
806    }
807
808    #[test]
809    fn max_num_bits_smoke() {
810        let max_num_bits_bn254 = FieldElement::<ark_bn254::Fr>::max_num_bits();
811        assert_eq!(max_num_bits_bn254, 254);
812    }
813
814    proptest! {
815        #[test]
816        fn test_endianness_prop(value in any::<u64>()) {
817            let field = FieldElement::<ark_bn254::Fr>::from(value);
818            // Test serialization consistency
819            let le_bytes = field.to_le_bytes();
820            let be_bytes = field.to_be_bytes();
821
822            let mut reversed_le = le_bytes.clone();
823            reversed_le.reverse();
824            prop_assert_eq!(&be_bytes, &reversed_le, "BE bytes should be reverse of LE bytes");
825
826            // Test deserialization consistency
827            let from_le = FieldElement::from_le_bytes_reduce(&le_bytes);
828            let from_be = FieldElement::from_be_bytes_reduce(&be_bytes);
829            prop_assert_eq!(from_le, from_be, "Deserialization should be consistent between LE and BE");
830            prop_assert_eq!(from_le, field, "Deserialized value should match original");
831        }
832    }
833
834    #[test]
835    fn test_endianness() {
836        let field = FieldElement::<ark_bn254::Fr>::from(0x1234_5678_u32);
837        let le_bytes = field.to_le_bytes();
838        let be_bytes = field.to_be_bytes();
839
840        // Check that the bytes are reversed between BE and LE
841        let mut reversed_le = le_bytes.clone();
842        reversed_le.reverse();
843        assert_eq!(&be_bytes, &reversed_le);
844
845        // Verify we can reconstruct the same field element from either byte order
846        let from_le = FieldElement::from_le_bytes_reduce(&le_bytes);
847        let from_be = FieldElement::from_be_bytes_reduce(&be_bytes);
848        assert_eq!(from_le, from_be);
849        assert_eq!(from_le, field);
850
851        // Additional test with a larger number to ensure proper byte handling
852        let large_field = FieldElement::<ark_bn254::Fr>::from(0x0123_4567_89AB_CDEF_u64); // cSpell:disable-line
853        let large_le = large_field.to_le_bytes();
854        let reconstructed = FieldElement::from_le_bytes_reduce(&large_le);
855        assert_eq!(reconstructed, large_field);
856    }
857
858    proptest! {
859        // This currently panics due to the fact that we allow inputs which are greater than the field modulus,
860        // automatically reducing them to fit within the canonical range.
861        #[test]
862        #[should_panic(expected = "serialized field element is not equal to input")]
863        fn recovers_original_hex_string(hex in "[0-9a-f]{64}") {
864            let fe: FieldElement::<ark_bn254::Fr> = FieldElement::from_hex(&hex).expect("should accept any 32 byte hex string");
865            let output_hex = fe.to_hex();
866
867            prop_assert_eq!(hex, output_hex, "serialized field element is not equal to input");
868        }
869
870        #[test]
871        fn accepts_odd_length_hex_strings(hex in "(?:0x)[0-9a-fA-F]+") {
872            // Here we inject a "0" immediately after the "0x" (if it exists) to construct an equivalent
873            // hex string with the opposite parity length.
874            let insert_index = if hex.starts_with("0x") { 2 } else { 0 };
875            let mut opposite_parity_string = hex.clone();
876            opposite_parity_string.insert(insert_index, '0');
877
878            let fe_1: FieldElement::<ark_bn254::Fr> = FieldElement::from_hex(&hex).unwrap();
879            let fe_2: FieldElement::<ark_bn254::Fr> = FieldElement::from_hex(&opposite_parity_string).unwrap();
880
881            prop_assert_eq!(fe_1, fe_2, "equivalent hex strings with opposite parity deserialized to different values");
882        }
883    }
884
885    #[test]
886    fn test_to_hex() {
887        type F = FieldElement<ark_bn254::Fr>;
888        assert_eq!(
889            F::zero().to_hex(),
890            "0000000000000000000000000000000000000000000000000000000000000000"
891        );
892        assert_eq!(
893            F::one().to_hex(),
894            "0000000000000000000000000000000000000000000000000000000000000001"
895        );
896        assert_eq!(
897            F::from(0x123_u128).to_hex(),
898            "0000000000000000000000000000000000000000000000000000000000000123"
899        );
900        assert_eq!(
901            F::from(0x1234_u128).to_hex(),
902            "0000000000000000000000000000000000000000000000000000000000001234"
903        );
904    }
905
906    #[test]
907    fn test_to_short_hex() {
908        type F = FieldElement<ark_bn254::Fr>;
909        assert_eq!(F::zero().to_short_hex(), "0x00");
910        assert_eq!(F::one().to_short_hex(), "0x01");
911        assert_eq!(F::from(0x123_u128).to_short_hex(), "0x0123");
912        assert_eq!(F::from(0x1234_u128).to_short_hex(), "0x1234");
913    }
914
915    #[test]
916    fn to_string_as_signed_integer() {
917        type F = FieldElement<ark_bn254::Fr>;
918        assert_eq!(F::zero().to_string_as_signed_integer(8), "0");
919        assert_eq!(F::one().to_string_as_signed_integer(8), "1");
920        assert_eq!(F::from(127_u128).to_string_as_signed_integer(8), "127");
921        assert_eq!(F::from(128_u128).to_string_as_signed_integer(8), "-128");
922        assert_eq!(F::from(129_u128).to_string_as_signed_integer(8), "-127");
923        assert_eq!(F::from(255_u128).to_string_as_signed_integer(8), "-1");
924        assert_eq!(F::from(32767_u128).to_string_as_signed_integer(16), "32767");
925        assert_eq!(F::from(32768_u128).to_string_as_signed_integer(16), "-32768");
926        assert_eq!(F::from(65535_u128).to_string_as_signed_integer(16), "-1");
927    }
928}