Skip to main content

zinc_utils/field/
const_monty.rs

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