#[cfg(not(feature = "std"))]
use alloc::format;
use num_traits::Float;
use crate::primitives::errors::LowessError;
pub struct Validator;
impl Validator {
pub fn validate_inputs<T: Float>(x: &[T], y: &[T]) -> Result<(), LowessError> {
if x.is_empty() || y.is_empty() {
return Err(LowessError::EmptyInput);
}
let n = x.len();
if n != y.len() {
return Err(LowessError::MismatchedInputs {
x_len: n,
y_len: y.len(),
});
}
if n < 2 {
return Err(LowessError::TooFewPoints { got: n, min: 2 });
}
for i in 0..n {
if !x[i].is_finite() {
return Err(LowessError::InvalidNumericValue(format!(
"x[{}]={}",
i,
x[i].to_f64().unwrap_or(f64::NAN)
)));
}
if !y[i].is_finite() {
return Err(LowessError::InvalidNumericValue(format!(
"y[{}]={}",
i,
y[i].to_f64().unwrap_or(f64::NAN)
)));
}
}
Ok(())
}
pub fn validate_scalar<T: Float>(val: T, name: &str) -> Result<(), LowessError> {
if !val.is_finite() {
return Err(LowessError::InvalidNumericValue(format!(
"{}={}",
name,
val.to_f64().unwrap_or(f64::NAN)
)));
}
Ok(())
}
pub fn validate_fraction<T: Float>(fraction: T) -> Result<(), LowessError> {
if !fraction.is_finite() || fraction <= T::zero() || fraction > T::one() {
return Err(LowessError::InvalidFraction(
fraction.to_f64().unwrap_or(f64::NAN),
));
}
Ok(())
}
pub fn validate_iterations(iterations: usize) -> Result<(), LowessError> {
const MAX_ITERATIONS: usize = 1000;
if iterations > MAX_ITERATIONS {
return Err(LowessError::InvalidIterations(iterations));
}
Ok(())
}
pub fn validate_interval_level<T: Float>(level: T) -> Result<(), LowessError> {
if !level.is_finite() || level <= T::zero() || level >= T::one() {
return Err(LowessError::InvalidIntervals(
level.to_f64().unwrap_or(f64::NAN),
));
}
Ok(())
}
pub fn validate_cv_fractions<T: Float>(fracs: &[T]) -> Result<(), LowessError> {
if fracs.is_empty() {
return Err(LowessError::InvalidFraction(0.0));
}
for &f in fracs {
Self::validate_fraction(f)?;
}
Ok(())
}
pub fn validate_kfold(k: usize) -> Result<(), LowessError> {
if k < 2 {
return Err(LowessError::InvalidNumericValue(format!(
"k-fold must be at least 2, got {}",
k
)));
}
Ok(())
}
pub fn validate_tolerance<T: Float>(tol: T) -> Result<(), LowessError> {
if !tol.is_finite() || tol <= T::zero() {
return Err(LowessError::InvalidTolerance(
tol.to_f64().unwrap_or(f64::NAN),
));
}
Ok(())
}
pub fn validate_delta<T: Float>(delta: T) -> Result<(), LowessError> {
if !delta.is_finite() || delta < T::zero() {
return Err(LowessError::InvalidDelta(
delta.to_f64().unwrap_or(f64::NAN),
));
}
Ok(())
}
pub fn validate_chunk_size(chunk_size: usize, min: usize) -> Result<(), LowessError> {
if chunk_size < min {
return Err(LowessError::InvalidChunkSize {
got: chunk_size,
min,
});
}
Ok(())
}
pub fn validate_overlap(overlap: usize, chunk_size: usize) -> Result<(), LowessError> {
if overlap >= chunk_size {
return Err(LowessError::InvalidOverlap {
overlap,
chunk_size,
});
}
Ok(())
}
pub fn validate_window_capacity(window_capacity: usize, min: usize) -> Result<(), LowessError> {
if window_capacity < min {
return Err(LowessError::InvalidWindowCapacity {
got: window_capacity,
min,
});
}
Ok(())
}
pub fn validate_min_points(
min_points: usize,
window_capacity: usize,
) -> Result<(), LowessError> {
if min_points < 2 || min_points > window_capacity {
return Err(LowessError::InvalidMinPoints {
got: min_points,
window_capacity,
});
}
Ok(())
}
pub fn validate_no_duplicates(
duplicate_param: Option<&'static str>,
) -> Result<(), LowessError> {
if let Some(param) = duplicate_param {
return Err(LowessError::DuplicateParameter { parameter: param });
}
Ok(())
}
}