1use core::iter::Peekable;
2
3use std_shims::Vec;
4
5use super::fields::Field;
6
7pub trait IteratorMutExt<'a, T: 'a>: Iterator<Item = &'a mut T> {
8 fn assign(self, other: impl IntoIterator<Item = T>)
9 where
10 Self: Sized,
11 {
12 self.zip(other).for_each(|(a, b)| *a = b);
13 }
14}
15
16impl<'a, T: 'a, I: Iterator<Item = &'a mut T>> IteratorMutExt<'a, T> for I {}
17
18pub struct PeekTakeWhile<'a, I: Iterator, P: FnMut(&I::Item) -> bool> {
21 iter: &'a mut Peekable<I>,
22 predicate: P,
23}
24impl<I: Iterator, P: FnMut(&I::Item) -> bool> Iterator for PeekTakeWhile<'_, I, P> {
25 type Item = I::Item;
26
27 fn next(&mut self) -> Option<Self::Item> {
28 self.iter.next_if(&mut self.predicate)
29 }
30}
31pub trait PeekableExt<'a, I: Iterator> {
32 fn peek_take_while<P: FnMut(&I::Item) -> bool>(
37 &'a mut self,
38 predicate: P,
39 ) -> PeekTakeWhile<'a, I, P>;
40}
41impl<'a, I: Iterator> PeekableExt<'a, I> for Peekable<I> {
42 fn peek_take_while<P: FnMut(&I::Item) -> bool>(
43 &'a mut self,
44 predicate: P,
45 ) -> PeekTakeWhile<'a, I, P> {
46 PeekTakeWhile {
47 iter: self,
48 predicate,
49 }
50 }
51}
52
53pub fn all_unique<T: Eq + core::hash::Hash>(iter: impl IntoIterator<Item = T>) -> bool {
54 let mut used = hashbrown::HashSet::new();
55 iter.into_iter().all(|elt| used.insert(elt))
56}
57
58pub const fn bit_reverse_index(i: usize, log_size: u32) -> usize {
60 if log_size == 0 {
61 return i;
62 }
63 i.reverse_bits() >> (usize::BITS - log_size)
64}
65
66pub fn bit_reverse<T>(v: &mut [T]) {
72 let n = v.len();
73 assert!(n.is_power_of_two());
74 let log_n = n.ilog2();
75 for i in 0..n {
76 let j = bit_reverse_index(i, log_n);
77 if j > i {
78 v.swap(i, j);
79 }
80 }
81}
82
83pub const fn previous_bit_reversed_circle_domain_index(
87 i: usize,
88 domain_log_size: u32,
89 eval_log_size: u32,
90) -> usize {
91 offset_bit_reversed_circle_domain_index(i, domain_log_size, eval_log_size, -1)
92}
93
94pub const fn offset_bit_reversed_circle_domain_index(
98 i: usize,
99 domain_log_size: u32,
100 eval_log_size: u32,
101 offset: isize,
102) -> usize {
103 let mut prev_index = bit_reverse_index(i, eval_log_size);
104 let half_size = 1 << (eval_log_size - 1);
105 let step_size = offset * (1 << (eval_log_size - domain_log_size - 1)) as isize;
106 if prev_index < half_size {
107 prev_index = (prev_index as isize + step_size).rem_euclid(half_size as isize) as usize;
108 } else {
109 prev_index =
110 ((prev_index as isize - step_size).rem_euclid(half_size as isize) as usize) + half_size;
111 }
112 bit_reverse_index(prev_index, eval_log_size)
113}
114
115#[cfg(feature = "prover")]
118pub(crate) fn circle_domain_order_to_coset_order(
119 values: &[crate::core::fields::m31::BaseField],
120) -> Vec<crate::core::fields::m31::BaseField> {
121 let n = values.len();
122 let mut coset_order = vec![];
123 for i in 0..(n / 2) {
124 coset_order.push(values[i]);
125 coset_order.push(values[n - 1 - i]);
126 }
127 coset_order
128}
129
130pub fn coset_order_to_circle_domain_order<F: Field>(values: &[F]) -> Vec<F> {
131 let mut circle_domain_order = Vec::with_capacity(values.len());
132 let n = values.len();
133 let half_len = n / 2;
134 for i in 0..half_len {
135 circle_domain_order.push(values[i << 1]);
136 }
137 for i in 0..half_len {
138 circle_domain_order.push(values[n - 1 - (i << 1)]);
139 }
140 circle_domain_order
141}
142
143pub const fn circle_domain_index_to_coset_index(
148 circle_index: usize,
149 log_domain_size: u32,
150) -> usize {
151 let n = 1 << log_domain_size;
152 if circle_index < n / 2 {
153 circle_index * 2
154 } else {
155 (n - 1 - circle_index) * 2 + 1
156 }
157}
158
159pub const fn coset_index_to_circle_domain_index(coset_index: usize, log_domain_size: u32) -> usize {
164 if coset_index.is_multiple_of(2) {
165 coset_index / 2
166 } else {
167 ((2 << log_domain_size) - coset_index) / 2
168 }
169}
170
171pub fn bit_reverse_coset_to_circle_domain_order<T>(v: &mut [T]) {
177 let n = v.len();
178 assert!(n.is_power_of_two());
179 let log_n = n.ilog2();
180 for i in 0..n {
181 let j = bit_reverse_index(coset_index_to_circle_domain_index(i, log_n), log_n);
182 if j > i {
183 v.swap(i, j);
184 }
185 }
186}
187
188#[allow(clippy::uninit_vec)]
192pub unsafe fn uninit_vec<T>(len: usize) -> Vec<T> {
193 let mut vec = Vec::with_capacity(len);
194 vec.set_len(len);
195 vec
196}
197
198#[cfg(all(test, feature = "prover"))]
199mod tests {
200 use itertools::Itertools;
201
202 use super::{
203 offset_bit_reversed_circle_domain_index, previous_bit_reversed_circle_domain_index,
204 };
205 use crate::core::poly::circle::CanonicCoset;
206 use crate::core::utils::{
207 circle_domain_index_to_coset_index, coset_index_to_circle_domain_index,
208 };
209 use crate::m31;
210 use crate::prover::backend::cpu::CpuCircleEvaluation;
211 use crate::prover::poly::NaturalOrder;
212
213 #[test]
214 fn test_offset_bit_reversed_circle_domain_index() {
215 let domain_log_size = 3;
216 let eval_log_size = 6;
217 let initial_index = 5;
218
219 let actual = offset_bit_reversed_circle_domain_index(
220 initial_index,
221 domain_log_size,
222 eval_log_size,
223 -2,
224 );
225 let expected_prev = previous_bit_reversed_circle_domain_index(
226 initial_index,
227 domain_log_size,
228 eval_log_size,
229 );
230 let expected_prev2 = previous_bit_reversed_circle_domain_index(
231 expected_prev,
232 domain_log_size,
233 eval_log_size,
234 );
235 assert_eq!(actual, expected_prev2);
236 }
237
238 #[test]
239 fn test_previous_bit_reversed_circle_domain_index() {
240 let log_size = 4;
241 let n = 1 << log_size;
242 let domain = CanonicCoset::new(log_size).circle_domain();
243 let values = (0..n).map(|i| m31!(i as u32)).collect_vec();
244 let evaluation = CpuCircleEvaluation::<_, NaturalOrder>::new(domain, values);
245 let bit_reversed_evaluation = evaluation.clone().bit_reverse();
246
247 let neighbor_pairs = (0..n)
263 .map(|index| {
264 let prev_index =
265 previous_bit_reversed_circle_domain_index(index, log_size - 3, log_size);
266 (
267 bit_reversed_evaluation[index],
268 bit_reversed_evaluation[prev_index],
269 )
270 })
271 .sorted()
272 .collect_vec();
273 let mut expected_neighbor_pairs = vec![
274 (m31!(0), m31!(4)),
275 (m31!(15), m31!(11)),
276 (m31!(1), m31!(5)),
277 (m31!(14), m31!(10)),
278 (m31!(2), m31!(6)),
279 (m31!(13), m31!(9)),
280 (m31!(3), m31!(7)),
281 (m31!(12), m31!(8)),
282 (m31!(4), m31!(0)),
283 (m31!(11), m31!(15)),
284 (m31!(5), m31!(1)),
285 (m31!(10), m31!(14)),
286 (m31!(6), m31!(2)),
287 (m31!(9), m31!(13)),
288 (m31!(7), m31!(3)),
289 (m31!(8), m31!(12)),
290 ];
291 expected_neighbor_pairs.sort();
292
293 assert_eq!(neighbor_pairs, expected_neighbor_pairs);
294 }
295
296 #[test]
297 fn test_circle_domain_and_coset_index_conversion() {
298 let log_size = 3;
299 let n = 1 << log_size;
300
301 for i in 0..n {
303 let coset_idx = circle_domain_index_to_coset_index(i, log_size);
304 let circle_idx = coset_index_to_circle_domain_index(coset_idx, log_size);
305 assert_eq!(i, circle_idx);
306 }
307 }
308}