use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub type DegreeType = u8;
pub type BasisIndexingType = u8;
pub type SmallIntegers = i8;
#[derive(Debug)]
pub enum DifferentiateError {
NotInTheSpace,
CantComputeDerivative,
}
#[derive(Debug)]
pub enum MonomialError {
DesiredMonomialNotInSpace(DegreeType),
}
#[derive(Debug)]
pub enum SubspaceError {
NotStoredAsCoefficients,
NoSuchBasisVector(BasisIndexingType),
}
#[derive(Clone)]
pub enum PointSpecifier<T> {
NegOne,
Zero,
One,
General(T),
}
pub trait Generic1DPoly<T>:
Sized
+ AddAssign<T>
+ Add<T, Output = Self>
+ Add<Self, Output = Self>
+ AddAssign<Self>
+ SubAssign<T>
+ Sub<T, Output = Self>
+ Sub<Self, Output = Self>
+ SubAssign<Self>
+ MulAssign<T>
+ Mul<T, Output = Self>
+ Neg<Output = Self>
where
T: Clone
+ Neg<Output = T>
+ AddAssign<T>
+ Add<T, Output = T>
+ Mul<T, Output = T>
+ MulAssign<T>
+ From<SmallIntegers>
+ Sub<T, Output = T>,
{
fn create_zero_poly() -> Self;
fn create_monomial(
degree: DegreeType,
zero_pred: &impl Fn(&T) -> bool,
surely_fits: bool,
) -> Result<Self, MonomialError>;
fn evaluate_at(&self, t: T) -> T;
fn evaluate_at_many<const POINT_COUNT: usize>(
&self,
mut ts: [T; POINT_COUNT],
) -> [T; POINT_COUNT] {
#[allow(clippy::needless_range_loop)]
for idx in 0..ts.len() {
let mut cur_evaluate_point = 0.into();
core::mem::swap(&mut cur_evaluate_point, &mut ts[idx]);
ts[idx] = self.evaluate_at(cur_evaluate_point);
}
ts
}
fn evaluate_at_zero(&self) -> T {
self.evaluate_at(0.into())
}
fn evaluate_at_one(&self) -> T {
self.evaluate_at(1.into())
}
fn evaluate_at_neg_one(&self) -> T {
self.evaluate_at((-1).into())
}
fn is_zero_polynomial(&self, zero_pred: &impl Fn(&T) -> bool) -> bool;
fn is_constant_polynomial(&self, zero_pred: &impl Fn(&T) -> bool) -> bool;
fn polynomial_degree(&self, zero_pred: &impl Fn(&T) -> bool) -> Option<DegreeType>;
fn differentiate(self) -> Result<Self, DifferentiateError>;
fn truncating_product(
&self,
rhs: &Self,
zero_pred: &impl Fn(&T) -> bool,
sure_will_cancel: bool,
) -> Option<Self>;
fn evaluate_at_specifier(&self, around_here: PointSpecifier<T>) -> T {
match around_here {
PointSpecifier::NegOne => self.evaluate_at_neg_one(),
PointSpecifier::Zero => self.evaluate_at_zero(),
PointSpecifier::One => self.evaluate_at_one(),
PointSpecifier::General(t) => self.evaluate_at(t),
}
}
fn linear_approx(self, around_here: PointSpecifier<T>) -> Result<(T, T), DifferentiateError> {
let constant_term = match &around_here {
PointSpecifier::NegOne => self.evaluate_at_neg_one(),
PointSpecifier::Zero => self.evaluate_at_zero(),
PointSpecifier::One => self.evaluate_at_one(),
PointSpecifier::General(t) => self.evaluate_at(t.clone()),
};
let derivative = self.differentiate()?;
let linear_term = derivative.evaluate_at_specifier(around_here);
Ok((constant_term, linear_term))
}
fn linear_approx_poly(
self,
around_here: PointSpecifier<T>,
) -> Result<Self, Result<DifferentiateError, MonomialError>> {
let (constant_term, linear_term) = self.linear_approx(around_here).map_err(Ok)?;
let mut answer = Self::create_zero_poly();
answer += constant_term;
let mut linear_poly = Self::create_monomial(1, &|_| false, false).map_err(Err)?;
linear_poly *= linear_term;
Ok(answer + linear_poly)
}
fn all_basis_vectors(up_to: BasisIndexingType) -> Result<Vec<Self>, SubspaceError>;
fn set_basis_vector_coeff(
&mut self,
which_coeff: BasisIndexingType,
new_coeff: T,
) -> Result<(), SubspaceError>;
}
pub fn test_same_polynomial<const N: usize, T, P, Q>(
p: &P,
q: &Q,
degree: DegreeType,
t_points: [T; N],
small_enough: &impl Fn(&T) -> bool,
) where
P: Generic1DPoly<T>,
Q: Generic1DPoly<T>,
T: Clone
+ Neg<Output = T>
+ AddAssign<T>
+ Add<Output = T>
+ Mul<Output = T>
+ MulAssign<T>
+ From<SmallIntegers>
+ Sub<Output = T>
+ std::fmt::Display,
{
let p_at_t = p.evaluate_at_zero();
let q_at_t = q.evaluate_at_zero();
let diff = p_at_t.clone() - q_at_t.clone();
assert!(small_enough(&diff), "{p_at_t} {q_at_t} {degree} 0");
for t_point in t_points {
let p_at_t = p.evaluate_at(t_point.clone());
let q_at_t = q.evaluate_at(t_point.clone());
let diff = p_at_t.clone() - q_at_t.clone();
assert!(small_enough(&diff), "{p_at_t} {q_at_t} {degree} {t_point}");
}
let p_at_t = p.evaluate_at_one();
let q_at_t = q.evaluate_at_one();
let diff = p_at_t.clone() - q_at_t.clone();
assert!(small_enough(&diff), "{p_at_t} {q_at_t} {degree} 1");
}