1use ark_ff::{BigInteger, PrimeField};
8use o1_utils::FieldHelpers;
9use std::iter::repeat;
10
11pub(crate) fn encoding_size_full<F: PrimeField>() -> usize {
14 F::size_in_bytes()
15}
16
17pub(crate) const fn encoding_size<F: PrimeField>() -> usize {
20 (F::MODULUS_BIT_SIZE / 8) as usize
21}
22
23pub fn encode<F: PrimeField>(bytes: &[u8]) -> F {
27 F::from_be_bytes_mod_order(bytes)
28}
29
30pub(crate) fn decode_full<F: PrimeField>(x: F) -> Vec<u8> {
33 x.into_bigint().to_bytes_be()
34}
35
36fn decode<F: PrimeField>(x: F) -> Vec<u8> {
39 let n = encoding_size::<F>();
41 let m = encoding_size_full::<F>();
43 let full_bytes = decode_full(x);
44 full_bytes[(m - n)..m].to_vec()
45}
46
47pub(crate) fn decode_into<F: PrimeField>(buffer: &mut [u8], x: F) {
50 let bytes = decode(x);
51 buffer.copy_from_slice(&bytes);
52}
53
54pub(crate) fn decode_from_field_elements<F: PrimeField>(xs: Vec<F>) -> Vec<u8> {
56 xs.into_iter().flat_map(decode).collect()
57}
58
59fn encode_as_field_elements_aux<F: PrimeField>(n: usize, bytes: &[u8]) -> Vec<F> {
61 bytes
62 .chunks(n)
63 .map(|chunk| {
64 if chunk.len() == n {
65 encode(chunk)
66 } else {
67 let bytes: Vec<_> = chunk.iter().copied().chain(repeat(0)).take(n).collect();
70 encode(&bytes)
71 }
72 })
73 .collect()
74}
75
76pub fn encode_as_field_elements<F: PrimeField>(bytes: &[u8]) -> Vec<F> {
78 encode_as_field_elements_aux(encoding_size::<F>(), bytes)
79}
80
81pub fn encode_as_field_elements_full<F: PrimeField>(bytes: &[u8]) -> Vec<F> {
83 encode_as_field_elements_aux(encoding_size_full::<F>(), bytes)
84}
85
86pub fn encode_for_domain<F: PrimeField>(domain_size: usize, bytes: &[u8]) -> Vec<Vec<F>> {
89 let xs = encode_as_field_elements(bytes);
90 xs.chunks(domain_size)
91 .map(|chunk| {
92 if chunk.len() == domain_size {
93 chunk.to_vec()
94 } else {
95 let mut padded_chunk = Vec::with_capacity(domain_size);
98 padded_chunk.extend_from_slice(chunk);
99 padded_chunk.resize(domain_size, F::zero());
100 padded_chunk
101 }
102 })
103 .collect()
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
110 use ark_std::UniformRand;
111 use mina_curves::pasta::Fp;
112 use once_cell::sync::Lazy;
113 use proptest::prelude::*;
114
115 use crate::utils::test_utils::UserData;
116
117 proptest! {
118 #[test]
120 fn test_decodes_consistency(xs in any::<[u8;31]>())
121 { let n : Fp = encode(&xs);
122 let y_full : [u8; 31] = decode_full(n).as_slice()[1..32].try_into().unwrap();
123 let y = decode(n);
124 prop_assert_eq!(y_full, y.as_slice());
125 }
126
127 #[test]
129 fn test_round_trip_from_bytes(xs in any::<[u8;31]>())
130 { let n : Fp = encode(&xs);
131 let ys : [u8; 31] = decode_full(n).as_slice()[1..32].try_into().unwrap();
132 prop_assert_eq!(xs, ys);
133 }
134
135 #[test]
137 fn test_round_trip_from_fp(
138 x in prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
139 ) {
140 let bytes = decode_full(x);
141 let y = encode(&bytes);
142 prop_assert_eq!(x,y);
143 }
144 }
145
146 static DOMAIN: Lazy<Radix2EvaluationDomain<Fp>> = Lazy::new(|| {
147 const SRS_SIZE: usize = 1 << 16;
148 Radix2EvaluationDomain::new(SRS_SIZE).unwrap()
149 });
150
151 proptest! {
153 #![proptest_config(ProptestConfig::with_cases(20))]
154 #[test]
155 fn test_round_trip_encoding_to_field_elems(UserData(xs) in UserData::arbitrary()
156 )
157 { let chunked = encode_for_domain::<Fp>(DOMAIN.size(), &xs);
158 let elems = chunked
159 .into_iter()
160 .flatten()
161 .collect();
162 let ys = decode_from_field_elements(elems)
163 .into_iter()
164 .take(xs.len())
165 .collect::<Vec<u8>>();
166 prop_assert_eq!(xs,ys);
167 }
168 }
169}