use crate::Accelerator;
use crate::DomainError;
use crate::InterpType;
use crate::Interpolation;
use crate::InterpolationError;
use crate::types::utils::{check_if_inbounds, check1d_data};
const MIN_SIZE: usize = 3;
pub struct Steffen;
impl<T> InterpType<T> for Steffen
where
T: crate::Num,
{
type Interpolation = SteffenInterp<T>;
fn build(&self, xa: &[T], ya: &[T]) -> Result<SteffenInterp<T>, InterpolationError> {
check1d_data(xa, ya, MIN_SIZE)?;
let size = xa.len();
let h0 = xa[1] - xa[0];
let s0 = (ya[1] - ya[0]) / h0;
let mut y_prime = Vec::<T>::with_capacity(size);
y_prime.push(s0);
let one = T::one();
let half = T::from(0.5).unwrap();
for i in 1..(size - 1) {
let hi = xa[i + 1] - xa[i];
let him1 = xa[i] - xa[i - 1];
let si = (ya[i + 1] - ya[i]) / hi;
let sim1 = (ya[i] - ya[i - 1]) / him1;
let pi = (sim1 * hi + si * him1) / (him1 + hi);
let min1 = si.abs().min(half * pi.abs());
let min2 = sim1.abs().min(min1);
y_prime.push((steffen_copysign(one, sim1) + steffen_copysign(one, si)) * min2);
}
y_prime.push((ya[size - 1] - ya[size - 2]) / (xa[size - 1] - xa[size - 2]));
let mut a = Vec::<T>::with_capacity(size - 1);
let mut b = Vec::<T>::with_capacity(size - 1);
let mut c = Vec::<T>::with_capacity(size - 1);
let mut d = Vec::<T>::with_capacity(size - 1);
let two = T::from(2.0).unwrap();
let three = T::from(3.0).unwrap();
for i in 0..(size - 1) {
let hi = xa[i + 1] - xa[i];
let si = (ya[i + 1] - ya[i]) / hi;
a.push((y_prime[i] + y_prime[i + 1] - two * si) / hi.powi(2));
b.push((three * si - two * y_prime[i] - y_prime[i + 1]) / hi);
c.push(y_prime[i]);
d.push(ya[i]);
}
let state = SteffenInterp {
a,
b,
c,
d,
y_prime,
};
Ok(state)
}
fn name(&self) -> &str {
"Steffen"
}
fn min_size(&self) -> usize {
MIN_SIZE
}
}
#[allow(dead_code)]
pub struct SteffenInterp<T>
where
T: crate::Num,
{
a: Vec<T>,
b: Vec<T>,
c: Vec<T>,
d: Vec<T>,
y_prime: Vec<T>,
}
impl<T> Interpolation<T> for SteffenInterp<T>
where
T: crate::Num,
{
#[allow(unused_variables)]
fn eval(&self, xa: &[T], ya: &[T], x: T, acc: &mut Accelerator) -> Result<T, DomainError> {
check_if_inbounds(xa, x)?;
let index = acc.find(xa, x);
let xlo = xa[index];
let delx = x - xlo;
let a = self.a[index];
let b = self.b[index];
let c = self.c[index];
let d = self.d[index];
Ok(d + delx * (c + delx * (b + delx * a)))
}
#[allow(unused_variables)]
fn eval_deriv(
&self,
xa: &[T],
ya: &[T],
x: T,
acc: &mut Accelerator,
) -> Result<T, DomainError> {
check_if_inbounds(xa, x)?;
let index = acc.find(xa, x);
let xlo = xa[index];
let delx = x - xlo;
let a = self.a[index];
let b = self.b[index];
let c = self.c[index];
let two = T::from(2.0).unwrap();
let three = T::from(3.0).unwrap();
Ok(c + delx * (two * b + delx * three * a))
}
#[allow(unused_variables)]
fn eval_deriv2(
&self,
xa: &[T],
ya: &[T],
x: T,
acc: &mut Accelerator,
) -> Result<T, DomainError> {
check_if_inbounds(xa, x)?;
let index = acc.find(xa, x);
let xlo = xa[index];
let delx = x - xlo;
let a = self.a[index];
let b = self.b[index];
let two = T::from(2.0).unwrap();
let six = T::from(6.0).unwrap();
Ok(six * delx * a + two * b)
}
#[allow(unused_variables)]
fn eval_integ(
&self,
xa: &[T],
ya: &[T],
a: T,
b: T,
acc: &mut Accelerator,
) -> Result<T, DomainError> {
check_if_inbounds(xa, a)?;
check_if_inbounds(xa, b)?;
let index_a = acc.find(xa, a);
let index_b = acc.find(xa, b);
let quarter = T::from(0.25).unwrap();
let half = T::from(0.5).unwrap();
let third = T::from(1.0 / 3.0).unwrap();
let mut result = T::zero();
for i in index_a..=index_b {
let xlo = xa[i];
let xhi = xa[i + 1];
let dx = xhi - xlo;
if dx.is_zero() {
continue;
}
let x1 = if i == index_a { a - xlo } else { T::zero() };
let x2 = if i == index_b { b - xlo } else { xhi - xlo };
let x12 = x1.powi(2);
let x22 = x2.powi(2);
result += quarter * self.a[i] * (x22.powi(2) - x12.powi(2));
result += third * self.b[i] * (x22 * x2 - x12 * x1);
result += half * self.c[i] * (x22 - x12);
result += self.d[i] * (x2 - x1);
}
Ok(result)
}
}
fn steffen_copysign<T>(x: T, y: T) -> T
where
T: crate::Num,
{
if (x.is_sign_negative() & y.is_sign_positive()) | (x.is_sign_positive() & y.is_sign_negative())
{
-x
} else {
x
}
}