use std::convert::Infallible;
use std::ops::{AddAssign, MulAssign};
use num_traits::Float;
use nalgebra::{ComplexField, DMatrix, RealField};
use super::Method;
use crate::{Continuous, Discrete, Polynomial, StateSpace, StateSpaceError, TransferFunction};
use crate::math::binomial_coefficient;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct BackwardDifference;
impl<T: Float + AddAssign + MulAssign> Method<T, TransferFunction<T, Continuous>> for BackwardDifference {
type Output = TransferFunction<T, Discrete>;
type Error = Infallible;
fn apply(&self, tf: &TransferFunction<T, Continuous>, ts: T) -> Result<Self::Output, Self::Error> {
Ok(transform(tf, ts))
}
}
impl<T: Float + ComplexField + RealField> Method<T, StateSpace<T, Continuous>> for BackwardDifference {
type Output = StateSpace<T, Discrete>;
type Error = StateSpaceError;
fn apply(&self, system: &StateSpace<T, Continuous>, ts: T) -> Result<Self::Output, Self::Error> {
let n = system.order.system;
let identity = DMatrix::<T>::identity(n, n);
let m = if n == 0 { identity.clone() } else { (&identity - system.a.scale(ts)).try_inverse().ok_or(StateSpaceError::SingularMatrix)? };
let b = (&m * &system.b).scale(ts);
let c = &system.c * &m;
let d = &system.d + (&c * &system.b).scale(ts);
StateSpace::new(m, b, c, d)
}
}
fn substitute<T: Float + AddAssign>(coeffs: &Polynomial<T>, n: usize, alpha: T) -> Polynomial<T> {
let mut poly = Polynomial::zeros(n);
for (k, &ck) in coeffs.iter().rev().enumerate() {
if ck != T::zero() {
let scaler = ck * alpha.powi(k as i32);
for i in 0..=k {
let term = scaler * T::from(binomial_coefficient(k, i)).unwrap();
poly[i] += if i % 2 == 0 { term } else { -term };
}
}
}
poly
}
fn transform<T: Float + AddAssign + MulAssign>(tf: &TransferFunction<T, Continuous>, ts: T) -> TransferFunction<T, Discrete> {
let n = tf.denominator.len() - 1;
let alpha = T::one() / ts;
let mut numer_z = substitute(&tf.numerator, n, alpha);
let mut denom_z = substitute(&tf.denominator, n, alpha);
let scale = T::one() / denom_z[0];
numer_z *= scale;
denom_z *= scale;
TransferFunction::from_polynomials(numer_z, denom_z)
}