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        fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
141            let f = F::from(x);
142            let mut acc = F::zero();
143            for i in 0..128 {
144                if (x >> i) & 1 == 1 { acc += F::from(1u64) * F::from(1u64 << i.min(63)); }
145            }
146            let u = Uint::<{ U256::LIMBS }>::from(x);
147            let g2: F = F::from(u);
148            prop_assert_eq!(f, g2);
149        }
150
151        #[test]
152        fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
153            let f = F::from(x);
154            let abs = x.unsigned_abs();
155            let g_abs = F::from(abs);
156            if x < 0 {
157                prop_assert_eq!(f + g_abs, F::zero());
158            } else {
159                prop_assert_eq!(f, g_abs);
160            }
161        }
162
163        #[test]
164        fn prop_from_bool_is_identity(b in any_bool()) {
165            let f = F::from(b);
166            prop_assert_eq!(f, if b { F::one() } else { F::zero() });
167        }
168
169        #[test]
170        fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
171            let u: Uint<{ U256::LIMBS }> = Uint::from(x);
172            let g_from_uint: F = u.into();
173            let g_direct: F = F::from(x);
174            prop_assert_eq!(g_from_uint, g_direct);
175        }
176
177        #[test]
178        fn prop_from_ref_generic_matches_owned(x in any::<u64>()) {
179            let a: F = F::from(x);
180            let b: F = F::from(&x);
181            prop_assert_eq!(a, b);
182        }
183
184        #[test]
185        fn prop_inner_ops_match_normal_ops((x, y) in any::<(u128, u128)>()) {
186            let a: F = x.into();
187            let b: F = y.into();
188            prop_assert_eq!(F::add_inner(a.inner(), b.inner(), &()), (a + b).into_inner());
189            prop_assert_eq!(F::sub_inner(a.inner(), b.inner(), &()), (a - b).into_inner());
190
191            let mut res = a;
192            res.mul_assign_by_inner(b.inner());
193            prop_assert_eq!(res, a * b);
194        }
195    }
196}