1use alloc::vec::Vec;
2use core::ops::{Add, AddAssign, Mul, Neg, Sub};
3
4use p3_field::extension::ComplexExtendable;
5use p3_field::{
6 ExtensionField, Field, PackedValue, PrimeCharacteristicRing, batch_multiplicative_inverse,
7};
8use p3_maybe_rayon::prelude::*;
9
10#[allow(clippy::manual_non_exhaustive)]
14#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
15pub struct Point<F> {
16 pub x: F,
17 pub y: F,
18 _private: (),
19}
20
21impl<F: Field> Point<F> {
22 #[inline]
23 pub fn new(x: F, y: F) -> Self {
24 debug_assert_eq!(x.square() + y.square(), F::ONE);
25 Self { x, y, _private: () }
26 }
27
28 const ZERO: Self = Self {
29 x: F::ONE,
30 y: F::ZERO,
31 _private: (),
32 };
33
34 pub fn from_projective_line(t: F) -> Self {
41 let t2 = t.square();
42 let inv_denom = (F::ONE + t2).try_inverse().expect("t^2 = -1");
43 Self::new((F::ONE - t2) * inv_denom, t.double() * inv_denom)
44 }
45
46 pub fn to_projective_line(self) -> Option<F> {
56 (self.x + F::ONE).try_inverse().map(|x| x * self.y)
57 }
58
59 pub fn double(self) -> Self {
62 Self::new(self.x.square().double() - F::ONE, self.x.double() * self.y)
63 }
64
65 pub fn repeated_double(mut self, n: usize) -> Self {
67 for _ in 0..n {
68 self = self.double();
69 }
70 self
71 }
72
73 pub fn v_n(mut self, log_n: usize) -> F {
77 debug_assert!(log_n >= 1, "v_n requires log_n >= 1");
78 for _ in 0..log_n.saturating_sub(1) {
79 self.x = self.x.square().double() - F::ONE; }
81 self.x
82 }
83
84 pub fn v_n_prod(mut self, log_n: usize) -> F {
89 if log_n <= 1 {
90 return F::ONE;
91 }
92 let mut output = self.x;
93 for _ in 0..(log_n - 2) {
94 self.x = self.x.square().double() - F::ONE; output *= self.x;
96 }
97 output
98 }
99
100 pub fn v_tilde_p<EF: ExtensionField<F>>(self, at: Point<EF>) -> EF {
105 (at - self).to_projective_line().unwrap()
106 }
107
108 pub fn s_p_at_p(self, log_n: usize) -> F {
111 debug_assert!(log_n >= 1, "s_p_at_p requires log_n >= 1");
112 -self.v_n_prod(log_n).mul_2exp_u64((2 * log_n - 1) as u64) * self.y
113 }
114
115 pub fn v_p<EF: ExtensionField<F>>(self, at: Point<EF>) -> (EF, EF) {
120 let diff = -at + self;
121 (EF::ONE - diff.x, -diff.y)
122 }
123}
124
125pub(crate) fn compute_lagrange_den_batched<F: Field, EF: ExtensionField<F>>(
129 points: &[Point<F>],
130 at: Point<EF>,
131 log_n: usize,
132) -> Vec<EF> {
133 let s_p = {
135 let mut s_p = F::zero_vec(points.len());
136
137 if log_n < 2 {
138 for (slot, p) in s_p.iter_mut().zip(points) {
140 *slot = p.s_p_at_p(log_n);
141 }
142 } else {
143 let exp = (2 * log_n - 1) as u64;
145 let iters = log_n - 2;
146 let width = F::Packing::WIDTH;
147 let packed_len = (points.len() / width) * width;
148
149 s_p[..packed_len]
150 .par_chunks_exact_mut(width)
151 .zip(points.par_chunks_exact(width))
152 .for_each(|(slots, chunk)| {
153 let mut cur = F::Packing::from_fn(|l| chunk[l].x);
155 let mut output = cur;
156
157 for _ in 0..iters {
159 cur = cur.square().double() - F::Packing::ONE;
160 output *= cur;
161 }
162
163 let ys = F::Packing::from_fn(|l| chunk[l].y);
165 let packed_s_p = -(output.mul_2exp_u64(exp) * ys);
166
167 slots.copy_from_slice(packed_s_p.as_slice());
168 });
169
170 for (slot, &pt) in s_p[packed_len..].iter_mut().zip(&points[packed_len..]) {
172 *slot = pt.s_p_at_p(log_n);
173 }
174 }
175 s_p
176 };
177
178 let (numer, denom): (Vec<_>, Vec<_>) = points
180 .par_iter()
181 .zip(&s_p)
182 .map(|(&pt, &s_p)| {
183 let diff = at - pt;
184 let numer = diff.x + F::ONE;
185 let denom = diff.y * s_p;
186 (numer, denom)
187 })
188 .unzip();
189
190 let inv_d = batch_multiplicative_inverse(&denom);
192
193 numer
195 .par_iter()
196 .zip(inv_d.par_iter())
197 .map(|(&num, &inv_d)| num * inv_d)
198 .collect()
199}
200
201impl<F: ComplexExtendable> Point<F> {
202 pub fn generator(log_n: usize) -> Self {
203 let g = F::circle_two_adic_generator(log_n);
204 Self::new(g.real(), g.imag())
205 }
206}
207
208impl<F: Field> Neg for Point<F> {
211 type Output = Self;
212 fn neg(mut self) -> Self::Output {
213 self.y = -self.y;
214 self
215 }
216}
217
218impl<F: Field, EF: ExtensionField<F>> Add<Point<F>> for Point<EF> {
219 type Output = Self;
220 fn add(self, rhs: Point<F>) -> Self::Output {
221 Self::new(
222 self.x * rhs.x - self.y * rhs.y,
223 self.x * rhs.y + self.y * rhs.x,
224 )
225 }
226}
227
228impl<F: Field> AddAssign for Point<F> {
229 fn add_assign(&mut self, rhs: Self) {
230 *self = *self + rhs;
231 }
232}
233
234impl<F: Field, EF: ExtensionField<F>> Sub<Point<F>> for Point<EF> {
235 type Output = Self;
236 fn sub(self, rhs: Point<F>) -> Self::Output {
237 Self::new(
238 self.x * rhs.x + self.y * rhs.y,
239 self.y * rhs.x - self.x * rhs.y,
240 )
241 }
242}
243
244impl<F: Field> Mul<usize> for Point<F> {
245 type Output = Self;
246 fn mul(mut self, mut rhs: usize) -> Self::Output {
247 let mut res = Self::ZERO;
248 while rhs != 0 {
249 if rhs & 1 == 1 {
250 res += self;
251 }
252 rhs >>= 1;
253 self = self.double();
254 }
255 res
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use p3_field::extension::BinomialExtensionField;
262 use p3_mersenne_31::Mersenne31;
263 use proptest::prelude::*;
264 use rand::rngs::SmallRng;
265 use rand::{RngExt, SeedableRng};
266
267 use super::*;
268
269 type F = Mersenne31;
270 type EF = BinomialExtensionField<F, 3>;
271 type Pt = Point<F>;
272
273 #[test]
274 fn test_arithmetic() {
275 let one = Pt::generator(3);
276 assert_eq!(one - one, Pt::ZERO);
277 assert_eq!(one + one, one * 2);
278 assert_eq!(one + one + one, one * 3);
279 assert_eq!(one * 7, -one);
280 assert_eq!(one * 8, Pt::ZERO);
281
282 let generator = Pt::generator(10);
283 let log_n = 10;
284 let vn_prod_gen = (1..log_n).map(|i| generator.v_n(i)).product();
285 assert_eq!(generator.v_n_prod(log_n), vn_prod_gen);
286 }
287
288 #[cfg(debug_assertions)]
289 #[test]
290 #[should_panic(expected = "v_n requires log_n >= 1")]
291 fn test_v_n_underflow_log_n_0() {
292 let p = Pt::generator(3);
293 let _ = p.v_n(0);
294 }
295
296 #[cfg(debug_assertions)]
297 #[test]
298 #[should_panic(expected = "s_p_at_p requires log_n >= 1")]
299 fn test_s_p_at_p_underflow_log_n_0() {
300 let p = Pt::generator(3);
301 let _ = p.s_p_at_p(0);
302 }
303
304 fn lagrange_den_scalar(points: &[Pt], at: Point<EF>, log_n: usize) -> Vec<EF> {
306 points
307 .iter()
308 .map(|&pt| {
309 let diff = at - pt;
310 let numer = diff.x + F::ONE;
311 let denom = diff.y * pt.s_p_at_p(log_n);
312 numer * denom.inverse()
313 })
314 .collect()
315 }
316
317 proptest! {
318 #[test]
319 fn compute_lagrange_den_batched_matches_scalar(
320 log_n in 1usize..19,
321 len in 0usize..40,
322 at_seed in any::<u64>(),
323 ) {
324 let prefix: Vec<Pt> = crate::CircleDomain::standard(log_n).points().take(40).collect();
326 let points = &prefix[..len.min(prefix.len())];
327
328 let mut rng = SmallRng::seed_from_u64(at_seed);
330 let at = Point::<EF>::from_projective_line(rng.random());
331
332 let all_invertible = points
334 .iter()
335 .all(|&pt| (at - pt).y * pt.s_p_at_p(log_n) != EF::ZERO);
336 prop_assume!(all_invertible);
337
338 prop_assert_eq!(
339 compute_lagrange_den_batched(points, at, log_n),
340 lagrange_den_scalar(points, at, log_n)
341 );
342 }
343 }
344}