use super::{PzMap, TransferFunction};
use crate::Domain;
use crate::Polynomial;
use num_complex::Complex;
use num_traits::Float;
impl<T: Float + std::fmt::Display, D> std::fmt::Display for PzMap<T, D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let write_complex = |f: &mut std::fmt::Formatter<'_>, c: &Complex<T>| {
let sign = if c.im.is_sign_negative() { '-' } else { '+' };
match f.precision() {
Some(p) => writeln!(f, " {:.*} {} j{:.*}", p, c.re, sign, p, c.im.abs()),
None => writeln!(f, " {} {} j{}", c.re, sign, c.im.abs()),
}
};
writeln!(f, "Poles:")?;
for pole in &self.poles {
write_complex(f, pole)?;
}
writeln!(f, "Zeros:")?;
for zero in &self.zeros {
write_complex(f, zero)?;
}
Ok(())
}
}
impl<T: Float + std::fmt::Display, D: Domain> std::fmt::Display for TransferFunction<T, D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let numer = format_polynomial(&self.numerator, D::VARIABLE, f.precision());
let denom = format_polynomial(&self.denominator, D::VARIABLE, f.precision());
let paren = |s: String, terms: usize| if terms > 1 { format!("({})", s) } else { s };
let denom_terms = count_terms(&self.denominator);
let denom_is_one = denom_terms <= 1 && self.denominator.iter().next_back() == Some(&T::one());
let numer = paren(numer, if denom_is_one { 0 } else { count_terms(&self.numerator) });
if denom_is_one {
write!(f, "{}", numer)
} else {
write!(f, "{} / {}", numer, paren(denom, denom_terms))
}
}
}
fn count_terms<T: Float>(p: &Polynomial<T>) -> usize {
p.iter().filter(|c| !c.is_zero()).count()
}
fn format_polynomial<T: Float + std::fmt::Display>(
p: &Polynomial<T>,
variable: char,
precision: Option<usize>,
) -> String {
let fmt_num = |c: T| match precision {
Some(prec) => format!("{:.*}", prec, c),
None => {
let s = c.to_string();
if c.is_finite() && !s.contains('.') { s + ".0" } else { s }
}
};
let degree = p.len().saturating_sub(1);
let mut out = String::new();
for (i, &c) in p.iter().enumerate() {
if c.is_zero() {
continue;
}
let power = degree - i;
let sign = if c.is_sign_negative() { "-" } else { "+" };
let abs = c.abs();
let var = match power {
0 => String::new(),
1 => variable.to_string(),
n => format!("{}^{}", variable, n),
};
let term = if power == 0 {
fmt_num(abs)
} else if abs == T::one() {
var
} else {
format!("{} * {}", fmt_num(abs), var)
};
if out.is_empty() {
if sign == "-" {
out.push('-');
}
} else {
out.push_str(&format!(" {} ", sign));
}
out.push_str(&term);
}
if out.is_empty() { fmt_num(T::zero()) } else { out }
}