use core::cmp::Ordering;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy)]
pub struct Jet<T: Numeric, const N: usize> {
pub coeffs: [T; N],
}
impl<T: Numeric, const N: usize> Jet<T, N> {
#[inline]
pub fn new(coeffs: [T; N]) -> Self {
Jet { coeffs }
}
#[inline]
pub const fn constant(value: T) -> Self {
let mut coeffs = [T::ZERO; N];
coeffs[0] = value;
Jet { coeffs }
}
#[inline]
pub fn variable(value: T) -> Self {
const { assert!(N >= 2, "Jet::variable needs at least 2 coefficients") };
let mut coeffs = [T::ZERO; N];
coeffs[0] = value;
coeffs[1] = T::ONE;
Jet { coeffs }
}
#[inline]
pub fn value(&self) -> T {
self.coeffs[0]
}
#[inline]
pub fn coefficient(&self, k: usize) -> T {
self.coeffs[k]
}
#[inline]
pub fn derivative(&self, k: usize) -> T {
let mut factorial = T::ONE;
for i in 2..=k {
factorial *= T::from_usize(i);
}
factorial * self.coeffs[k]
}
}
impl<T: Numeric, const N: usize> Add for Jet<T, N> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Jet {
coeffs: core::array::from_fn(|k| self.coeffs[k] + rhs.coeffs[k]),
}
}
}
impl<T: Numeric, const N: usize> Sub for Jet<T, N> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Jet {
coeffs: core::array::from_fn(|k| self.coeffs[k] - rhs.coeffs[k]),
}
}
}
impl<T: Numeric, const N: usize> Mul for Jet<T, N> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Jet {
coeffs: core::array::from_fn(|k| {
let mut acc = T::ZERO;
for i in 0..=k {
acc += self.coeffs[i] * rhs.coeffs[k - i];
}
acc
}),
}
}
}
impl<T: Numeric, const N: usize> Div for Jet<T, N> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
let b0 = rhs.coeffs[0];
let mut c = [T::ZERO; N];
for k in 0..N {
let mut acc = self.coeffs[k];
for (i, &ci) in c.iter().enumerate().take(k) {
acc -= ci * rhs.coeffs[k - i];
}
c[k] = acc / b0;
}
Jet { coeffs: c }
}
}
impl<T: Numeric, const N: usize> Neg for Jet<T, N> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Jet {
coeffs: core::array::from_fn(|k| -self.coeffs[k]),
}
}
}
impl<T: Numeric, const N: usize> AddAssign for Jet<T, N> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<T: Numeric, const N: usize> SubAssign for Jet<T, N> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<T: Numeric, const N: usize> MulAssign for Jet<T, N> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: Numeric, const N: usize> DivAssign for Jet<T, N> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<T: Numeric, const N: usize> PartialEq for Jet<T, N> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.coeffs[0] == other.coeffs[0]
}
}
impl<T: Numeric, const N: usize> PartialOrd for Jet<T, N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.coeffs[0].partial_cmp(&other.coeffs[0])
}
}
impl<T: Numeric, const N: usize> Jet<T, N> {
#[inline]
fn sin_cos(self) -> (Self, Self) {
let v = &self.coeffs;
let mut s = [T::ZERO; N];
let mut c = [T::ZERO; N];
s[0] = v[0].sin();
c[0] = v[0].cos();
for k in 1..N {
let mut sk = T::ZERO;
let mut ck = T::ZERO;
for i in 1..=k {
let weighted = T::from_usize(i) * v[i];
sk += weighted * c[k - i];
ck += weighted * s[k - i];
}
let kth = T::from_usize(k);
s[k] = sk / kth;
c[k] = -(ck / kth);
}
(Jet { coeffs: s }, Jet { coeffs: c })
}
}
impl<T: Numeric, const N: usize> Numeric for Jet<T, N> {
const ZERO: Self = Self::constant(T::ZERO);
const ONE: Self = Self::constant(T::ONE);
const TWO: Self = Self::constant(T::TWO);
const HALF: Self = Self::constant(T::HALF);
const PI: Self = Self::constant(T::PI);
const EPSILON: Self = Self::constant(T::EPSILON);
const NAN: Self = Self::constant(T::NAN);
const INFINITY: Self = Self::constant(T::INFINITY);
const NEG_INFINITY: Self = Self::constant(T::NEG_INFINITY);
const MAX: Self = Self::constant(T::MAX);
const MIN_POSITIVE: Self = Self::constant(T::MIN_POSITIVE);
#[inline]
fn from_f64(value: f64) -> Self {
Self::constant(T::from_f64(value))
}
#[inline]
fn from_u64(value: u64) -> Self {
Self::constant(T::from_u64(value))
}
#[inline]
fn from_usize(value: usize) -> Self {
Self::constant(T::from_usize(value))
}
#[inline]
fn abs(self) -> Self {
let sign = if self.coeffs[0] < T::ZERO {
-T::ONE
} else {
T::ONE
};
Jet {
coeffs: core::array::from_fn(|k| sign * self.coeffs[k]),
}
}
#[inline]
fn sqrt(self) -> Self {
let v = &self.coeffs;
let mut u = [T::ZERO; N];
u[0] = v[0].sqrt();
for k in 1..N {
let mut acc = T::ZERO;
for i in 1..k {
acc += u[i] * u[k - i];
}
u[k] = (v[k] - acc) / (T::TWO * u[0]);
}
Jet { coeffs: u }
}
#[inline]
fn sin(self) -> Self {
self.sin_cos().0
}
#[inline]
fn cos(self) -> Self {
self.sin_cos().1
}
#[inline]
fn tan(self) -> Self {
let (sin, cos) = self.sin_cos();
sin / cos
}
#[inline]
fn exp(self) -> Self {
let v = &self.coeffs;
let mut u = [T::ZERO; N];
u[0] = v[0].exp();
for k in 1..N {
let mut acc = T::ZERO;
for i in 1..=k {
acc += T::from_usize(i) * v[i] * u[k - i];
}
u[k] = acc / T::from_usize(k);
}
Jet { coeffs: u }
}
#[inline]
fn ln(self) -> Self {
let v = &self.coeffs;
let mut u = [T::ZERO; N];
u[0] = v[0].ln();
for k in 1..N {
let mut acc = T::ZERO;
for j in 1..k {
acc += T::from_usize(j) * u[j] * v[k - j];
}
u[k] = (v[k] - acc / T::from_usize(k)) / v[0];
}
Jet { coeffs: u }
}
#[inline]
fn is_nan(self) -> bool {
self.coeffs[0].is_nan()
}
#[inline]
fn is_finite(self) -> bool {
self.coeffs[0].is_finite()
}
}