use core::fmt;
use core::fmt::{Debug, Display};
use core::marker::PhantomData;
use core::ops::Sub;
use num::{One, Zero};
use crate::numerics::{Abs, IsNegativeOne, IsPositive};
use crate::{Evaluable, FreeSizePolynomial, SizedPolynomial};
#[macro_export]
macro_rules! integral {
( $( $x:expr ),* ) => {
{
use $crate::{Polynomial, Integrable};
Polynomial::new(vec![$($x,)*]).integral()
}
};
}
#[derive(Debug, Clone)]
pub struct Integral<N, P: FreeSizePolynomial<N> + Evaluable<N>> {
polynomial: P,
phantom: PhantomData<N>,
}
impl<N, P: FreeSizePolynomial<N> + Evaluable<N>> Integral<N, P> {
pub fn new(polynomial: P) -> Self {
Integral {
polynomial,
phantom: PhantomData,
}
}
pub fn inner(&self) -> &P {
&self.polynomial
}
}
pub trait Integrable<N, P: FreeSizePolynomial<N> + Evaluable<N>> {
fn integral(&self) -> Integral<N, P>;
}
impl<N, P: FreeSizePolynomial<N> + Evaluable<N> + SizedPolynomial<N> + Display> Display
for Integral<N, P>
where
N: IsPositive + Zero + One + Copy + IsNegativeOne + PartialEq + Display + Abs,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.polynomial.is_zero() {
write!(f, "C")
} else {
write!(f, "{} + C", self.polynomial)
}
}
}
impl<N, P: FreeSizePolynomial<N> + Evaluable<N> + Clone> Integral<N, P> {
pub fn replace_c(&self, c: N) -> P {
let mut p = self.polynomial.clone();
p.add_term(c, 0);
p
}
}
impl<N: Sub<Output = N>, P: FreeSizePolynomial<N> + Evaluable<N>> Integral<N, P> {
pub fn eval(&self, start: N, end: N) -> N {
self.polynomial.eval(end) - self.polynomial.eval(start)
}
}
#[cfg(test)]
mod test {
use crate::integral;
use alloc::string::ToString;
#[test]
fn test_integral_empty_polynomial() {
let integral = integral!(0);
assert_eq!("C", integral.to_string());
}
#[test]
fn test_integral_str() {
let integral = integral!(6, 4, 2);
assert_eq!("2x^3 + 2x^2 + 2x + C", integral.to_string());
}
#[test]
fn test_integral_str_negatives() {
let a = integral![-3, -2, 1];
assert_eq!("-x^3 - x^2 + x + C", a.to_string());
}
}