pub const POLYNOMIAL_ORDER: usize = 6;
#[derive(Clone, Copy, Debug, Default)]
pub struct PolynomialCoefficients {
pub fwd: [[f64; POLYNOMIAL_ORDER]; POLYNOMIAL_ORDER],
pub inv: [[f64; POLYNOMIAL_ORDER]; POLYNOMIAL_ORDER],
}
#[derive(Clone, Copy, Debug, Default)]
pub struct FourierCoefficients {
pub fwd: [f64; POLYNOMIAL_ORDER],
pub inv: [f64; POLYNOMIAL_ORDER],
pub etc: [f64; 2],
}
pub mod taylor {
use super::FourierCoefficients;
use super::POLYNOMIAL_ORDER;
use super::PolynomialCoefficients;
pub fn fourier_coefficients(
arg: f64,
coefficients: &PolynomialCoefficients,
) -> FourierCoefficients {
let mut result = FourierCoefficients::default();
for i in 0..POLYNOMIAL_ORDER {
result.fwd[i] = arg * horner(arg, &coefficients.fwd[i]);
result.inv[i] = arg * horner(arg, &coefficients.inv[i]);
}
result
}
pub fn horner(arg: f64, coefficients: &[f64]) -> f64 {
if coefficients.is_empty() {
return 0.;
}
let mut coefficients = coefficients.iter().rev();
let mut value = *(coefficients.next().unwrap());
for c in coefficients {
value = value.mul_add(arg, *c);
}
value
}
}
pub mod fourier {
pub fn sin(arg: f64, coefficients: &[f64]) -> f64 {
let (sin_arg, cos_arg) = arg.sin_cos();
let x = 2.0 * cos_arg;
let mut c0 = 0.0;
let mut c1 = 0.0;
for c in coefficients.iter().rev() {
(c1, c0) = (c0, x.mul_add(c0, c - c1));
}
sin_arg * c0
}
pub fn cos(arg: f64, coefficients: &[f64]) -> f64 {
let cos_arg = arg.cos();
let x = 2.0 * cos_arg;
let mut c0 = 0.0;
let mut c1 = 0.0;
for c in coefficients.iter().rev() {
(c1, c0) = (c0, x.mul_add(c0, c - c1));
}
cos_arg * c0 - c1
}
#[allow(unused_assignments)] pub fn complex_sin(arg: [f64; 2], coefficients: &[f64]) -> [f64; 2] {
let (sin_r, cos_r) = arg[0].sin_cos();
let sinh_i = arg[1].sinh();
let cosh_i = arg[1].cosh();
let r = 2. * cos_r * cosh_i;
let i = -2. * sin_r * sinh_i;
let mut coefficients = coefficients.iter().rev();
let Some(c) = coefficients.next() else {
return [0.; 2];
};
let (mut hr2, mut hr1, mut hr) = (0., 0., *c);
let (mut hi2, mut hi1, mut hi) = (0., 0., 0.);
for c in coefficients {
(hr2, hi2, hr1, hi1) = (hr1, hi1, hr, hi);
hr = -hr2 + r * hr1 - i * hi1 + c;
hi = -hi2 + i * hr1 + r * hi1;
}
let r = sin_r * cosh_i;
let i = cos_r * sinh_i;
[r * hr - i * hi, r * hi + i * hr]
}
#[inline(always)]
pub fn sin_optimized_for_tmerc(trig: [f64; 2], coefficients: &[f64]) -> f64 {
let (sin_arg, cos_arg) = (trig[0], trig[1]);
let x = 2.0 * cos_arg;
let mut c0 = 0.0;
let mut c1 = 0.0;
for c in coefficients.iter().rev() {
(c1, c0) = (c0, x.mul_add(c0, c - c1));
}
sin_arg * c0
}
#[allow(unused_assignments)] #[inline(always)]
pub fn complex_sin_optimized_for_tmerc(
trig: [f64; 2],
hyp: [f64; 2],
coefficients: &[f64],
) -> [f64; 2] {
let (sin_r, cos_r) = (trig[0], trig[1]);
let (sinh_i, cosh_i) = (hyp[0], hyp[1]);
let r = 2. * cos_r * cosh_i;
let i = -2. * sin_r * sinh_i;
let mut coefficients = coefficients.iter().rev();
let Some(c) = coefficients.next() else {
return [0.; 2];
};
let (mut hr2, mut hr1, mut hr) = (0., 0., *c);
let (mut hi2, mut hi1, mut hi) = (0., 0., 0.);
for c in coefficients {
(hr2, hi2, hr1, hi1) = (hr1, hi1, hr, hi);
hr = -hr2 + r * hr1 - i * hi1 + c;
hi = -hi2 + i * hr1 + r * hi1;
}
let r = sin_r * cosh_i;
let i = cos_r * sinh_i;
[r * hr - i * hi, r * hi + i * hr]
}
}
#[cfg(test)]
mod tests {
use super::taylor::*;
use crate::authoring::*;
#[test]
fn test_horner() -> Result<(), Error> {
let coefficients = [1_f64, 2., 3.];
assert_eq!(horner(1., &coefficients), 6.);
assert_eq!(horner(2., &coefficients), 17.);
assert_eq!(horner(-2., &coefficients), 9.);
assert_eq!(horner(-2., &[1_f64]), 1.);
assert_eq!(horner(-2., &[3_f64]), 3.);
assert_eq!(horner(-2., &[]), 0.);
let e = Ellipsoid::named("GRS80")?;
let n = e.third_flattening();
let nn = n * n;
let d = [1., 1. / 4., 1. / 64., 1. / 256., 25. / 16384.];
let result = horner(nn, &d) / (1. + n);
let expected = 0.9983242984230415;
assert!((result - expected).abs() < 1e-14);
Ok(())
}
#[test]
fn test_clenshaw() -> Result<(), Error> {
use super::*;
let coefficients = [1., 2., 3.];
assert_eq!(fourier::sin(0., &[]), 0.);
assert_eq!(fourier::sin(1., &[]), 0.);
assert_eq!(fourier::sin(0.5, &[]), 0.);
let x = 30_f64.to_radians();
let result = 1.0 * x.sin() + 2.0 * (2.0 * x).sin() + 3.0 * (3.0 * x).sin();
assert!((fourier::sin(x, &coefficients) - result).abs() < 1e-14);
let result = 1.0 * x.cos() + 2.0 * (2.0 * x).cos() + 3.0 * (3.0 * x).cos();
assert!((fourier::cos(x, &coefficients) - result).abs() < 1e-14);
let coefficients = [6., 5., 4., 3., 2., 1.];
let arg = [30f64.to_radians(), 60f64.to_radians()];
let r = 248.658_846_388_817_7;
let i = -463.436_347_907_636_56;
let sum = fourier::complex_sin(arg, &coefficients);
assert!((sum[0] - r).abs() < 1e-14);
assert!((sum[1] - i).abs() < 1e-14);
Ok(())
}
}