stwo_gpu/core/poly/
line.rs1use core::cmp::Ordering;
2use core::fmt::Debug;
3use core::iter::Map;
4use core::ops::{Deref, DerefMut};
5
6use num_traits::Zero;
7use serde::{Deserialize, Serialize};
8use std_shims::Vec;
9
10use super::circle::CircleDomain;
11use crate::core::circle::{CirclePoint, Coset, CosetIterator};
12use crate::core::fields::m31::BaseField;
13use crate::core::fields::qm31::SecureField;
14use crate::core::poly::utils::fold;
15use crate::core::utils::bit_reverse;
16
17#[derive(Copy, Clone, Debug)]
21pub struct LineDomain {
22 coset: Coset,
23}
24
25impl LineDomain {
26 pub fn new(coset: Coset) -> Self {
32 match coset.size().cmp(&2) {
33 Ordering::Less => {}
34 Ordering::Equal => {
35 assert!(!coset.initial.x.is_zero(), "coset x-coordinates not unique");
37 }
38 Ordering::Greater => {
39 assert!(
43 coset.initial.log_order() >= coset.step.log_order() + 2,
44 "coset x-coordinates not unique"
45 );
46 }
47 }
48 Self { coset }
49 }
50
51 pub fn at(&self, i: usize) -> BaseField {
53 self.coset.at(i).x
54 }
55
56 pub const fn size(&self) -> usize {
58 self.coset.size()
59 }
60
61 pub const fn log_size(&self) -> u32 {
63 self.coset.log_size()
64 }
65
66 pub fn iter(&self) -> LineDomainIterator {
68 self.coset.iter().map(|p| p.x)
69 }
70
71 pub fn double(&self) -> Self {
73 Self {
74 coset: self.coset.double(),
75 }
76 }
77
78 pub const fn coset(&self) -> Coset {
80 self.coset
81 }
82}
83
84impl IntoIterator for LineDomain {
85 type Item = BaseField;
86 type IntoIter = LineDomainIterator;
87
88 fn into_iter(self) -> LineDomainIterator {
90 self.iter()
91 }
92}
93
94impl From<CircleDomain> for LineDomain {
95 fn from(domain: CircleDomain) -> Self {
96 Self {
97 coset: domain.half_coset,
98 }
99 }
100}
101
102type LineDomainIterator =
104 Map<CosetIterator<CirclePoint<BaseField>>, fn(CirclePoint<BaseField>) -> BaseField>;
105
106#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
108pub struct LinePoly {
109 #[allow(rustdoc::private_intra_doc_links)]
113 coeffs: Vec<SecureField>,
114 log_size: u32,
116}
117
118impl LinePoly {
119 pub fn new(coeffs: Vec<SecureField>) -> Self {
125 assert!(coeffs.len().is_power_of_two());
126 let log_size = coeffs.len().ilog2();
127 Self { coeffs, log_size }
128 }
129
130 pub fn eval_at_point(&self, mut x: SecureField) -> SecureField {
132 let mut doublings = Vec::new();
133 for _ in 0..self.log_size {
134 doublings.push(x);
135 x = CirclePoint::double_x(x);
136 }
137 fold(&self.coeffs, &doublings)
138 }
139
140 #[allow(clippy::len_without_is_empty)]
142 pub fn len(&self) -> usize {
143 debug_assert_eq!(self.coeffs.len(), 1 << self.log_size);
146 1 << self.log_size
147 }
148
149 pub fn into_ordered_coefficients(mut self) -> Vec<SecureField> {
151 bit_reverse(&mut self.coeffs);
152 self.coeffs
153 }
154
155 pub fn from_ordered_coefficients(mut coeffs: Vec<SecureField>) -> Self {
161 bit_reverse(&mut coeffs);
162 Self::new(coeffs)
163 }
164}
165
166impl Deref for LinePoly {
167 type Target = [SecureField];
168
169 fn deref(&self) -> &[SecureField] {
170 &self.coeffs
171 }
172}
173
174impl DerefMut for LinePoly {
175 fn deref_mut(&mut self) -> &mut [SecureField] {
176 &mut self.coeffs
177 }
178}
179#[cfg(test)]
180mod tests {
181
182 use super::LineDomain;
183 use crate::core::circle::{CirclePoint, Coset};
184 use crate::core::fields::m31::BaseField;
185
186 #[test]
187 #[should_panic]
188 fn bad_line_domain() {
189 let coset = Coset::odds(2);
191
192 LineDomain::new(coset);
193 }
194
195 #[test]
196 fn line_domain_of_size_two_works() {
197 const LOG_SIZE: u32 = 1;
198 let coset = Coset::subgroup(LOG_SIZE);
199
200 LineDomain::new(coset);
201 }
202
203 #[test]
204 fn line_domain_of_size_one_works() {
205 const LOG_SIZE: u32 = 0;
206 let coset = Coset::subgroup(LOG_SIZE);
207
208 LineDomain::new(coset);
209 }
210
211 #[test]
212 fn line_domain_size_is_correct() {
213 const LOG_SIZE: u32 = 8;
214 let coset = Coset::half_odds(LOG_SIZE);
215 let domain = LineDomain::new(coset);
216
217 let size = domain.size();
218
219 assert_eq!(size, 1 << LOG_SIZE);
220 }
221
222 #[test]
223 fn line_domain_coset_returns_the_coset() {
224 let coset = Coset::half_odds(5);
225 let domain = LineDomain::new(coset);
226
227 assert_eq!(domain.coset(), coset);
228 }
229
230 #[test]
231 fn line_domain_double_works() {
232 const LOG_SIZE: u32 = 8;
233 let coset = Coset::half_odds(LOG_SIZE);
234 let domain = LineDomain::new(coset);
235
236 let doubled_domain = domain.double();
237
238 assert_eq!(doubled_domain.size(), 1 << (LOG_SIZE - 1));
239 assert_eq!(doubled_domain.at(0), CirclePoint::double_x(domain.at(0)));
240 assert_eq!(doubled_domain.at(1), CirclePoint::double_x(domain.at(1)));
241 }
242
243 #[test]
244 fn line_domain_iter_works() {
245 const LOG_SIZE: u32 = 8;
246 let coset = Coset::half_odds(LOG_SIZE);
247 let domain = LineDomain::new(coset);
248
249 let elements = domain.iter().collect::<std_shims::Vec<BaseField>>();
250
251 assert_eq!(elements.len(), domain.size());
252 for (i, element) in elements.into_iter().enumerate() {
253 assert_eq!(element, domain.at(i), "mismatch at {i}");
254 }
255 }
256}