use crate::bindings::*;
use crate::*;
use drop_guard::guard;
pub fn interpolate(
algorithm: Algorithm,
derivative: Derivative,
mut x: Vec<f64>,
mut y: Vec<f64>,
x_eval: &[f64],
) -> Result<Vec<f64>> {
if x.len() != y.len() {
return Err(GSLError::Invalid);
}
sorting::sort_xy(&mut x, &mut y);
let (x, y) = sorting::dedup_x_mean(&x, &y)?;
interpolate_monotonic(algorithm, derivative, &x, &y, x_eval)
}
pub fn interpolate_monotonic(
algorithm: Algorithm,
derivative: Derivative,
x: &[f64],
y: &[f64],
x_eval: &[f64],
) -> Result<Vec<f64>> {
unsafe {
if x.len() != y.len() {
return Err(GSLError::Invalid);
}
let n = x.len();
let algorithm = match algorithm {
Algorithm::Linear => gsl_interp_linear,
Algorithm::Steffen => gsl_interp_steffen,
};
if n < gsl_interp_type_min_size(algorithm) as usize {
return Err(GSLError::Invalid);
}
let workspace = guard(gsl_interp_alloc(algorithm, n as u64), |workspace| {
gsl_interp_free(workspace);
});
assert!(!workspace.is_null());
let accel = guard(gsl_interp_accel_alloc(), |accel| {
gsl_interp_accel_free(accel);
});
assert!(!accel.is_null());
GSLError::from_raw(gsl_interp_init(
*workspace,
x.as_ptr(),
y.as_ptr(),
n as u64,
))?;
x_eval
.iter()
.map(|&x_eval| {
let mut y_eval = 0.0;
GSLError::from_raw(match derivative {
Derivative::None => gsl_interp_eval_e(
*workspace,
x.as_ptr(),
y.as_ptr(),
x_eval,
*accel,
&mut y_eval,
),
Derivative::First => gsl_interp_eval_deriv_e(
*workspace,
x.as_ptr(),
y.as_ptr(),
x_eval,
*accel,
&mut y_eval,
),
Derivative::Second => gsl_interp_eval_deriv2_e(
*workspace,
x.as_ptr(),
y.as_ptr(),
x_eval,
*accel,
&mut y_eval,
),
})
.map(|_| y_eval)
})
.collect()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Algorithm {
Linear,
Steffen,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Derivative {
None,
First,
Second,
}
#[test]
fn test_linear_fit() {
disable_error_handler();
let x = [0.0, 1.0, 2.0, 3.0, 4.0];
let y = [0.0, 2.0, 4.0, 6.0, 8.0];
let x_eval = [0.5, 1.5, 2.5, 3.5];
let expected = [1.0, 3.0, 5.0, 7.0];
for (y_eval, y_expected) in
interpolate_monotonic(Algorithm::Linear, Derivative::None, &x, &y, &x_eval)
.unwrap()
.iter()
.zip(expected.iter())
{
approx::assert_abs_diff_eq!(y_eval, y_expected);
}
}
#[test]
fn test_invalid_params() {
disable_error_handler();
interpolate_monotonic(Algorithm::Linear, Derivative::None, &[], &[], &[0.0]).unwrap_err();
interpolate_monotonic(
Algorithm::Linear,
Derivative::None,
&[0.0, 1.0, 2.0],
&[0.0; 3],
&[100.0],
)
.unwrap_err();
}