1use core::marker::PhantomData;
4
5use p3_binary_field::BinaryField128;
6use p3_commit::Encoder;
7use p3_field::PrimeCharacteristicRing;
8use p3_matrix::Matrix;
9use p3_matrix::dense::RowMajorMatrix;
10use p3_maybe_rayon::prelude::*;
11use p3_util::log2_strict_usize;
12
13use crate::domain::domain_point;
14use crate::poly::PolyBasisNtt;
15use crate::traits::AdditiveNtt;
16
17#[derive(Clone, Debug, Default)]
28pub struct AdditiveRsEncoder<F, Ntt = PolyBasisNtt> {
29 ntt: Ntt,
30 _marker: PhantomData<F>,
31}
32
33impl<Ntt: AdditiveNtt<BinaryField128> + Sync> Encoder<BinaryField128>
36 for AdditiveRsEncoder<BinaryField128, Ntt>
37{
38 fn encode_batch(
39 &self,
40 message: RowMajorMatrix<BinaryField128>,
41 log_inv_rate: usize,
42 ) -> RowMajorMatrix<BinaryField128> {
43 if log_inv_rate == 0 {
44 return self.ntt.ntt_batch(message);
45 }
46
47 let width = message.width();
48 let len = message.values.len();
49 let padded_len = u32::try_from(log_inv_rate)
50 .ok()
51 .and_then(|rate| len.checked_shl(rate))
52 .filter(|&padded| padded >> log_inv_rate == len)
56 .expect("codeword length overflows usize");
57 let log_message_height = log2_strict_usize(message.height());
58
59 let mut values = BinaryField128::zero_vec(padded_len);
69 values
70 .par_chunks_mut(len)
71 .enumerate()
72 .for_each(|(c, chunk)| {
73 let shift = domain_point::<BinaryField128>(c << log_message_height);
74 let coset = self
75 .ntt
76 .shifted_ntt_batch(RowMajorMatrix::new(message.values.clone(), width), shift);
77 chunk.copy_from_slice(&coset.values);
78 });
79
80 RowMajorMatrix::new(values, width)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use alloc::vec;
87
88 use p3_binary_field::{BinaryField128, TowerLevel};
89 use p3_commit::Encoder;
90 use p3_field::PrimeCharacteristicRing;
91 use p3_matrix::Matrix;
92 use p3_matrix::dense::RowMajorMatrix;
93 use proptest::prelude::*;
94 use rand::SeedableRng;
95 use rand::rngs::SmallRng;
96
97 use super::AdditiveRsEncoder;
98 use crate::naive::NaiveAdditiveNtt;
99 use crate::traits::AdditiveNtt;
100
101 type F = BinaryField128;
102
103 fn matrix(log_n: usize, width: usize, seed: u64) -> RowMajorMatrix<F> {
105 RowMajorMatrix::new(
106 (0..(width << log_n))
107 .map(|i| {
108 let bits = seed
109 .wrapping_mul(0x9e37_79b9_7f4a_7c15)
110 .wrapping_add(i as u64);
111 F::from_le_byte_iter(bits.to_le_bytes().into_iter().cycle())
112 })
113 .collect(),
114 width,
115 )
116 }
117
118 #[test]
120 fn encodes_by_padding_and_transforming() {
121 let mut rng = SmallRng::seed_from_u64(1);
122 let message = RowMajorMatrix::<F>::rand(&mut rng, 1 << 5, 3);
123
124 let mut padded = message.clone();
125 padded.values.resize(message.values.len() * 4, F::ZERO);
126 let expected = NaiveAdditiveNtt::<F>::default().ntt_batch(padded);
127
128 let encoded = AdditiveRsEncoder::<F>::default().encode_batch(message, 2);
129 assert_eq!(encoded.height(), 1 << 7);
130 assert_eq!(encoded, expected);
131 }
132
133 #[test]
136 fn codeword_prefix_is_the_message_transform() {
137 let mut rng = SmallRng::seed_from_u64(2);
138 let message = RowMajorMatrix::<F>::rand(&mut rng, 1 << 5, 2);
139
140 let encoded = AdditiveRsEncoder::<F>::default().encode_batch(message.clone(), 1);
141 let direct = AdditiveRsEncoder::<F>::default().encode_batch(message, 0);
142 assert_eq!(&encoded.values[..direct.values.len()], &direct.values[..]);
143 }
144
145 #[test]
146 #[should_panic = "codeword length overflows usize"]
147 fn encode_batch_panics_when_the_codeword_length_overflows() {
148 let message = RowMajorMatrix::new(vec![F::ZERO; 2], 1);
149 let _ = AdditiveRsEncoder::<F>::default().encode_batch(message, usize::BITS as usize - 1);
150 }
151
152 proptest! {
153 #![proptest_config(ProptestConfig::with_cases(32))]
154
155 #[test]
159 fn encode_batch_matches_padding_and_transforming(
160 log_n in 0usize..=6,
161 log_inv_rate in 0usize..=3,
162 width in 1usize..=4,
163 seed in any::<u64>(),
164 ) {
165 let message = matrix(log_n, width, seed);
166
167 let mut padded = message.clone();
168 padded
169 .values
170 .resize(message.values.len() << log_inv_rate, F::ZERO);
171 let expected = NaiveAdditiveNtt::<F>::default().ntt_batch(padded);
172
173 let encoded = AdditiveRsEncoder::<F>::default().encode_batch(message, log_inv_rate);
174 prop_assert_eq!(encoded, expected);
175 }
176 }
177}