zinc_utils/field/
boxed_monty.rs1use crate::{from_ref::FromRef, projectable_to_field::ProjectableToField};
2use crypto_primitives::{
3 ProjectElementWithConfig, SetConfig, Wrapper,
4 crypto_bigint_boxed_monty::{BoxedMontyField, BoxedMontyFieldElement},
5 crypto_bigint_boxed_uint::BoxedUint,
6 crypto_bigint_uint::Uint,
7};
8
9impl FromRef<Self> for BoxedMontyFieldElement {
10 fn from_ref(value: &Self) -> Self {
11 value.clone()
12 }
13}
14
15impl<const LIMBS: usize> FromRef<Uint<LIMBS>> for BoxedUint {
16 #[inline]
17 fn from_ref(value: &Uint<LIMBS>) -> Self {
18 value.inner().into()
19 }
20}
21
22impl<T> ProjectableToField<BoxedMontyField> for T
23where
24 BoxedMontyField: ProjectElementWithConfig<T>,
25{
26 fn prepare_projection(
27 cfg: &BoxedMontyField,
28 _sampled_value: &<BoxedMontyField as SetConfig>::Element,
29 ) -> impl Fn(&Self) -> <BoxedMontyField as SetConfig>::Element {
30 let cfg = cfg.clone();
31 move |value: &T| cfg.project(value)
32 }
33}
34
35#[cfg(test)]
36#[allow(
37 clippy::arithmetic_side_effects,
38 clippy::cast_possible_truncation,
39 clippy::cast_possible_wrap
40)]
41mod prop_tests {
42 use crypto_primitives::{
43 BaseFieldConfig, ProjectElementWithConfig, SemiringConfig,
44 crypto_bigint_boxed_monty::BoxedMontyField,
45 crypto_bigint_boxed_uint::BoxedUint,
46 crypto_bigint_uint::{U256, Uint},
47 };
48 use proptest::prelude::*;
49 use std::str::FromStr;
50
51 const MODULUS: &str = "00dca94d8a1ecce3b6e8755d8999787d0524d8ca1ea755e7af84fb646fa31f27";
52
53 fn get_dyn_config(hex_modulus: &str) -> BoxedMontyField {
54 let modulus =
55 BoxedUint::from_str(&format!("0x{hex_modulus}")).expect("Invalid modulus hex string");
56 BoxedMontyField::new(&modulus).expect("Failed to create field config")
57 }
58
59 fn any_u128() -> impl Strategy<Value = u128> {
60 any::<u128>()
61 }
62 fn any_i128() -> impl Strategy<Value = i128> {
63 any::<i128>()
64 }
65 fn any_bool() -> impl Strategy<Value = bool> {
66 any::<bool>()
67 }
68
69 proptest! {
70 #[test]
71 #[cfg_attr(miri, ignore)] fn prop_from_unsigned_matches_sum_of_bits(x in any_u128()) {
73 let cfg = get_dyn_config(MODULUS);
74 let f = cfg.project(&x);
75 let mut acc = cfg.zero();
76 for i in 0..128 {
77 if (x >> i) & 1 == 1 {
78 let bit = cfg.mul(&cfg.project(&1u64), &cfg.project(&(1u64 << i.min(63))));
79 cfg.add_assign(&mut acc, &bit);
80 }
81 }
82 let u = Uint::<{ U256::LIMBS }>::from(x);
83 let g2 = cfg.project(&u);
84 prop_assert_eq!(f, g2);
85 }
86
87 #[test]
88 #[cfg_attr(miri, ignore)] fn prop_from_signed_is_neg_of_abs_when_negative(x in any_i128()) {
90 let cfg = get_dyn_config(MODULUS);
91 let f = cfg.project(&x);
92 let abs = x.unsigned_abs();
93 let g_abs = cfg.project(&abs);
94 if x < 0 {
95 prop_assert_eq!(cfg.add(&f, &g_abs), cfg.zero());
96 } else {
97 prop_assert_eq!(f, g_abs);
98 }
99 }
100
101 #[test]
102 #[cfg_attr(miri, ignore)] fn prop_from_bool_is_identity(b in any_bool()) {
104 let cfg = get_dyn_config(MODULUS);
105 let f = cfg.project(&b);
106 prop_assert_eq!(f, if b { cfg.one() } else { cfg.zero() });
107 }
108
109 #[test]
110 #[cfg_attr(miri, ignore)] fn prop_from_uint_roundtrip_through_uint(x in any_u128()) {
112 let cfg = get_dyn_config(MODULUS);
113 let u = BoxedUint::from(x).resize(cfg.bits_precision());
114 let g_from_uint = cfg.project(&u);
115 let g_direct = cfg.project(&x);
116 prop_assert_eq!(g_from_uint, g_direct);
117 }
118 }
119}