use alloc::vec::Vec;
use core::convert::TryFrom;
use slatec_sys::interpolation as raw;
use slatec_sys::{FortranInteger, FortranLogical};
use crate::runtime::{lock_native, permit_recoverable_native_statuses};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PchipError {
TooFewPoints,
LengthMismatch,
NonFiniteInput,
KnotsNotStrictlyIncreasing,
InvalidEndpointCondition,
InvalidSwitchPolicy,
OutOfDomain,
DimensionOverflow,
AllocationFailed,
NativeFailure {
routine: &'static str,
code: FortranInteger,
},
NativeContractViolation {
detail: &'static str,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Extrapolation {
Reject,
Allow,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EndpointCondition<T> {
NotAKnot,
FirstDerivative(T),
SecondDerivative(T),
ThreePoint,
FourPoint,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MonotoneEndpointCondition<T> {
Default,
FirstDerivative {
value: T,
adjust_for_monotonicity: bool,
},
SecondDerivative {
value: T,
adjust_for_monotonicity: bool,
},
ThreePoint {
adjust_for_monotonicity: bool,
},
FourPoint {
adjust_for_monotonicity: bool,
},
SecondDerivativeContinuous {
adjust_for_monotonicity: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SwitchPointPolicy<T> {
ForceExtrema,
LimitDeviation(T),
Unrestricted,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ConstructionReport {
None,
MonotonicitySwitches(usize),
EndpointAdjusted {
beginning: bool,
end: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct HermiteEvaluation<T> {
pub value: T,
pub first_derivative: T,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EvaluationReport {
pub extrapolated_points: usize,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IntegrationReport<T> {
pub value: T,
pub lower_extrapolated: bool,
pub upper_extrapolated: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PiecewiseCubicHermite<T> {
knots: Vec<T>,
values: Vec<T>,
derivatives: Vec<T>,
construction: ConstructionReport,
}
impl<T> PiecewiseCubicHermite<T> {
#[must_use]
pub fn knots(&self) -> &[T] {
&self.knots
}
#[must_use]
pub fn values(&self) -> &[T] {
&self.values
}
#[must_use]
pub fn derivatives(&self) -> &[T] {
&self.derivatives
}
#[must_use]
pub fn construction_report(&self) -> &ConstructionReport {
&self.construction
}
#[must_use]
pub fn len(&self) -> usize {
self.knots.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
false
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for f32 {}
impl Sealed for f64 {}
}
#[doc(hidden)]
pub trait PchipScalar: sealed::Sealed + Copy + Default + PartialOrd + core::fmt::Debug {
fn finite(self) -> bool;
fn zero() -> Self;
fn negative_one() -> Self;
unsafe fn monotone(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
ierr: &mut FortranInteger,
);
unsafe fn controlled(
ic: *const FortranInteger,
vc: *const Self,
switch: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
);
unsafe fn spline(
ic: *const FortranInteger,
vc: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
);
unsafe fn evaluate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
ierr: &mut FortranInteger,
);
unsafe fn evaluate_derivative(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
derivatives: *mut Self,
ierr: &mut FortranInteger,
);
unsafe fn integrate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
lower: &Self,
upper: &Self,
ierr: &mut FortranInteger,
) -> Self;
}
impl PchipScalar for f32 {
fn finite(self) -> bool {
self.is_finite()
}
fn zero() -> Self {
0.0
}
fn negative_one() -> Self {
-1.0
}
unsafe fn monotone(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe { raw::pchim(n, x, f, d, incfd, ierr) }
}
unsafe fn controlled(
ic: *const FortranInteger,
vc: *const Self,
switch: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe {
raw::pchic(
ic,
vc,
switch,
n,
x,
f,
d,
incfd,
workspace,
workspace_len,
ierr,
)
}
}
unsafe fn spline(
ic: *const FortranInteger,
vc: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe { raw::pchsp(ic, vc, n, x, f, d, incfd, workspace, workspace_len, ierr) }
}
unsafe fn evaluate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
ierr: &mut FortranInteger,
) {
unsafe { raw::pchfe(n, x, f, d, incfd, skip, count, points, values, ierr) }
}
unsafe fn evaluate_derivative(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
derivatives: *mut Self,
ierr: &mut FortranInteger,
) {
unsafe {
raw::pchfd(
n,
x,
f,
d,
incfd,
skip,
count,
points,
values,
derivatives,
ierr,
)
}
}
unsafe fn integrate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
lower: &Self,
upper: &Self,
ierr: &mut FortranInteger,
) -> Self {
unsafe { raw::pchia(n, x, f, d, incfd, skip, lower, upper, ierr) }
}
}
impl PchipScalar for f64 {
fn finite(self) -> bool {
self.is_finite()
}
fn zero() -> Self {
0.0
}
fn negative_one() -> Self {
-1.0
}
unsafe fn monotone(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe { raw::dpchim(n, x, f, d, incfd, ierr) }
}
unsafe fn controlled(
ic: *const FortranInteger,
vc: *const Self,
switch: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe {
raw::dpchic(
ic,
vc,
switch,
n,
x,
f,
d,
incfd,
workspace,
workspace_len,
ierr,
)
}
}
unsafe fn spline(
ic: *const FortranInteger,
vc: *const Self,
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *mut Self,
incfd: &mut FortranInteger,
workspace: *mut Self,
workspace_len: &mut FortranInteger,
ierr: &mut FortranInteger,
) {
unsafe { raw::dpchsp(ic, vc, n, x, f, d, incfd, workspace, workspace_len, ierr) }
}
unsafe fn evaluate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
ierr: &mut FortranInteger,
) {
unsafe { raw::dpchfe(n, x, f, d, incfd, skip, count, points, values, ierr) }
}
unsafe fn evaluate_derivative(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
count: &mut FortranInteger,
points: *const Self,
values: *mut Self,
derivatives: *mut Self,
ierr: &mut FortranInteger,
) {
unsafe {
raw::dpchfd(
n,
x,
f,
d,
incfd,
skip,
count,
points,
values,
derivatives,
ierr,
)
}
}
unsafe fn integrate(
n: &mut FortranInteger,
x: *const Self,
f: *const Self,
d: *const Self,
incfd: &mut FortranInteger,
skip: &mut FortranLogical,
lower: &Self,
upper: &Self,
ierr: &mut FortranInteger,
) -> Self {
unsafe { raw::dpchia(n, x, f, d, incfd, skip, lower, upper, ierr) }
}
}
impl<T: PchipScalar> PiecewiseCubicHermite<T> {
pub fn monotone(knots: &[T], values: &[T]) -> Result<Self, PchipError> {
validate_knots_values(knots, values)?;
let mut derivatives = zeroed_like(values)?;
let mut n = native_len(knots.len())?;
let mut incfd = 1;
let mut ierr = 0;
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
unsafe {
T::monotone(
&mut n,
knots.as_ptr(),
values.as_ptr(),
derivatives.as_mut_ptr(),
&mut incfd,
&mut ierr,
)
};
if ierr < 0 {
return Err(native_failure("PCHIM/DPCHIM", ierr));
}
Ok(Self {
knots: copy_slice(knots)?,
values: copy_slice(values)?,
derivatives,
construction: if ierr == 0 {
ConstructionReport::None
} else {
ConstructionReport::MonotonicitySwitches(ierr as usize)
},
})
}
pub fn from_derivatives(
knots: Vec<T>,
values: Vec<T>,
derivatives: Vec<T>,
) -> Result<Self, PchipError> {
validate_full(&knots, &values, &derivatives)?;
Ok(Self {
knots,
values,
derivatives,
construction: ConstructionReport::None,
})
}
pub fn monotone_with_conditions(
knots: &[T],
values: &[T],
beginning: MonotoneEndpointCondition<T>,
end: MonotoneEndpointCondition<T>,
switch_policy: SwitchPointPolicy<T>,
) -> Result<Self, PchipError> {
validate_knots_values(knots, values)?;
let (left_code, left_value) = monotone_endpoint(beginning)?;
let (right_code, right_value) = monotone_endpoint(end)?;
let switch = switch_value(switch_policy)?;
let mut derivatives = zeroed_like(values)?;
let mut workspace = zeroed::<T>(
knots
.len()
.checked_sub(1)
.and_then(|v| v.checked_mul(2))
.ok_or(PchipError::DimensionOverflow)?,
)?;
let mut n = native_len(knots.len())?;
let mut workspace_len = native_len(workspace.len())?;
let mut incfd = 1;
let mut ierr = 0;
let ic = [left_code, right_code];
let vc = [left_value, right_value];
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
unsafe {
T::controlled(
ic.as_ptr(),
vc.as_ptr(),
&switch,
&mut n,
knots.as_ptr(),
values.as_ptr(),
derivatives.as_mut_ptr(),
&mut incfd,
workspace.as_mut_ptr(),
&mut workspace_len,
&mut ierr,
)
};
if ierr < 0 {
return Err(native_failure("PCHIC/DPCHIC", ierr));
}
Ok(Self {
knots: copy_slice(knots)?,
values: copy_slice(values)?,
derivatives,
construction: endpoint_report(ierr),
})
}
pub fn spline(
knots: &[T],
values: &[T],
beginning: EndpointCondition<T>,
end: EndpointCondition<T>,
) -> Result<Self, PchipError> {
validate_knots_values(knots, values)?;
let (left_code, left_value) = spline_endpoint(beginning)?;
let (right_code, right_value) = spline_endpoint(end)?;
let mut derivatives = zeroed_like(values)?;
let mut workspace = zeroed::<T>(
knots
.len()
.checked_mul(2)
.ok_or(PchipError::DimensionOverflow)?,
)?;
let mut n = native_len(knots.len())?;
let mut workspace_len = native_len(workspace.len())?;
let mut incfd = 1;
let mut ierr = 0;
let ic = [left_code, right_code];
let vc = [left_value, right_value];
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
unsafe {
T::spline(
ic.as_ptr(),
vc.as_ptr(),
&mut n,
knots.as_ptr(),
values.as_ptr(),
derivatives.as_mut_ptr(),
&mut incfd,
workspace.as_mut_ptr(),
&mut workspace_len,
&mut ierr,
)
};
if ierr < 0 {
return Err(native_failure("PCHSP/DPCHSP", ierr));
}
Ok(Self {
knots: copy_slice(knots)?,
values: copy_slice(values)?,
derivatives,
construction: ConstructionReport::None,
})
}
pub fn evaluate(&self, point: T) -> Result<T, PchipError> {
let mut output = [point];
self.evaluate_with_policy_into(
core::slice::from_ref(&point),
&mut output,
Extrapolation::Reject,
)?;
Ok(output[0])
}
pub fn evaluate_into(
&self,
points: &[T],
output: &mut [T],
) -> Result<EvaluationReport, PchipError> {
self.evaluate_with_policy_into(points, output, Extrapolation::Reject)
}
pub fn evaluate_with_policy_into(
&self,
points: &[T],
output: &mut [T],
extrapolation: Extrapolation,
) -> Result<EvaluationReport, PchipError> {
if points.len() != output.len() {
return Err(PchipError::LengthMismatch);
}
let count = validate_points(self, points, extrapolation)?;
if points.is_empty() {
return Ok(EvaluationReport {
extrapolated_points: 0,
});
}
let mut n = native_len(self.len())?;
let mut native_count = native_len(points.len())?;
let mut incfd = 1;
let mut skip: FortranLogical = 1;
let mut ierr = 0;
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
unsafe {
T::evaluate(
&mut n,
self.knots.as_ptr(),
self.values.as_ptr(),
self.derivatives.as_ptr(),
&mut incfd,
&mut skip,
&mut native_count,
points.as_ptr(),
output.as_mut_ptr(),
&mut ierr,
)
};
evaluation_status(ierr, count)
}
pub fn evaluate_with_derivative(&self, point: T) -> Result<HermiteEvaluation<T>, PchipError> {
let mut values = [point];
let mut derivatives = [point];
self.evaluate_with_derivatives_into(
core::slice::from_ref(&point),
&mut values,
&mut derivatives,
)?;
Ok(HermiteEvaluation {
value: values[0],
first_derivative: derivatives[0],
})
}
pub fn evaluate_with_derivatives_into(
&self,
points: &[T],
values: &mut [T],
derivatives: &mut [T],
) -> Result<EvaluationReport, PchipError> {
self.evaluate_with_derivatives_with_policy_into(
points,
values,
derivatives,
Extrapolation::Reject,
)
}
pub fn evaluate_with_derivatives_with_policy_into(
&self,
points: &[T],
values: &mut [T],
derivatives: &mut [T],
extrapolation: Extrapolation,
) -> Result<EvaluationReport, PchipError> {
if points.len() != values.len() || points.len() != derivatives.len() {
return Err(PchipError::LengthMismatch);
}
let count = validate_points(self, points, extrapolation)?;
if points.is_empty() {
return Ok(EvaluationReport {
extrapolated_points: 0,
});
}
let mut n = native_len(self.len())?;
let mut native_count = native_len(points.len())?;
let mut incfd = 1;
let mut skip: FortranLogical = 1;
let mut ierr = 0;
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
unsafe {
T::evaluate_derivative(
&mut n,
self.knots.as_ptr(),
self.values.as_ptr(),
self.derivatives.as_ptr(),
&mut incfd,
&mut skip,
&mut native_count,
points.as_ptr(),
values.as_mut_ptr(),
derivatives.as_mut_ptr(),
&mut ierr,
)
};
evaluation_status(ierr, count)
}
pub fn integrate(&self, lower: T, upper: T) -> Result<T, PchipError> {
Ok(self
.integrate_with_policy(lower, upper, Extrapolation::Reject)?
.value)
}
pub fn integrate_with_policy(
&self,
lower: T,
upper: T,
extrapolation: Extrapolation,
) -> Result<IntegrationReport<T>, PchipError> {
if !lower.finite() || !upper.finite() {
return Err(PchipError::NonFiniteInput);
}
let lower_outside = lower < self.knots[0] || lower > self.knots[self.len() - 1];
let upper_outside = upper < self.knots[0] || upper > self.knots[self.len() - 1];
if extrapolation == Extrapolation::Reject && (lower_outside || upper_outside) {
return Err(PchipError::OutOfDomain);
}
let mut n = native_len(self.len())?;
let mut incfd = 1;
let mut skip: FortranLogical = 1;
let mut ierr = 0;
let _native = lock_native();
let _xerror = permit_recoverable_native_statuses();
let value = unsafe {
T::integrate(
&mut n,
self.knots.as_ptr(),
self.values.as_ptr(),
self.derivatives.as_ptr(),
&mut incfd,
&mut skip,
&lower,
&upper,
&mut ierr,
)
};
if ierr < 0 {
return Err(native_failure("PCHIA/DPCHIA", ierr));
}
if ierr > 3 {
return Err(PchipError::NativeContractViolation {
detail: "PCHIA returned an undocumented warning status",
});
}
Ok(IntegrationReport {
value,
lower_extrapolated: ierr == 1 || ierr == 3,
upper_extrapolated: ierr == 2 || ierr == 3,
})
}
}
fn copy_slice<T: Copy>(values: &[T]) -> Result<Vec<T>, PchipError> {
let mut copied = Vec::new();
copied
.try_reserve_exact(values.len())
.map_err(|_| PchipError::AllocationFailed)?;
copied.extend_from_slice(values);
Ok(copied)
}
fn zeroed<T: Copy + Default>(length: usize) -> Result<Vec<T>, PchipError> {
let mut values = Vec::new();
values
.try_reserve_exact(length)
.map_err(|_| PchipError::AllocationFailed)?;
values.resize(length, T::default());
Ok(values)
}
fn zeroed_like<T: Copy + Default>(values: &[T]) -> Result<Vec<T>, PchipError> {
zeroed(values.len())
}
fn native_len(length: usize) -> Result<FortranInteger, PchipError> {
FortranInteger::try_from(length).map_err(|_| PchipError::DimensionOverflow)
}
fn validate_knots_values<T: PchipScalar>(knots: &[T], values: &[T]) -> Result<(), PchipError> {
if knots.len() < 2 {
return Err(PchipError::TooFewPoints);
}
if knots.len() != values.len() {
return Err(PchipError::LengthMismatch);
}
native_len(knots.len())?;
for (&knot, &value) in knots.iter().zip(values) {
if !knot.finite() || !value.finite() {
return Err(PchipError::NonFiniteInput);
}
}
if knots.windows(2).any(|pair| !(pair[1] > pair[0])) {
return Err(PchipError::KnotsNotStrictlyIncreasing);
}
Ok(())
}
fn validate_full<T: PchipScalar>(
knots: &[T],
values: &[T],
derivatives: &[T],
) -> Result<(), PchipError> {
validate_knots_values(knots, values)?;
if derivatives.len() != knots.len() {
return Err(PchipError::LengthMismatch);
}
if derivatives.iter().any(|value| !value.finite()) {
return Err(PchipError::NonFiniteInput);
}
Ok(())
}
fn validate_points<T: PchipScalar>(
curve: &PiecewiseCubicHermite<T>,
points: &[T],
extrapolation: Extrapolation,
) -> Result<usize, PchipError> {
native_len(points.len())?;
let first = curve.knots[0];
let last = curve.knots[curve.len() - 1];
let mut count = 0usize;
for &point in points {
if !point.finite() {
return Err(PchipError::NonFiniteInput);
}
if point < first || point > last {
count = count.checked_add(1).ok_or(PchipError::DimensionOverflow)?;
}
}
if extrapolation == Extrapolation::Reject && count > 0 {
return Err(PchipError::OutOfDomain);
}
Ok(count)
}
fn spline_endpoint<T: PchipScalar>(
condition: EndpointCondition<T>,
) -> Result<(FortranInteger, T), PchipError> {
match condition {
EndpointCondition::NotAKnot => Ok((0, T::zero())),
EndpointCondition::FirstDerivative(value) => finite_endpoint(1, value),
EndpointCondition::SecondDerivative(value) => finite_endpoint(2, value),
EndpointCondition::ThreePoint => Ok((3, T::zero())),
EndpointCondition::FourPoint => Ok((4, T::zero())),
}
}
fn monotone_endpoint<T: PchipScalar>(
condition: MonotoneEndpointCondition<T>,
) -> Result<(FortranInteger, T), PchipError> {
let zero = T::zero();
match condition {
MonotoneEndpointCondition::Default => Ok((0, zero)),
MonotoneEndpointCondition::FirstDerivative {
value,
adjust_for_monotonicity,
} => finite_endpoint(if adjust_for_monotonicity { -1 } else { 1 }, value),
MonotoneEndpointCondition::SecondDerivative {
value,
adjust_for_monotonicity,
} => finite_endpoint(if adjust_for_monotonicity { -2 } else { 2 }, value),
MonotoneEndpointCondition::ThreePoint {
adjust_for_monotonicity,
} => Ok((if adjust_for_monotonicity { -3 } else { 3 }, zero)),
MonotoneEndpointCondition::FourPoint {
adjust_for_monotonicity,
} => Ok((if adjust_for_monotonicity { -4 } else { 4 }, zero)),
MonotoneEndpointCondition::SecondDerivativeContinuous {
adjust_for_monotonicity,
} => Ok((if adjust_for_monotonicity { -5 } else { 5 }, zero)),
}
}
fn finite_endpoint<T: PchipScalar>(
code: FortranInteger,
value: T,
) -> Result<(FortranInteger, T), PchipError> {
if value.finite() {
Ok((code, value))
} else {
Err(PchipError::InvalidEndpointCondition)
}
}
fn switch_value<T: PchipScalar>(policy: SwitchPointPolicy<T>) -> Result<T, PchipError> {
match policy {
SwitchPointPolicy::ForceExtrema => Ok(T::zero()),
SwitchPointPolicy::LimitDeviation(value) => {
if value.finite() && value > T::zero() {
Ok(value)
} else {
Err(PchipError::InvalidSwitchPolicy)
}
}
SwitchPointPolicy::Unrestricted => Ok(T::negative_one()),
}
}
fn endpoint_report(status: FortranInteger) -> ConstructionReport {
match status {
0 => ConstructionReport::None,
1 => ConstructionReport::EndpointAdjusted {
beginning: true,
end: false,
},
2 => ConstructionReport::EndpointAdjusted {
beginning: false,
end: true,
},
3 => ConstructionReport::EndpointAdjusted {
beginning: true,
end: true,
},
_ => ConstructionReport::None,
}
}
fn evaluation_status(
status: FortranInteger,
expected_extrapolations: usize,
) -> Result<EvaluationReport, PchipError> {
if status < 0 {
return Err(native_failure("PCHFE/PCHFD", status));
}
let returned = usize::try_from(status).map_err(|_| PchipError::NativeContractViolation {
detail: "negative PCHIP extrapolation count",
})?;
if returned < expected_extrapolations {
return Err(PchipError::NativeContractViolation {
detail: "PCHIP missed a preflighted extrapolation point",
});
}
Ok(EvaluationReport {
extrapolated_points: returned,
})
}
fn native_failure(routine: &'static str, code: FortranInteger) -> PchipError {
PchipError::NativeFailure { routine, code }
}