use num_traits::Float;
#[inline]
pub fn taylor_add<F: Float>(a: &[F], b: &[F], c: &mut [F]) {
for k in 0..c.len() {
c[k] = a[k] + b[k];
}
}
#[inline]
pub fn taylor_sub<F: Float>(a: &[F], b: &[F], c: &mut [F]) {
for k in 0..c.len() {
c[k] = a[k] - b[k];
}
}
#[inline]
pub fn taylor_neg<F: Float>(a: &[F], c: &mut [F]) {
for k in 0..c.len() {
c[k] = -a[k];
}
}
#[inline]
pub fn taylor_scale<F: Float>(a: &[F], s: F, c: &mut [F]) {
for k in 0..c.len() {
c[k] = s * a[k];
}
}
#[inline]
pub fn taylor_mul<F: Float>(a: &[F], b: &[F], c: &mut [F]) {
let n = c.len();
for k in 0..n {
let mut sum = F::zero();
for j in 0..=k {
sum = sum + a[j] * b[k - j];
}
c[k] = sum;
}
}
#[inline]
pub fn taylor_div<F: Float>(a: &[F], b: &[F], c: &mut [F]) {
let n = c.len();
let inv_b0 = F::one() / b[0];
for k in 0..n {
let mut sum = a[k];
for j in 1..=k {
sum = sum - b[j] * c[k - j];
}
c[k] = sum * inv_b0;
}
}
#[inline]
pub fn taylor_recip<F: Float>(a: &[F], c: &mut [F]) {
let n = c.len();
let inv_a0 = F::one() / a[0];
c[0] = inv_a0;
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + a[j] * c[k - j];
}
c[k] = -sum * inv_a0;
}
}
#[inline]
pub fn taylor_exp<F: Float>(a: &[F], c: &mut [F]) {
let n = c.len();
c[0] = a[0].exp();
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * c[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_ln<F: Float>(a: &[F], c: &mut [F]) {
let n = c.len();
let inv_a0 = F::one() / a[0];
c[0] = a[0].ln();
for k in 1..n {
let mut sum = F::zero();
for j in 1..k {
sum = sum + F::from(j).unwrap() * c[j] * a[k - j];
}
c[k] = (a[k] - sum / F::from(k).unwrap()) * inv_a0;
}
}
#[inline]
pub fn taylor_sqrt<F: Float>(a: &[F], c: &mut [F]) {
let n = c.len();
if a[0] == F::zero() {
c[0] = F::zero();
for ci in c.iter_mut().skip(1) {
*ci = F::infinity();
}
return;
}
if a[0] < F::zero() {
for ci in c.iter_mut() {
*ci = F::nan();
}
return;
}
c[0] = a[0].sqrt();
let two_c0 = F::from(2.0).unwrap() * c[0];
for k in 1..n {
let mut sum = F::zero();
for j in 1..k {
sum = sum + c[j] * c[k - j];
}
c[k] = (a[k] - sum) / two_c0;
}
}
#[inline]
pub fn taylor_sin_cos<F: Float>(a: &[F], s: &mut [F], co: &mut [F]) {
let n = s.len();
let (s0, c0) = a[0].sin_cos();
s[0] = s0;
co[0] = c0;
for k in 1..n {
let inv_k = F::one() / F::from(k).unwrap();
let mut sum_s = F::zero();
let mut sum_c = F::zero();
for j in 1..=k {
let jf = F::from(j).unwrap();
sum_s = sum_s + jf * a[j] * co[k - j];
sum_c = sum_c + jf * a[j] * s[k - j];
}
s[k] = sum_s * inv_k;
co[k] = -sum_c * inv_k;
}
}
#[inline]
pub fn taylor_sinh_cosh<F: Float>(a: &[F], sh: &mut [F], ch: &mut [F]) {
let n = sh.len();
sh[0] = a[0].sinh();
ch[0] = a[0].cosh();
for k in 1..n {
let inv_k = F::one() / F::from(k).unwrap();
let mut sum_sh = F::zero();
let mut sum_ch = F::zero();
for j in 1..=k {
let jf = F::from(j).unwrap();
sum_sh = sum_sh + jf * a[j] * ch[k - j];
sum_ch = sum_ch + jf * a[j] * sh[k - j];
}
sh[k] = sum_sh * inv_k;
ch[k] = sum_ch * inv_k;
}
}
#[inline]
pub fn taylor_atan<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let n = c.len();
taylor_mul(a, a, scratch1);
scratch2[..n].copy_from_slice(&scratch1[..n]);
scratch2[0] = F::one() + scratch1[0];
c[0] = a[0].atan();
taylor_recip(scratch2, scratch1);
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch1[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_asin<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let n = c.len();
c[0] = a[0].asin();
taylor_mul(a, a, scratch1);
scratch2[0] = (F::one() - a[0]) * (F::one() + a[0]);
for k in 1..n {
scratch2[k] = -scratch1[k];
}
taylor_sqrt(scratch2, scratch1);
taylor_recip(scratch1, scratch2);
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch2[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_acos<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
taylor_asin(a, c, scratch1, scratch2);
c[0] = a[0].acos();
for ck in c[1..].iter_mut() {
*ck = -*ck;
}
}
#[inline]
pub fn taylor_tan<F: Float>(a: &[F], c: &mut [F], scratch: &mut [F]) {
let n = c.len();
c[0] = a[0].tan();
scratch[0] = F::one() + c[0] * c[0];
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch[k - j];
}
c[k] = sum / F::from(k).unwrap();
let mut s_k = F::zero();
for j in 0..=k {
s_k = s_k + c[j] * c[k - j];
}
scratch[k] = s_k;
}
}
#[inline]
pub fn taylor_tanh<F: Float>(a: &[F], c: &mut [F], scratch: &mut [F]) {
let n = c.len();
c[0] = a[0].tanh();
scratch[0] = F::one() - c[0] * c[0];
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch[k - j];
}
c[k] = sum / F::from(k).unwrap();
let mut s_k = F::zero();
for j in 0..=k {
s_k = s_k + c[j] * c[k - j];
}
scratch[k] = -s_k;
}
}
#[inline]
pub fn taylor_asinh<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let n = c.len();
c[0] = a[0].asinh();
taylor_mul(a, a, scratch1);
scratch2[..n].copy_from_slice(&scratch1[..n]);
scratch2[0] = F::one() + scratch1[0];
taylor_sqrt(scratch2, scratch1);
taylor_recip(scratch1, scratch2);
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch2[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_acosh<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let n = c.len();
c[0] = a[0].acosh();
taylor_mul(a, a, scratch1);
scratch2[..n].copy_from_slice(&scratch1[..n]);
scratch2[0] = (a[0] - F::one()) * (a[0] + F::one());
taylor_sqrt(scratch2, scratch1);
taylor_recip(scratch1, scratch2);
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch2[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_atanh<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let n = c.len();
c[0] = a[0].atanh();
taylor_mul(a, a, scratch1);
scratch2[0] = (F::one() - a[0]) * (F::one() + a[0]);
for k in 1..n {
scratch2[k] = -scratch1[k];
}
taylor_recip(scratch2, scratch1);
for k in 1..n {
let mut sum = F::zero();
for j in 1..=k {
sum = sum + F::from(j).unwrap() * a[j] * scratch1[k - j];
}
c[k] = sum / F::from(k).unwrap();
}
}
#[inline]
pub fn taylor_powf<F: Float>(
a: &[F],
b: &[F],
c: &mut [F],
scratch1: &mut [F],
scratch2: &mut [F],
) {
if b[1..].iter().all(|&bk| bk == F::zero()) {
let b0 = b[0];
if let Some(ni) = b0.to_i32() {
if F::from(ni).unwrap() == b0 {
taylor_powi(a, ni, c, scratch1, scratch2);
return;
}
}
}
taylor_ln(a, scratch1);
taylor_mul(b, scratch1, scratch2);
taylor_exp(scratch2, c);
c[0] = a[0].powf(b[0]);
}
#[inline]
pub fn taylor_powi<F: Float>(a: &[F], n: i32, c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let deg = c.len();
if n == 0 {
c[0] = F::one();
for ck in c[1..deg].iter_mut() {
*ck = F::zero();
}
return;
}
if n == 1 {
c.copy_from_slice(a);
return;
}
if n == -1 {
taylor_recip(a, c);
return;
}
if a[0] <= F::zero() || n.unsigned_abs() <= 8 {
taylor_powi_squaring(a, n, c, scratch1, scratch2);
} else {
taylor_ln(a, scratch1);
let nf = F::from(n).unwrap();
taylor_scale(scratch1, nf, scratch2);
taylor_exp(scratch2, c);
c[0] = a[0].powi(n);
}
}
fn taylor_powi_squaring<F: Float>(
a: &[F],
n: i32,
c: &mut [F],
scratch1: &mut [F],
scratch2: &mut [F],
) {
let deg = c.len();
let abs_n = n.unsigned_abs();
c[0] = F::one();
for ck in c[1..deg].iter_mut() {
*ck = F::zero();
}
scratch1[..deg].copy_from_slice(&a[..deg]);
let mut power = abs_n;
while power > 0 {
if power & 1 == 1 {
taylor_mul(c, &*scratch1, scratch2);
c[..deg].copy_from_slice(&scratch2[..deg]);
}
power >>= 1;
if power > 0 {
let base_ref: &[F] = &*scratch1;
for k in 0..deg {
let mut sum = F::zero();
for j in 0..=k {
sum = sum + base_ref[j] * base_ref[k - j];
}
scratch2[k] = sum;
}
scratch1[..deg].copy_from_slice(&scratch2[..deg]);
}
}
if n < 0 {
scratch1[..deg].copy_from_slice(&c[..deg]);
taylor_recip(scratch1, c);
}
}
#[inline]
pub fn taylor_cbrt<F: Float>(a: &[F], c: &mut [F], scratch1: &mut [F], scratch2: &mut [F]) {
let deg = c.len();
debug_assert_eq!(a.len(), c.len());
if a[0] == F::zero() {
c[0] = F::zero();
for ci in c.iter_mut().skip(1) {
*ci = F::infinity();
}
return;
}
if a[0] < F::zero() {
for i in 0..deg {
c[i] = -a[i];
}
let three = F::from(3.0).unwrap();
let third = F::one() / three;
taylor_ln(c, scratch1);
taylor_scale(scratch1, third, scratch2);
taylor_exp(scratch2, c);
c[0] = a[0].cbrt();
for ci in c.iter_mut().skip(1) {
*ci = -*ci;
}
} else {
let three = F::from(3.0).unwrap();
let third = F::one() / three;
taylor_ln(a, scratch1);
taylor_scale(scratch1, third, scratch2);
taylor_exp(scratch2, c);
c[0] = a[0].cbrt();
}
}
#[inline]
pub fn taylor_exp2<F: Float>(a: &[F], c: &mut [F], scratch: &mut [F]) {
let ln2 = F::from(2.0).unwrap().ln();
taylor_scale(a, ln2, scratch);
taylor_exp(scratch, c);
c[0] = a[0].exp2();
}
#[inline]
pub fn taylor_exp_m1<F: Float>(a: &[F], c: &mut [F]) {
taylor_exp(a, c);
c[0] = a[0].exp_m1();
}
#[inline]
pub fn taylor_log2<F: Float>(a: &[F], c: &mut [F]) {
taylor_ln(a, c);
let inv_ln2 = F::one() / F::from(2.0).unwrap().ln();
c[0] = a[0].log2();
for ck in c[1..].iter_mut() {
*ck = *ck * inv_ln2;
}
}
#[inline]
pub fn taylor_log10<F: Float>(a: &[F], c: &mut [F]) {
taylor_ln(a, c);
let inv_ln10 = F::one() / F::from(10.0).unwrap().ln();
c[0] = a[0].log10();
for ck in c[1..].iter_mut() {
*ck = *ck * inv_ln10;
}
}
#[inline]
pub fn taylor_ln_1p<F: Float>(a: &[F], c: &mut [F], scratch: &mut [F]) {
let n = c.len();
debug_assert!(n >= 1, "taylor_ln_1p requires c.len() >= 1");
scratch[1..n].copy_from_slice(&a[1..n]);
scratch[0] = F::one() + a[0];
taylor_ln(scratch, c);
c[0] = a[0].ln_1p();
}
#[inline]
pub fn taylor_hypot<F: Float>(
a: &[F],
b: &[F],
c: &mut [F],
scratch1: &mut [F],
scratch2: &mut [F],
) {
let n = c.len();
let scale = a[0].abs().max(b[0].abs());
if scale == F::zero() {
if n > 1 && (a[1] != F::zero() || b[1] != F::zero()) {
let mut a_shifted = vec![F::zero(); n];
let mut b_shifted = vec![F::zero(); n];
a_shifted[..(n - 1)].copy_from_slice(&a[1..n]);
b_shifted[..(n - 1)].copy_from_slice(&b[1..n]);
let mut inner_c = vec![F::zero(); n];
let mut inner_s1 = vec![F::zero(); n];
let mut inner_s2 = vec![F::zero(); n];
taylor_hypot(
&a_shifted,
&b_shifted,
&mut inner_c,
&mut inner_s1,
&mut inner_s2,
);
c[0] = F::zero();
c[1..n].copy_from_slice(&inner_c[..(n - 1)]);
return;
}
taylor_mul(a, a, scratch1);
taylor_mul(b, b, scratch2);
for k in 0..n {
scratch1[k] = scratch1[k] + scratch2[k];
}
taylor_sqrt(scratch1, c);
c[0] = a[0].hypot(b[0]);
return;
}
let inv_scale = F::one() / scale;
for k in 0..n {
scratch1[k] = a[k] * inv_scale;
}
for k in 0..n {
scratch2[k] = b[k] * inv_scale;
}
taylor_mul(scratch1, scratch1, c);
taylor_mul(scratch2, scratch2, scratch1);
for k in 0..n {
c[k] = c[k] + scratch1[k];
}
taylor_sqrt(c, scratch1);
for k in 0..n {
c[k] = scratch1[k] * scale;
}
c[0] = a[0].hypot(b[0]);
}
#[inline]
pub fn taylor_atan2<F: Float>(
a: &[F],
b: &[F],
c: &mut [F],
scratch1: &mut [F],
scratch2: &mut [F],
scratch3: &mut [F],
) {
if b[0] != F::zero() {
taylor_div(a, b, scratch1);
taylor_atan(scratch1, c, scratch2, scratch3);
c[0] = a[0].atan2(b[0]);
} else if a[0] != F::zero() {
taylor_div(b, a, scratch1);
taylor_atan(scratch1, c, scratch2, scratch3);
for ck in c.iter_mut() {
*ck = -*ck;
}
c[0] = a[0].atan2(b[0]);
} else {
taylor_discontinuous(F::zero(), c);
}
}
#[inline]
pub fn taylor_discontinuous<F: Float>(val: F, c: &mut [F]) {
c[0] = val;
for ck in c[1..].iter_mut() {
*ck = F::zero();
}
}