Skip to main content

zinc_utils/field/
const_monty.rs

1use crypto_bigint::modular::{ConstMontyForm, ConstMontyParams};
2use crypto_primitives::{
3    FixedConfig, Wrapper, crypto_bigint_const_monty::ConstMontyField, crypto_bigint_int::Int,
4    crypto_bigint_uint::Uint,
5};
6
7use crate::{
8    from_ref::FromRef, mul_by_scalar::MulByScalar, projectable_to_field::ProjectableToField,
9};
10
11impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> MulByScalar<ConstMontyField<Mod, LIMBS>>
12    for ConstMontyField<Mod, LIMBS>
13{
14    #[allow(clippy::arithmetic_side_effects)] // False alert
15    fn mul_by_scalar<const CHECK: bool>(
16        self,
17        rhs: &ConstMontyField<Mod, LIMBS>,
18    ) -> Option<ConstMontyField<Mod, LIMBS>> {
19        // Field operations cannot overflow
20        Some(self * rhs)
21    }
22}
23
24macro_rules! impl_from_primitive_ref {
25    ($($t:ty),* $(,)?) => {
26        $(
27            impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> FromRef<$t> for ConstMontyField<Mod, LIMBS> {
28                #![allow(clippy::arithmetic_side_effects)]
29                fn from_ref(value: &$t) -> Self {
30                    Self::from(*value)
31                }
32            }
33        )*
34    };
35}
36impl_from_primitive_ref!(u8, u16, u32, u64, u128);
37
38impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> FromRef<Uint<LIMBS>>
39    for ConstMontyForm<Mod, LIMBS>
40{
41    fn from_ref(value: &Uint<LIMBS>) -> Self {
42        ConstMontyForm::new(value.inner())
43    }
44}
45
46impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize> FromRef<Self>
47    for ConstMontyField<Mod, LIMBS>
48{
49    fn from_ref(value: &Self) -> Self {
50        *value
51    }
52}
53
54// TODO(alex): Can probably be generalized with From/FromRef
55impl<Mod: ConstMontyParams<LIMBS>, const LIMBS: usize, const LIMBS2: usize>
56    ProjectableToField<FixedConfig<ConstMontyField<Mod, LIMBS>>> for Int<LIMBS2>
57{
58    fn prepare_projection(
59        _cfg: &FixedConfig<ConstMontyField<Mod, LIMBS>>,
60        _sampled_value: &ConstMontyField<Mod, LIMBS>,
61    ) -> impl Fn(&Self) -> ConstMontyField<Mod, LIMBS> {
62        // No need to read anything
63        |value: &Int<LIMBS2>| value.into()
64    }
65}
66
67#[cfg(test)]
68#[allow(
69    clippy::arithmetic_side_effects,
70    clippy::cast_possible_truncation,
71    clippy::cast_possible_wrap
72)]
73mod tests {
74    use crate::projectable_to_field::ProjectableToField;
75
76    use super::*;
77
78    use crypto_bigint::{U128, U256, const_monty_params};
79    use crypto_primitives::{crypto_bigint_const_monty::F256, crypto_bigint_int::Int};
80    use num_traits::{One, Zero};
81    use proptest::prelude::*;
82
83    const_monty_params!(
84        ModQ,
85        U256,
86        "00dca94d8a1ecce3b6e8755d8999787d0524d8ca1ea755e7af84fb646fa31f27"
87    );
88    type F = F256<ModQ>;
89
90    #[test]
91    fn prepare_projection_for_int() {
92        // Create a sample field element and an Int value
93        let sampled = F::from(5_u64);
94
95        let cfg = FixedConfig::default();
96        let projection_fn = Int::<{ U128::LIMBS }>::prepare_projection(&cfg, &sampled);
97
98        let int_value = Int::<{ U128::LIMBS }>::from(10_i64);
99        let result = projection_fn(&int_value);
100        assert_eq!(result, F::from(&int_value));
101        assert_eq!(result, F::from(10_u64));
102
103        let int_value = Int::<{ U128::LIMBS }>::from(-7_i64);
104        let result = projection_fn(&int_value);
105        assert_eq!(result, F::from(&int_value));
106        assert_eq!(result + F::from(7_u64), F::zero());
107    }
108
109    fn any_u128() -> impl Strategy<Value = u128> {
110        any::<u128>()
111    }
112    fn any_i128() -> impl Strategy<Value = i128> {
113        any::<i128>()
114    }
115    fn any_bool() -> impl Strategy<Value = bool> {
116        any::<bool>()
117    }
118
119    proptest! {
120        #[test]
121        #[cfg_attr(miri, ignore)] // long running
122        fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
123            let f = F::from(x);
124            let mut acc = F::zero();
125            for i in 0..128 {
126                if (x >> i) & 1 == 1 { acc += F::from(1u64) * F::from(1u64 << i.min(63)); }
127            }
128            let u = Uint::<{ U256::LIMBS }>::from(x);
129            let g2: F = F::from(u);
130            prop_assert_eq!(f, g2);
131        }
132
133        #[test]
134        #[cfg_attr(miri, ignore)] // long running
135        fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
136            let f = F::from(x);
137            let abs = x.unsigned_abs();
138            let g_abs = F::from(abs);
139            if x < 0 {
140                prop_assert_eq!(f + g_abs, F::zero());
141            } else {
142                prop_assert_eq!(f, g_abs);
143            }
144        }
145
146        #[test]
147        #[cfg_attr(miri, ignore)] // long running
148        fn prop_from_bool_is_identity(b in any_bool()) {
149            let f = F::from(b);
150            prop_assert_eq!(f, if b { F::one() } else { F::zero() });
151        }
152
153        #[test]
154        #[cfg_attr(miri, ignore)] // long running
155        fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
156            let u: Uint<{ U256::LIMBS }> = Uint::from(x);
157            let g_from_uint: F = u.into();
158            let g_direct: F = F::from(x);
159            prop_assert_eq!(g_from_uint, g_direct);
160        }
161
162        #[test]
163        #[cfg_attr(miri, ignore)] // long running
164        fn prop_from_ref_generic_matches_owned(x in any::<u64>()) {
165            let a: F = F::from(x);
166            let b: F = F::from(&x);
167            prop_assert_eq!(a, b);
168        }
169    }
170}