zinc_utils/field/
const_monty.rs1use 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)] fn mul_by_scalar<const CHECK: bool>(&self, rhs: &Self) -> Option<Self> {
17 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 |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 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}