use std::collections::HashMap;
use super::basic; use super::series;
use num_complex::Complex64;
#[derive(Clone)]
pub struct Poly {
coef: HashMap<i32, f64>,
l: Option<f64>,
compute_fn: fn(&Self, f64) -> f64,
compute_fnc: fn(&Self, Complex64) -> Complex64
}
impl std::fmt::Debug for Poly {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "{:?}", self.coef)?;
writeln!(f, "{:?}", self.l)?;
Ok(())
}
}
impl std::fmt::Display for Poly {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "Power :: Factor")?;
for (power, factor) in &self.coef {
writeln!(f, "{:5} :: {}", power, factor)?;
}
Ok(())
}
}
impl Default for Poly {
fn default() -> Self {
Self {
coef: HashMap::new(),
l: None,
compute_fn: Self::compute_base,
compute_fnc: Self::compute_base_complex,
}
}
}
impl Poly {
pub fn from(pow_fac: &[(usize, f64)]) -> Self {
let coef: HashMap<i32, f64> = pow_fac.iter().map(|(p, f)| (*p as i32, *f)).collect();
Self {
coef,
..Self::default()
}
}
pub fn gen_legendre(n: usize, l: isize) -> Self {
assert!(l.abs() as usize <= n, "l must be between -n and n!");
let mut poly = Self::legendre(n);
if l == 0 {
return poly;
}
poly.l = Some(l.abs() as f64);
let pre_f: f64 = if l >= 0 {
(-1_f64).powi(l as i32)
} else {
basic::factorial((n as isize + l) as usize) as f64 / basic::factorial((n as isize - l) as usize) as f64
};
poly.derive(l.abs() as usize);
poly *= pre_f;
if l.abs() % 2 != 0 {
poly.compute_fn = Self::compute_legendre;
poly.compute_fnc = Self::compute_legendre_complex;
return poly;
} else {
let pre_poly = Poly::from(&[(0, 1.0), (2, -1.0)]);
return poly * pre_poly.pow(l.abs() as usize / 2);
}
}
pub fn legendre(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = HashMap::new();
for k in 0..=(n / 2) {
let c: f64 = ((-1.0_f64).powi(k as i32) * (basic::binomial(n, k) * basic::binomial(2*n - 2*k, n)) as f64) / 2.0_f64.powi(n as i32);
coef.insert((n - 2 * k) as i32, c);
}
Self {
coef,
..Self::default()
}
}
pub fn laguerre<U>(n: usize, l: U) -> Self
where U: Into<f64> {
let alpha: f64 = l.into();
let mut coef: HashMap<i32, f64> = HashMap::new();
for i in (0..=n).rev() {
let c: f64 = (-1.0_f64).powi(i as i32) * basic::binomial_reduced(n as f64 + alpha, n - i) as f64 / basic::factorial(i) as f64;
coef.insert(i as i32, c);
}
Self {
coef,
l: Some(alpha),
..Self::default()
}
}
pub fn bernoulli(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = HashMap::new();
for k in 0..=n {
let c: f64 = basic::binomial(n, k) as f64 * Self::bernoulli_number(n - k);
coef.insert(k as i32, c);
}
Self {
coef,
..Self::default()
}
}
pub fn euler(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = (0..=n).map(|x| (x as i32, 0.0)).collect();
for k in 0..=n {
let binom: f64 = basic::binomial(n, k) as f64;
let f: f64 = Self::euler_number(k) / 2.0_f64.powi(k as i32);
for p in 0..=(n-k) {
let pre: f64 = (-0.5_f64).powi(p as i32);
let triangle_val: f64 = basic::binomial(n-k, p) as f64;
let id: i32 = (n-k-p) as i32;
if let Some(fact) = coef.get_mut(&id) {
*fact += binom * f * pre * triangle_val;
}
}
}
Self {
coef,
..Self::default()
}
}
pub fn factorial_rising(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = (0..=n).rev().map(|k| {
(k as i32, Poly::stirling_number(n, k) as f64)
}).collect();
if n > 0 {
coef.remove(&0);
}
Self {
coef,
..Self::default()
}
}
pub fn factorial_falling(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = (0..=n).rev().map(|k| {
(k as i32, Poly::stirling_number_signed(n, k) as f64)
}).collect();
if n > 0 {
coef.remove(&0);
}
Self {
coef,
..Self::default()
}
}
pub fn bessel(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = HashMap::new();
let mut kf: usize = 1; let mut twos: usize = 1; let mut top: usize = basic::factorial(n); let mut bot: usize = top; let mut c: f64; coef.insert(0, 1.0);
for k in 1..=n {
kf *= k;
top *= n + k;
twos *= 2;
bot /= n - k + 1;
c = top as f64 / (twos * bot * kf) as f64;
coef.insert(k as i32, c);
}
Self {
coef,
..Self::default()
}
}
pub fn hermite(n: usize) -> Self {
let mut coef: HashMap<i32, f64> = HashMap::new();
let nf: usize = basic::factorial(n);
let mut n2m: usize;
let mut sign: isize = 1;
let mut twos: usize = 2_usize.pow(n as u32);
let mut mf: usize = 1;
let mut c: f64;
coef.insert(n as i32, twos as f64);
for m in 1..=(n / 2) {
sign *= -1;
twos = 2_usize.pow((n - 2 * m) as u32);
mf *= m;
n2m = basic::factorial(n - 2 * m);
c = sign as f64 * (nf * twos) as f64 / (mf * n2m) as f64;
coef.insert((n - 2 * m) as i32, c);
}
Self {
coef,
..Self::default()
}
}
pub fn derive(&mut self, m: usize) {
for _ in 1..=m {
let mut temp_c: HashMap<i32, f64> = HashMap::new();
for (p, f) in &self.coef {
match p {
0 => { },
_ => { temp_c.insert(p - 1, f * *p as f64); }
}
}
self.coef = temp_c;
}
}
pub fn integrate(&mut self, m: usize, coef: &[f64]) {
let n_coef: Vec<f64> = if coef.len() > 0 {
assert_eq!(coef.len(), m); coef.into() } else {
vec![1.0; m] };
for n_f in n_coef {
let mut temp_c: HashMap<i32, f64> = HashMap::new();
for (p, f) in &self.coef {
temp_c.insert(p + 1, f / (p + 1) as f64);
}
temp_c.insert(0, n_f);
self.coef = temp_c;
}
}
pub fn pow(&self, n: usize) -> Poly {
let mut res = Poly::from(&[(0, 1.0)]);
for _ in 1..=n { res *= self.clone();
}
res
}
pub fn get_coefs(&self) -> HashMap<i32, f64> {
self.coef.clone()
}
pub fn compute(&self, x: f64) -> f64 {
(self.compute_fn)(&self, x)
}
fn compute_base(&self, x: f64) -> f64 {
self.coef.iter().fold(0.0, |res, (p, f)| res + f * x.powi(*p))
}
fn compute_legendre(&self, x: f64) -> f64 {
let pre: f64 = (1.0 - x.powi(2)).powf(self.l.unwrap() / 2.0);
self.coef.iter().fold(0.0, |res, (p, f)| res + f * x.powi(*p)) * pre
}
pub fn compute_complex(&self, z: Complex64) -> Complex64 {
(self.compute_fnc)(&self, z)
}
fn compute_base_complex(&self, z: Complex64) -> Complex64 {
self.coef.iter().fold(Complex64::default(), |res, (p, f)| res + f * z.powi(*p))
}
fn compute_legendre_complex(&self, z: Complex64) -> Complex64 {
let pre: Complex64 = (1.0 - z.powi(2)).powf(self.l.unwrap() / 2.0);
self.coef.iter().fold(Complex64::default(), |res, (p, f)| res + f * z.powi(*p)) * pre
}
pub fn get_order(&self) -> i32 {
let p: Vec<i32> = self.coef.keys().map(|x| *x).collect();
series::max_slice(&p)
}
pub fn bernoulli_number(m: usize) -> f64 {
let mut res: f64 = 0.0;
for k in 0..=m {
let k1: f64 = (k + 1) as f64;
for v in 0..=k {
let sign: f64 = (-1.0_f64).powi(v as i32); let binom: f64 = basic::binomial(k, v) as f64; let frac: f64 = (v as f64).powi(m as i32) / k1;
res += sign * binom * frac; }
}
res
}
pub fn euler_number(m: usize) -> f64 {
let mut res: f64 = 0.0;
if m % 2 != 0 {
return res;
} else if m == 0 {
return 1.0;
}
for i in 1..=m {
let sign: f64 = (-1.0_f64).powi(i as i32);
let fact: f64 = 1.0 / 2.0_f64.powi(i as i32);
let mut term: f64 = 0.0;
for l in 0..=(2*i) {
let sign2: f64 = (-1.0_f64).powi(l as i32);
let binom: f64 = basic::binomial(2 * i, l) as f64;
let par: f64 = (i as f64 - l as f64).powi(m as i32);
term += sign2 * binom * par;
}
res += sign * fact * term;
}
res
}
pub fn stirling_number(n: usize, k: usize) -> usize {
if n == 0 && k == 0 {
1
} else if n == 0 || k == 0 {
0
} else {
(n - 1) * Poly::stirling_number(n - 1, k) + Poly::stirling_number(n - 1, k - 1)
}
}
pub fn stirling_number_signed(n: usize, k: usize) -> i32 {
let res = Self::stirling_number(n, k) as i32;
(-1.0_f64).powi((n - k) as i32) as i32 * res
}
}
impl std::cmp::PartialEq for Poly {
fn eq(&self, other: &Self) -> bool {
let c: bool = self.coef == other.coef;
let l: bool = self.l == other.l;
c & l
}
}
impl<T: Into<f64>> std::ops::Add<T> for Poly {
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
let mut coef = self.coef.clone();
match coef.get_mut(&0) {
Some(v) => *v += rhs.into(),
None => { coef.insert(0, rhs.into()); }
}
Self {
coef,
..self
}
}
}
impl std::ops::Add<Self> for Poly {
type Output = Self;
fn add(self, rhs: Poly) -> Self::Output {
let mut coef = self.coef.clone();
for (c, f) in &rhs.coef {
match coef.get_mut(c) {
Some(v) => *v += f,
None => { coef.insert(*c, *f); }
}
}
Poly {
coef,
..self
}
}
}
impl<T: Into<f64>> std::ops::AddAssign<T> for Poly {
fn add_assign(&mut self, rhs: T) {
match self.coef.get_mut(&0) {
Some(v) => *v += rhs.into(),
None => { self.coef.insert(0, rhs.into()); }
}
}
}
impl std::ops::AddAssign<Self> for Poly {
fn add_assign(&mut self, rhs: Self) {
for (c, f) in &rhs.coef {
match self.coef.get_mut(c) {
Some(v) => *v += f,
None => { self.coef.insert(*c, *f); }
}
}
}
}
impl<T: Into<f64>> std::ops::Sub<T> for Poly {
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
let mut coef = self.coef.clone();
match coef.get_mut(&0) {
Some(v) => *v -= rhs.into(),
None => { coef.insert(0, -rhs.into()); }
}
Self {
coef,
..self
}
}
}
impl std::ops::Sub<Self> for Poly {
type Output = Self;
fn sub(self, rhs: Poly) -> Self::Output {
let mut coef = self.coef.clone();
for (c, f) in &rhs.coef {
match coef.get_mut(c) {
Some(v) => *v -= f,
None => { coef.insert(*c, -*f); }
}
}
Poly {
coef,
..self
}
}
}
impl<T: Into<f64>> std::ops::SubAssign<T> for Poly {
fn sub_assign(&mut self, rhs: T) {
match self.coef.get_mut(&0) {
Some(v) => *v -= rhs.into(),
None => { self.coef.insert(0, -rhs.into()); }
}
}
}
impl std::ops::SubAssign<Self> for Poly {
fn sub_assign(&mut self, rhs: Self) {
for (c, f) in &rhs.coef {
match self.coef.get_mut(c) {
Some(v) => *v -= f,
None => { self.coef.insert(*c, -*f); }
}
}
}
}
impl<T: Into<f64>> std::ops::Mul<T> for Poly {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
let rhs_conv: f64 = rhs.into();
let mut coef = self.coef.clone();
for f in coef.values_mut() {
*f *= rhs_conv;
}
Self {
coef,
..self
}
}
}
impl std::ops::Mul<Self> for Poly {
type Output = Self;
fn mul(self, rhs: Poly) -> Self::Output {
let mut n_p: i32;
let mut n_f: f64;
let mut coef: HashMap<i32, f64> = HashMap::new();
for (p, f) in self.coef.iter() {
for (rhs_p, rhs_f) in &rhs.coef {
n_p = p + rhs_p;
n_f = f * rhs_f;
match coef.get_mut(&n_p) {
Some(v) => *v += n_f,
None => { coef.insert(n_p, n_f); }
}
}
};
Poly {
coef,
..self
}
}
}
impl<T: Into<f64>> std::ops::MulAssign<T> for Poly {
fn mul_assign(&mut self, rhs: T) {
let rhs_conv: f64 = rhs.into();
for f in self.coef.values_mut() {
*f *= rhs_conv;
}
}
}
impl std::ops::MulAssign<Self> for Poly {
fn mul_assign(&mut self, rhs: Self) {
let mut n_p: i32;
let mut n_f: f64;
let mut coef: HashMap<i32, f64> = HashMap::new();
for (p, f) in self.coef.iter() {
for (rhs_p, rhs_f) in &rhs.coef {
n_p = p + rhs_p;
n_f = f * rhs_f;
match coef.get_mut(&n_p) {
Some(v) => *v += n_f,
None => { coef.insert(n_p, n_f); }
}
}
};
self.coef = coef;
}
}
impl<T: Into<f64>> std::ops::Div<T> for Poly {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
let rhs_conv: f64 = rhs.into();
let mut coef = self.coef.clone();
for f in coef.values_mut() {
*f /= rhs_conv;
}
Self {
coef,
..self
}
}
}
impl<T: Into<f64>> std::ops::DivAssign<T> for Poly {
fn div_assign(&mut self, rhs: T) {
let rhs_conv: f64 = rhs.into();
for f in self.coef.values_mut() {
*f /= rhs_conv;
}
}
}
impl std::ops::Neg for Poly {
type Output = Self;
fn neg(self) -> Self::Output {
self * -1
}
}