Skip to main content

zinc_utils/field/
boxed_monty.rs

1use crypto_bigint::BoxedUint;
2use crypto_primitives::{
3    FromWithConfig, IntoWithConfig, PrimeField, crypto_bigint_boxed_monty::BoxedMontyField,
4    crypto_bigint_uint::Uint,
5};
6
7use crate::{
8    from_ref::FromRef, mul_by_scalar::MulByScalar, projectable_to_field::ProjectableToField,
9};
10
11impl MulByScalar<&Self> for BoxedMontyField {
12    #[allow(clippy::arithmetic_side_effects)] // False alert
13    fn mul_by_scalar<const CHECK: bool>(&self, rhs: &Self) -> Option<Self> {
14        // Field operations cannot overflow
15        Some(self * rhs)
16    }
17}
18
19impl FromRef<Self> for BoxedMontyField {
20    fn from_ref(value: &Self) -> Self {
21        value.clone()
22    }
23}
24
25impl<const LIMBS: usize> FromRef<Uint<LIMBS>> for BoxedUint {
26    #[inline]
27    fn from_ref(value: &Uint<LIMBS>) -> Self {
28        value.inner().into()
29    }
30}
31
32impl<T> ProjectableToField<BoxedMontyField> for T
33where
34    BoxedMontyField: for<'a> FromWithConfig<&'a T>,
35{
36    fn prepare_projection(
37        sampled_value: &BoxedMontyField,
38    ) -> impl Fn(&Self) -> BoxedMontyField + Send + Sync + 'static {
39        let config = sampled_value.cfg().clone();
40        move |value: &T| value.into_with_cfg(&config)
41    }
42}
43
44#[cfg(test)]
45#[allow(
46    clippy::arithmetic_side_effects,
47    clippy::cast_possible_truncation,
48    clippy::cast_possible_wrap
49)]
50mod prop_tests {
51    use crypto_bigint::{BoxedUint, U256};
52    use crypto_primitives::{
53        FromWithConfig, IntoWithConfig, PrimeField, crypto_bigint_boxed_monty::BoxedMontyField,
54    };
55    use proptest::prelude::*;
56
57    const MODULUS: &str = "00dca94d8a1ecce3b6e8755d8999787d0524d8ca1ea755e7af84fb646fa31f27";
58    type F = BoxedMontyField;
59
60    fn get_dyn_config(hex_modulus: &str) -> <BoxedMontyField as PrimeField>::Config {
61        let modulus =
62            BoxedUint::from_str_radix_vartime(hex_modulus, 16).expect("Invalid modulus hex string");
63        BoxedMontyField::make_cfg(&modulus).expect("Failed to create field config")
64    }
65
66    fn any_u128() -> impl Strategy<Value = u128> {
67        any::<u128>()
68    }
69    fn any_i128() -> impl Strategy<Value = i128> {
70        any::<i128>()
71    }
72    fn any_bool() -> impl Strategy<Value = bool> {
73        any::<bool>()
74    }
75
76    proptest! {
77        #[test]
78        fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
79            let cfg = get_dyn_config(MODULUS);
80            let f: F = x.into_with_cfg(&cfg);
81            let mut acc: F = F::zero_with_cfg(&cfg);
82            for i in 0..128 {
83                if (x >> i) & 1 == 1 { acc += F::from_with_cfg(1u64, &cfg) * F::from_with_cfg(1u64 << i.min(63), &cfg); }
84            }
85            let u = crypto_bigint::Uint::<{ U256::LIMBS }>::from(x);
86            let g2: F = u.into_with_cfg(&cfg);
87            prop_assert_eq!(f, g2);
88        }
89
90        #[test]
91        fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
92            let cfg = get_dyn_config(MODULUS);
93            let f: F = x.into_with_cfg(&cfg);
94            let abs = x.unsigned_abs();
95            let g_abs = abs.into_with_cfg(&cfg);
96            if x < 0 {
97                prop_assert_eq!(f + g_abs, F::zero_with_cfg(&cfg));
98            } else {
99                prop_assert_eq!(f, g_abs);
100            }
101        }
102
103        #[test]
104        fn prop_from_bool_is_identity(b in any_bool()) {
105            let cfg = get_dyn_config(MODULUS);
106            let f: F = b.into_with_cfg(&cfg);
107            prop_assert_eq!(f, if b { F::one_with_cfg(&cfg) } else { F::zero_with_cfg(&cfg) });
108        }
109
110        #[test]
111        fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
112            let cfg = get_dyn_config(MODULUS);
113            let u: crypto_bigint::Uint<{ U256::LIMBS }> = crypto_bigint::Uint::from(x);
114            let g_from_uint: F = u.into_with_cfg(&cfg);
115            let g_direct: F = x.into_with_cfg(&cfg);
116            prop_assert_eq!(g_from_uint, g_direct);
117        }
118
119        #[test]
120        fn prop_from_with_cfg_generic_matches_owned(x in any::<u64>()) {
121            let cfg = get_dyn_config(MODULUS);
122            let a: F = x.into_with_cfg(&cfg);
123            let b: F = F::from_with_cfg(&x, &cfg);
124            prop_assert_eq!(a, b);
125        }
126    }
127}