zinc_utils/field/
boxed_monty.rs1use crypto_bigint::BoxedUint;
2use crypto_primitives::{
3 FromWithConfig, IntoWithConfig, PrimeField, crypto_bigint_boxed_monty::BoxedMontyField,
4 crypto_bigint_uint::Uint,
5};
6
7use crate::{
8 from_ref::FromRef, mul_by_scalar::MulByScalar, projectable_to_field::ProjectableToField,
9};
10
11impl MulByScalar<&Self> for BoxedMontyField {
12 #[allow(clippy::arithmetic_side_effects)] fn mul_by_scalar<const CHECK: bool>(&self, rhs: &Self) -> Option<Self> {
14 Some(self * rhs)
16 }
17}
18
19impl FromRef<Self> for BoxedMontyField {
20 fn from_ref(value: &Self) -> Self {
21 value.clone()
22 }
23}
24
25impl<const LIMBS: usize> FromRef<Uint<LIMBS>> for BoxedUint {
26 #[inline]
27 fn from_ref(value: &Uint<LIMBS>) -> Self {
28 value.inner().into()
29 }
30}
31
32impl<T> ProjectableToField<BoxedMontyField> for T
33where
34 BoxedMontyField: for<'a> FromWithConfig<&'a T>,
35{
36 fn prepare_projection(
37 sampled_value: &BoxedMontyField,
38 ) -> impl Fn(&Self) -> BoxedMontyField + Send + Sync + 'static {
39 let config = sampled_value.cfg().clone();
40 move |value: &T| value.into_with_cfg(&config)
41 }
42}
43
44#[cfg(test)]
45#[allow(
46 clippy::arithmetic_side_effects,
47 clippy::cast_possible_truncation,
48 clippy::cast_possible_wrap
49)]
50mod prop_tests {
51 use crypto_bigint::{BoxedUint, U256};
52 use crypto_primitives::{
53 FromWithConfig, IntoWithConfig, PrimeField, crypto_bigint_boxed_monty::BoxedMontyField,
54 };
55 use proptest::prelude::*;
56
57 const MODULUS: &str = "00dca94d8a1ecce3b6e8755d8999787d0524d8ca1ea755e7af84fb646fa31f27";
58 type F = BoxedMontyField;
59
60 fn get_dyn_config(hex_modulus: &str) -> <BoxedMontyField as PrimeField>::Config {
61 let modulus =
62 BoxedUint::from_str_radix_vartime(hex_modulus, 16).expect("Invalid modulus hex string");
63 BoxedMontyField::make_cfg(&modulus).expect("Failed to create field config")
64 }
65
66 fn any_u128() -> impl Strategy<Value = u128> {
67 any::<u128>()
68 }
69 fn any_i128() -> impl Strategy<Value = i128> {
70 any::<i128>()
71 }
72 fn any_bool() -> impl Strategy<Value = bool> {
73 any::<bool>()
74 }
75
76 proptest! {
77 #[test]
78 fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
79 let cfg = get_dyn_config(MODULUS);
80 let f: F = x.into_with_cfg(&cfg);
81 let mut acc: F = F::zero_with_cfg(&cfg);
82 for i in 0..128 {
83 if (x >> i) & 1 == 1 { acc += F::from_with_cfg(1u64, &cfg) * F::from_with_cfg(1u64 << i.min(63), &cfg); }
84 }
85 let u = crypto_bigint::Uint::<{ U256::LIMBS }>::from(x);
86 let g2: F = u.into_with_cfg(&cfg);
87 prop_assert_eq!(f, g2);
88 }
89
90 #[test]
91 fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
92 let cfg = get_dyn_config(MODULUS);
93 let f: F = x.into_with_cfg(&cfg);
94 let abs = x.unsigned_abs();
95 let g_abs = abs.into_with_cfg(&cfg);
96 if x < 0 {
97 prop_assert_eq!(f + g_abs, F::zero_with_cfg(&cfg));
98 } else {
99 prop_assert_eq!(f, g_abs);
100 }
101 }
102
103 #[test]
104 fn prop_from_bool_is_identity(b in any_bool()) {
105 let cfg = get_dyn_config(MODULUS);
106 let f: F = b.into_with_cfg(&cfg);
107 prop_assert_eq!(f, if b { F::one_with_cfg(&cfg) } else { F::zero_with_cfg(&cfg) });
108 }
109
110 #[test]
111 fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
112 let cfg = get_dyn_config(MODULUS);
113 let u: crypto_bigint::Uint<{ U256::LIMBS }> = crypto_bigint::Uint::from(x);
114 let g_from_uint: F = u.into_with_cfg(&cfg);
115 let g_direct: F = x.into_with_cfg(&cfg);
116 prop_assert_eq!(g_from_uint, g_direct);
117 }
118
119 #[test]
120 fn prop_from_with_cfg_generic_matches_owned(x in any::<u64>()) {
121 let cfg = get_dyn_config(MODULUS);
122 let a: F = x.into_with_cfg(&cfg);
123 let b: F = F::from_with_cfg(&x, &cfg);
124 prop_assert_eq!(a, b);
125 }
126 }
127}