Skip to main content

zinc_utils/field/
monty.rs

1use crypto_primitives::{
2    ProjectElementWithConfig, SetConfig,
3    crypto_bigint_monty::{MontyField, MontyFieldElement},
4};
5
6use crate::{from_ref::FromRef, projectable_to_field::ProjectableToField};
7
8impl<const LIMBS: usize> FromRef<Self> for MontyFieldElement<LIMBS> {
9    fn from_ref(value: &Self) -> Self {
10        *value
11    }
12}
13
14impl<T, const LIMBS: usize> ProjectableToField<MontyField<LIMBS>> for T
15where
16    MontyField<LIMBS>: ProjectElementWithConfig<T>,
17{
18    fn prepare_projection(
19        cfg: &MontyField<LIMBS>,
20        _sampled_value: &<MontyField<LIMBS> as SetConfig>::Element,
21    ) -> impl Fn(&Self) -> <MontyField<LIMBS> as SetConfig>::Element {
22        let cfg = *cfg;
23        move |value: &T| cfg.project(value)
24    }
25}
26
27#[cfg(test)]
28#[allow(
29    clippy::arithmetic_side_effects,
30    clippy::cast_possible_truncation,
31    clippy::cast_possible_wrap
32)]
33mod prop_tests {
34    use crypto_bigint::U256;
35    use crypto_primitives::{
36        BaseFieldConfig, ProjectElementWithConfig, SemiringConfig, crypto_bigint_monty::MontyField,
37        crypto_bigint_uint::Uint,
38    };
39    use proptest::prelude::*;
40    use std::str::FromStr;
41
42    const LIMBS: usize = 4;
43    const MODULUS: &str = "00dca94d8a1ecce3b6e8755d8999787d0524d8ca1ea755e7af84fb646fa31f27";
44
45    fn get_dyn_config(hex_modulus: &str) -> MontyField<LIMBS> {
46        let modulus =
47            Uint::from_str(&format!("0x{hex_modulus}")).expect("Invalid modulus hex string");
48        MontyField::new(&modulus).expect("Failed to create field config")
49    }
50
51    fn any_u128() -> impl Strategy<Value = u128> {
52        any::<u128>()
53    }
54    fn any_i128() -> impl Strategy<Value = i128> {
55        any::<i128>()
56    }
57    fn any_bool() -> impl Strategy<Value = bool> {
58        any::<bool>()
59    }
60
61    proptest! {
62        #[test]
63        #[cfg_attr(miri, ignore)] // long running
64        fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
65            let cfg = get_dyn_config(MODULUS);
66            let f = cfg.project(&x);
67            let mut acc = cfg.zero();
68            for i in 0..128 {
69                if (x >> i) & 1 == 1 {
70                    let bit = cfg.mul(&cfg.project(&1u64), &cfg.project(&(1u64 << i.min(63))));
71                    cfg.add_assign(&mut acc, &bit);
72                }
73            }
74            let u = Uint::<{ U256::LIMBS }>::from(x);
75            let g2 = cfg.project(&u);
76            prop_assert_eq!(f, g2);
77        }
78
79        #[test]
80        #[cfg_attr(miri, ignore)] // long running
81        fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
82            let cfg = get_dyn_config(MODULUS);
83            let f = cfg.project(&x);
84            let abs = x.unsigned_abs();
85            let g_abs = cfg.project(&abs);
86            if x < 0 {
87                prop_assert_eq!(cfg.add(&f, &g_abs), cfg.zero());
88            } else {
89                prop_assert_eq!(f, g_abs);
90            }
91        }
92
93        #[test]
94        #[cfg_attr(miri, ignore)] // long running
95        fn prop_from_bool_is_identity(b in any_bool()) {
96            let cfg = get_dyn_config(MODULUS);
97            let f = cfg.project(&b);
98            prop_assert_eq!(f, if b { cfg.one() } else { cfg.zero() });
99        }
100
101        #[test]
102        #[cfg_attr(miri, ignore)] // long running
103        fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
104            let cfg = get_dyn_config(MODULUS);
105            let u: Uint<LIMBS> = Uint::from(x);
106            let g_from_uint = cfg.project(&u);
107            let g_direct = cfg.project(&x);
108            prop_assert_eq!(g_from_uint, g_direct);
109        }
110    }
111}