use core::cmp::Ordering;
use core::fmt::Debug;
use core::iter::Map;
use core::ops::{Deref, DerefMut};
use num_traits::Zero;
use serde::{Deserialize, Serialize};
use std_shims::Vec;
use super::circle::CircleDomain;
use crate::core::circle::{CirclePoint, Coset, CosetIterator};
use crate::core::fields::m31::BaseField;
use crate::core::fields::qm31::SecureField;
use crate::core::poly::utils::fold;
use crate::core::utils::bit_reverse;
#[derive(Copy, Clone, Debug)]
pub struct LineDomain {
coset: Coset,
}
impl LineDomain {
pub fn new(coset: Coset) -> Self {
match coset.size().cmp(&2) {
Ordering::Less => {}
Ordering::Equal => {
assert!(!coset.initial.x.is_zero(), "coset x-coordinates not unique");
}
Ordering::Greater => {
assert!(
coset.initial.log_order() >= coset.step.log_order() + 2,
"coset x-coordinates not unique"
);
}
}
Self { coset }
}
pub fn at(&self, i: usize) -> BaseField {
self.coset.at(i).x
}
pub const fn size(&self) -> usize {
self.coset.size()
}
pub const fn log_size(&self) -> u32 {
self.coset.log_size()
}
pub fn iter(&self) -> LineDomainIterator {
self.coset.iter().map(|p| p.x)
}
pub fn double(&self) -> Self {
Self {
coset: self.coset.double(),
}
}
pub fn repeated_double(&self, n_doubles: u32) -> Self {
Self {
coset: self.coset.repeated_double(n_doubles),
}
}
pub const fn coset(&self) -> Coset {
self.coset
}
}
impl IntoIterator for LineDomain {
type Item = BaseField;
type IntoIter = LineDomainIterator;
fn into_iter(self) -> LineDomainIterator {
self.iter()
}
}
impl From<CircleDomain> for LineDomain {
fn from(domain: CircleDomain) -> Self {
Self {
coset: domain.half_coset,
}
}
}
type LineDomainIterator =
Map<CosetIterator<CirclePoint<BaseField>>, fn(CirclePoint<BaseField>) -> BaseField>;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct LinePoly {
#[allow(rustdoc::private_intra_doc_links)]
coeffs: Vec<SecureField>,
log_size: u32,
}
impl LinePoly {
pub fn new(coeffs: Vec<SecureField>) -> Self {
assert!(coeffs.len().is_power_of_two());
let log_size = coeffs.len().ilog2();
Self { coeffs, log_size }
}
pub fn eval_at_point(&self, mut x: SecureField) -> SecureField {
let mut doublings = Vec::new();
for _ in 0..self.log_size {
doublings.push(x);
x = CirclePoint::double_x(x);
}
fold(&self.coeffs, &doublings)
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
debug_assert_eq!(self.coeffs.len(), 1 << self.log_size);
1 << self.log_size
}
pub fn into_ordered_coefficients(mut self) -> Vec<SecureField> {
bit_reverse(&mut self.coeffs);
self.coeffs
}
pub fn from_ordered_coefficients(mut coeffs: Vec<SecureField>) -> Self {
bit_reverse(&mut coeffs);
Self::new(coeffs)
}
}
impl Deref for LinePoly {
type Target = [SecureField];
fn deref(&self) -> &[SecureField] {
&self.coeffs
}
}
impl DerefMut for LinePoly {
fn deref_mut(&mut self) -> &mut [SecureField] {
&mut self.coeffs
}
}
#[cfg(test)]
mod tests {
use super::LineDomain;
use crate::core::circle::{CirclePoint, Coset};
use crate::core::fields::m31::BaseField;
#[test]
#[should_panic]
fn bad_line_domain() {
let coset = Coset::odds(2);
LineDomain::new(coset);
}
#[test]
fn line_domain_of_size_two_works() {
const LOG_SIZE: u32 = 1;
let coset = Coset::subgroup(LOG_SIZE);
LineDomain::new(coset);
}
#[test]
fn line_domain_of_size_one_works() {
const LOG_SIZE: u32 = 0;
let coset = Coset::subgroup(LOG_SIZE);
LineDomain::new(coset);
}
#[test]
fn line_domain_size_is_correct() {
const LOG_SIZE: u32 = 8;
let coset = Coset::half_odds(LOG_SIZE);
let domain = LineDomain::new(coset);
let size = domain.size();
assert_eq!(size, 1 << LOG_SIZE);
}
#[test]
fn line_domain_coset_returns_the_coset() {
let coset = Coset::half_odds(5);
let domain = LineDomain::new(coset);
assert_eq!(domain.coset(), coset);
}
#[test]
fn line_domain_double_works() {
const LOG_SIZE: u32 = 8;
let coset = Coset::half_odds(LOG_SIZE);
let domain = LineDomain::new(coset);
let doubled_domain = domain.double();
assert_eq!(doubled_domain.size(), 1 << (LOG_SIZE - 1));
assert_eq!(doubled_domain.at(0), CirclePoint::double_x(domain.at(0)));
assert_eq!(doubled_domain.at(1), CirclePoint::double_x(domain.at(1)));
}
#[test]
fn line_domain_iter_works() {
const LOG_SIZE: u32 = 8;
let coset = Coset::half_odds(LOG_SIZE);
let domain = LineDomain::new(coset);
let elements = domain.iter().collect::<std_shims::Vec<BaseField>>();
assert_eq!(elements.len(), domain.size());
for (i, element) in elements.into_iter().enumerate() {
assert_eq!(element, domain.at(i), "mismatch at {i}");
}
}
}