use crate::error::{InterpolateError, InterpolateResult};
use crate::traits::InterpolationFloat;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
#[allow(dead_code)]
pub fn compute_natural_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n);
let mut b = Array1::<F>::zeros(n);
let mut c = Array1::<F>::zeros(n);
let mut d = Array1::<F>::zeros(n);
b[0] = F::one();
d[0] = F::zero();
b[n - 1] = F::one();
d[n - 1] = F::zero();
for i in 1..n - 1 {
let h_i_minus_1 = x[i] - x[i - 1];
let h_i = x[i + 1] - x[i];
a[i] = h_i_minus_1;
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = y[i] - y[i - 1];
let dy_i = y[i + 1] - y[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in spline computation".to_string(),
));
}
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
for i in 1..n {
if b[i - 1].is_zero() {
return Err(InterpolateError::ComputationError(
"Zero diagonal element in Thomas algorithm forward sweep".to_string(),
));
}
let m = a[i] / b[i - 1];
b[i] -= m * c[i - 1];
d[i] = d[i] - m * d[i - 1];
}
if b[n - 1].is_zero() {
return Err(InterpolateError::ComputationError(
"Zero diagonal element in Thomas algorithm back substitution".to_string(),
));
}
sigma[n - 1] = d[n - 1] / b[n - 1];
for i in (0..n - 1).rev() {
if b[i].is_zero() {
return Err(InterpolateError::ComputationError(
"Zero diagonal element in Thomas algorithm back substitution".to_string(),
));
}
sigma[i] = (d[i] - c[i] * sigma[i + 1]) / b[i];
}
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in spline coefficient calculation".to_string(),
));
}
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}
#[allow(dead_code)]
pub fn compute_not_a_knot_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n);
let mut b = Array1::<F>::zeros(n);
let mut c = Array1::<F>::zeros(n);
let mut d = Array1::<F>::zeros(n);
let h0 = x[1] - x[0];
let h1 = x[2] - x[1];
if h0.is_zero() || h1.is_zero() || (h0 + h1).is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in not-a-knot spline boundary conditions".to_string(),
));
}
b[0] = h1;
c[0] = h0 + h1;
d[0] = ((h0 + h1) * h1 * (y[1] - y[0]) / h0 + h0 * h0 * (y[2] - y[1]) / h1) / (h0 + h1);
let hn_2 = x[n - 2] - x[n - 3];
let hn_1 = x[n - 1] - x[n - 2];
if hn_1.is_zero() || hn_2.is_zero() || (hn_1 + hn_2).is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in not-a-knot spline boundary conditions".to_string(),
));
}
a[n - 1] = hn_1 + hn_2;
b[n - 1] = hn_2;
d[n - 1] = ((hn_1 + hn_2) * hn_2 * (y[n - 1] - y[n - 2]) / hn_1
+ hn_1 * hn_1 * (y[n - 2] - y[n - 3]) / hn_2)
/ (hn_1 + hn_2);
for i in 1..n - 1 {
let h_i_minus_1 = x[i] - x[i - 1];
let h_i = x[i + 1] - x[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in not-a-knot spline computation".to_string(),
));
}
a[i] = h_i_minus_1;
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = y[i] - y[i - 1];
let dy_i = y[i + 1] - y[i];
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
let mut c_prime = Array1::<F>::zeros(n);
if b[0].is_zero() {
return Err(InterpolateError::ComputationError(
"Zero diagonal element in not-a-knot Thomas algorithm".to_string(),
));
}
c_prime[0] = c[0] / b[0];
for i in 1..n {
let m = b[i] - a[i] * c_prime[i - 1];
if m.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero diagonal element in not-a-knot Thomas algorithm".to_string(),
));
}
if i < n - 1 {
c_prime[i] = c[i] / m;
}
d[i] = (d[i] - a[i] * d[i - 1]) / m;
}
sigma[n - 1] = d[n - 1];
for i in (0..n - 1).rev() {
sigma[i] = d[i] - c_prime[i] * sigma[i + 1];
}
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in not-a-knot spline coefficient calculation".to_string(),
));
}
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}
#[allow(dead_code)]
pub fn compute_clamped_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
left_deriv: F,
right_deriv: F,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n);
let mut b = Array1::<F>::zeros(n);
let mut c = Array1::<F>::zeros(n);
let mut d = Array1::<F>::zeros(n);
let h0 = x[1] - x[0];
if h0.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in clamped spline boundary conditions".to_string(),
));
}
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
b[0] = two * h0;
c[0] = h0;
d[0] = six * ((y[1] - y[0]) / h0 - left_deriv);
let hn_1 = x[n - 1] - x[n - 2];
if hn_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in clamped spline boundary conditions".to_string(),
));
}
a[n - 1] = hn_1;
b[n - 1] = two * hn_1;
d[n - 1] = six * (right_deriv - (y[n - 1] - y[n - 2]) / hn_1);
for i in 1..n - 1 {
let h_i_minus_1 = x[i] - x[i - 1];
let h_i = x[i + 1] - x[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in clamped spline computation".to_string(),
));
}
a[i] = h_i_minus_1;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = y[i] - y[i - 1];
let dy_i = y[i + 1] - y[i];
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
for i in 1..n {
let m = a[i] / b[i - 1];
b[i] -= m * c[i - 1];
d[i] = d[i] - m * d[i - 1];
}
sigma[n - 1] = d[n - 1] / b[n - 1];
for i in (0..n - 1).rev() {
sigma[i] = (d[i] - c[i] * sigma[i + 1]) / b[i];
}
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in clamped spline coefficient calculation".to_string(),
));
}
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}
#[allow(dead_code)]
pub fn compute_periodic_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n - 1);
let mut b = Array1::<F>::zeros(n - 1);
let mut c = Array1::<F>::zeros(n - 1);
let mut d = Array1::<F>::zeros(n - 1);
for i in 0..n - 1 {
let h_i_minus_1 = if i == 0 {
x[n - 1] - x[n - 2]
} else {
x[i] - x[i - 1]
};
let h_i = x[i + 1] - x[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in periodic spline computation".to_string(),
));
}
a[i] = h_i_minus_1;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = if i == 0 {
y[0] - y[n - 2] } else {
y[i] - y[i - 1]
};
let dy_i = y[i + 1] - y[i];
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
let mut b_mod = b.clone();
let mut d_mod = d.clone();
for i in 1..n - 1 {
let m = a[i] / b_mod[i - 1];
b_mod[i] -= m * c[i - 1];
d_mod[i] = d_mod[i] - m * d_mod[i - 1];
}
sigma[n - 2] = d_mod[n - 2] / b_mod[n - 2];
for i in (0..n - 2).rev() {
sigma[i] = (d_mod[i] - c[i] * sigma[i + 1]) / b_mod[i];
}
sigma[n - 1] = sigma[0];
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in periodic spline coefficient calculation".to_string(),
));
}
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}
#[allow(dead_code)]
pub fn compute_second_derivative_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
left_d2: F,
right_d2: F,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n);
let mut b = Array1::<F>::zeros(n);
let mut c = Array1::<F>::zeros(n);
let mut d = Array1::<F>::zeros(n);
b[0] = F::one();
d[0] = left_d2;
b[n - 1] = F::one();
d[n - 1] = right_d2;
for i in 1..n - 1 {
let h_i_minus_1 = x[i] - x[i - 1];
let h_i = x[i + 1] - x[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in second derivative spline computation".to_string(),
));
}
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
a[i] = h_i_minus_1;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = y[i] - y[i - 1];
let dy_i = y[i + 1] - y[i];
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
for i in 1..n {
let m = a[i] / b[i - 1];
b[i] -= m * c[i - 1];
d[i] = d[i] - m * d[i - 1];
}
sigma[n - 1] = d[n - 1] / b[n - 1];
for i in (0..n - 1).rev() {
sigma[i] = (d[i] - c[i] * sigma[i + 1]) / b[i];
}
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in second derivative spline coefficient calculation"
.to_string(),
));
}
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}
#[allow(dead_code)]
pub fn compute_parabolic_runout_cubic_spline<F: InterpolationFloat>(
x: &ArrayView1<F>,
y: &ArrayView1<F>,
) -> InterpolateResult<Array2<F>> {
let n = x.len();
let n_segments = n - 1;
let mut coeffs = Array2::<F>::zeros((n_segments, 4));
let mut a = Array1::<F>::zeros(n);
let mut b = Array1::<F>::zeros(n);
let mut c = Array1::<F>::zeros(n);
let mut d = Array1::<F>::zeros(n);
let two = F::from_f64(2.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 2.0 to float type".to_string(),
)
})?;
let six = F::from_f64(6.0).ok_or_else(|| {
InterpolateError::ComputationError(
"Failed to convert constant 6.0 to float type".to_string(),
)
})?;
b[0] = two;
c[0] = F::one();
d[0] = F::zero();
a[n - 1] = F::one();
b[n - 1] = two;
d[n - 1] = F::zero();
for i in 1..n - 1 {
let h_i_minus_1 = x[i] - x[i - 1];
let h_i = x[i + 1] - x[i];
if h_i.is_zero() || h_i_minus_1.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in parabolic runout spline computation".to_string(),
));
}
a[i] = h_i_minus_1;
b[i] = two * (h_i_minus_1 + h_i);
c[i] = h_i;
let dy_i_minus_1 = y[i] - y[i - 1];
let dy_i = y[i + 1] - y[i];
d[i] = six * (dy_i / h_i - dy_i_minus_1 / h_i_minus_1);
}
let mut sigma = Array1::<F>::zeros(n);
for i in 1..n {
let m = a[i] / b[i - 1];
b[i] -= m * c[i - 1];
d[i] = d[i] - m * d[i - 1];
}
sigma[n - 1] = d[n - 1] / b[n - 1];
for i in (0..n - 1).rev() {
sigma[i] = (d[i] - c[i] * sigma[i + 1]) / b[i];
}
for i in 0..n_segments {
let h_i = x[i + 1] - x[i];
if h_i.is_zero() {
return Err(InterpolateError::ComputationError(
"Zero interval length in parabolic runout spline coefficient calculation"
.to_string(),
));
}
coeffs[[i, 0]] = y[i];
coeffs[[i, 1]] = (y[i + 1] - y[i]) / h_i - h_i * (two * sigma[i] + sigma[i + 1]) / six;
coeffs[[i, 2]] = sigma[i] / two;
coeffs[[i, 3]] = (sigma[i + 1] - sigma[i]) / (six * h_i);
}
Ok(coeffs)
}