acir_field/
field_element.rs

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