use alloc::vec::Vec;
use core::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Shr, ShrAssign, Sub, SubAssign,
};
use num::{One, Zero};
use crate::numerics::{Abs, IsNegativeOne, IsPositive, TryFromUsizeContinuous, TryFromUsizeExact};
use crate::polynomial::polynomial::term_with_deg;
use crate::{
Degree, Derivable, Evaluable, Integrable, Integral, MutablePolynomial, Polynomial, Roots,
SizedPolynomial, Term, TryAddError,
};
#[derive(Debug, Clone)]
pub struct LinearBinomial<N> {
pub coefficients: [N; 2],
}
impl<N: Sized> LinearBinomial<N> {
pub fn new(coefficients: [N; 2]) -> LinearBinomial<N> {
LinearBinomial { coefficients }
}
}
impl<N: Zero + Copy> LinearBinomial<N> {
pub fn ordered_term_iter(&self) -> impl Iterator<Item = (N, usize)> + '_ {
self.coefficients
.iter()
.enumerate()
.filter_map(|(index, &coeff)| {
if coeff.is_zero() {
None
} else {
Some((coeff, 1 - index))
}
})
}
}
impl<N> LinearBinomial<N>
where
N: Copy + Neg<Output = N> + Div<Output = N> + Zero,
{
pub fn root(&self) -> Roots<N> {
let [a, b] = self.coefficients;
if a.is_zero() {
if b.is_zero() {
Roots::InfiniteRoots
} else {
Roots::NoRoots
}
} else {
Roots::OneRealRoot(-b / a)
}
}
}
impl<N: Copy + Zero> SizedPolynomial<N> for LinearBinomial<N> {
fn term_with_degree(&self, degree: usize) -> Term<N> {
term_with_deg(&self.coefficients, degree)
}
fn terms_as_vec(&self) -> Vec<(N, usize)> {
self.ordered_term_iter().collect()
}
fn degree(&self) -> Degree {
if !self.coefficients[0].is_zero() {
Degree::Num(1)
} else if !self.coefficients[1].is_zero() {
Degree::Num(0)
} else {
Degree::NegInf
}
}
fn zero() -> Self {
LinearBinomial::new([N::zero(); 2])
}
fn set_to_zero(&mut self) {
self.coefficients = [N::zero(); 2];
}
}
impl<N> MutablePolynomial<N> for LinearBinomial<N>
where
N: Zero + SubAssign + AddAssign + Copy,
{
fn try_add_term(&mut self, coeff: N, degree: usize) -> Result<(), TryAddError> {
if degree <= 1 {
self.coefficients[1 - degree] += coeff;
Ok(())
} else {
Err(TryAddError::DegreeOutOfBounds)
}
}
fn try_sub_term(&mut self, coeff: N, degree: usize) -> Result<(), TryAddError> {
if degree <= 1 {
self.coefficients[1 - degree] -= coeff;
Ok(())
} else {
Err(TryAddError::DegreeOutOfBounds)
}
}
}
impl<N> Evaluable<N> for LinearBinomial<N>
where
N: Add<Output = N> + Mul<Output = N> + Copy,
{
fn eval(&self, point: N) -> N {
point * self.coefficients[0] + self.coefficients[1]
}
}
impl<N> Derivable<N> for LinearBinomial<N>
where
N: Zero + One + Copy + Mul<Output = N> + TryFromUsizeExact,
{
fn derivative(&self) -> LinearBinomial<N> {
LinearBinomial::new([N::zero(), self.coefficients[0]])
}
}
impl<N> Integrable<N, Polynomial<N>> for LinearBinomial<N>
where
N: Zero
+ Copy
+ DivAssign
+ Mul<Output = N>
+ MulAssign
+ AddAssign
+ Div<Output = N>
+ TryFromUsizeContinuous,
{
fn integral(&self) -> Integral<N, Polynomial<N>> {
Integral::new(Polynomial::new(vec![
self.coefficients[0]
/ N::try_from_usize_cont(2).expect("Failed to convert 2usize to N."),
self.coefficients[1],
N::zero(),
]))
}
}
impl<N> PartialEq for LinearBinomial<N>
where
N: Zero + PartialEq + Copy,
{
fn eq(&self, other: &Self) -> bool {
self.coefficients == other.coefficients
}
}
macro_rules! from_binomial_a_to_b {
($A:ty, $B:ty) => {
impl From<LinearBinomial<$A>> for LinearBinomial<$B> {
fn from(item: LinearBinomial<$A>) -> Self {
LinearBinomial::new([item.coefficients[0] as $B, item.coefficients[1] as $B])
}
}
};
}
upcast!(from_binomial_a_to_b);
poly_from_str!(LinearBinomial);
fmt_poly!(LinearBinomial);
impl<N: Copy + Neg<Output = N>> Neg for LinearBinomial<N> {
type Output = LinearBinomial<N>;
fn neg(self) -> LinearBinomial<N> {
LinearBinomial::new([-self.coefficients[0], -self.coefficients[1]])
}
}
impl<N> Sub<LinearBinomial<N>> for LinearBinomial<N>
where
N: Copy + Sub<Output = N>,
{
type Output = LinearBinomial<N>;
fn sub(self, rhs: LinearBinomial<N>) -> LinearBinomial<N> {
LinearBinomial::new([
self.coefficients[0] - rhs.coefficients[0],
self.coefficients[1] - rhs.coefficients[1],
])
}
}
impl<N> SubAssign<LinearBinomial<N>> for LinearBinomial<N>
where
N: SubAssign + Copy,
{
fn sub_assign(&mut self, rhs: LinearBinomial<N>) {
self.coefficients[0] -= rhs.coefficients[0];
self.coefficients[1] -= rhs.coefficients[1];
}
}
impl<N> Add<LinearBinomial<N>> for LinearBinomial<N>
where
N: Add<Output = N> + Copy,
{
type Output = LinearBinomial<N>;
fn add(self, rhs: LinearBinomial<N>) -> LinearBinomial<N> {
LinearBinomial::new([
self.coefficients[0] + rhs.coefficients[0],
self.coefficients[1] + rhs.coefficients[1],
])
}
}
impl<N: Copy + AddAssign> AddAssign<LinearBinomial<N>> for LinearBinomial<N> {
fn add_assign(&mut self, rhs: LinearBinomial<N>) {
self.coefficients[0] += rhs.coefficients[0];
self.coefficients[1] += rhs.coefficients[1];
}
}
impl<N: Mul<Output = N> + Copy> Mul<N> for LinearBinomial<N> {
type Output = LinearBinomial<N>;
fn mul(self, rhs: N) -> LinearBinomial<N> {
LinearBinomial::new([self.coefficients[0] * rhs, self.coefficients[1] * rhs])
}
}
impl<N: MulAssign + Copy> MulAssign<N> for LinearBinomial<N> {
fn mul_assign(&mut self, rhs: N) {
self.coefficients[0] *= rhs;
self.coefficients[1] *= rhs;
}
}
impl<N: Div<Output = N> + Copy> Div<N> for LinearBinomial<N> {
type Output = LinearBinomial<N>;
fn div(self, rhs: N) -> LinearBinomial<N> {
LinearBinomial::new([self.coefficients[0] / rhs, self.coefficients[1] / rhs])
}
}
impl<N: DivAssign + Copy> DivAssign<N> for LinearBinomial<N> {
fn div_assign(&mut self, rhs: N) {
self.coefficients[0] /= rhs;
self.coefficients[1] /= rhs;
}
}
impl<N: Zero + Copy> Shr<u32> for LinearBinomial<N> {
type Output = LinearBinomial<N>;
fn shr(self, rhs: u32) -> LinearBinomial<N> {
match rhs {
0 => LinearBinomial::new(self.coefficients),
1 => LinearBinomial::new([N::zero(), self.coefficients[0]]),
_ => LinearBinomial::zero(),
}
}
}
impl<N: Zero + Copy> ShrAssign<u32> for LinearBinomial<N> {
fn shr_assign(&mut self, rhs: u32) {
match rhs {
0 => {}
1 => {
self.coefficients[1] = self.coefficients[0];
self.coefficients[0] = N::zero();
}
_ => {
self.coefficients[0] = N::zero();
self.coefficients[1] = N::zero();
}
}
}
}
#[cfg(test)]
mod test {
use crate::{Degree, Derivable, Evaluable, LinearBinomial, Roots, SizedPolynomial};
#[test]
fn test_root_both_zero() {
let a = LinearBinomial::new([0, 0]);
assert_eq!(Roots::InfiniteRoots, a.root());
}
#[test]
fn test_root_constant() {
let a = LinearBinomial::new([0, 1]);
assert_eq!(Roots::NoRoots, a.root());
}
#[test]
fn test_root_both() {
let a = LinearBinomial::new([1, 2]);
assert_eq!(Roots::OneRealRoot(-2), a.root());
}
#[test]
fn test_degree_both_zero() {
let a = LinearBinomial::<i32>::zero();
assert_eq!(Degree::NegInf, a.degree());
}
#[test]
fn test_degree_second_non_zero() {
let a = LinearBinomial::new([0, 1u8]);
assert_eq!(Degree::Num(0), a.degree());
}
#[test]
fn test_degree_first_non_zero() {
let a = LinearBinomial::new([1u8, 0]);
assert_eq!(Degree::Num(1), a.degree());
}
#[test]
fn test_degree_both_non_zero() {
let a = LinearBinomial::new([2u8, 1u8]);
assert_eq!(Degree::Num(1), a.degree());
}
#[test]
fn test_eval() {
let a = LinearBinomial::new([5, 0]);
assert_eq!(25, a.eval(5));
}
#[test]
fn test_shr_pos() {
let a = LinearBinomial::new([1, 0]);
let c = LinearBinomial::new([0, 1]);
assert_eq!(a >> 1, c);
}
#[test]
fn test_shr_assign_pos() {
let mut a = LinearBinomial::new([1, 0]);
let c = LinearBinomial::new([0, 1]);
a >>= 1;
assert_eq!(a, c);
}
#[test]
fn test_shr_to_zero() {
let a = LinearBinomial::new([1, 2]);
assert_eq!(a >> 5, LinearBinomial::zero());
}
#[test]
fn test_shr_assign_to_zero() {
let mut a = LinearBinomial::new([1, 2]);
a >>= 5;
assert_eq!(a, LinearBinomial::zero());
}
#[test]
fn test_derivative_of_zero() {
let a: LinearBinomial<i32> = LinearBinomial::zero();
assert_eq!(a.derivative(), LinearBinomial::zero());
}
#[test]
fn test_derivative_of_degree_zero() {
let a = LinearBinomial::new([0, 1]);
assert_eq!(a.derivative(), LinearBinomial::zero());
}
#[test]
fn test_derivative() {
let a = LinearBinomial::new([1, 3]);
assert_eq!(a.derivative(), LinearBinomial::new([0, 1]));
}
}