loess-rs 0.9.0

LOESS (Locally Estimated Scatterplot Smoothing)
Documentation
//! Error types for LOESS operations.
//!
//! This module defines error conditions that can occur during LOESS smoothing,
//! including input validation, parameter constraints, and adapter limitations.

// Feature-gated imports
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::string::String;
#[cfg(feature = "std")]
use std::vec::Vec;

// External dependencies
use core::fmt::{Display, Formatter, Result};

// Error type for LOESS operations.
#[derive(Debug, Clone, PartialEq)]
pub enum LoessError {
    // Input arrays are empty; LOESS requires at least 2 points.
    EmptyInput,

    // Generic invalid input error with a descriptive message.
    InvalidInput(String),

    // `x` and `y` arrays must have the same number of elements.
    MismatchedInputs {
        // Number of elements in the `x` array.
        x_len: usize,
        // Number of elements in the `y` array.
        y_len: usize,
    },

    // Input data contains NaN or infinite values.
    InvalidNumericValue(String),

    // Number of points is below the minimum requirement for the selected parameters.
    TooFewPoints {
        // Number of points provided.
        got: usize,
        // Minimum required points.
        min: usize,
    },

    // Smoothing fraction must be in the range (0, 1].
    InvalidFraction(f64),

    // Robustness iterations (0 means initial fit only).
    InvalidIterations(usize),

    // Interval coverage level must be strictly between 0 and 1.
    InvalidIntervals(f64),

    // Convergence tolerance must be positive and finite.
    InvalidTolerance(f64),

    // Chunk size must be large enough to accommodate the minimum window.
    InvalidChunkSize {
        // The chunk size provided.
        got: usize,
        // Minimum required chunk size.
        min: usize,
    },

    // Overlap must be strictly less than the chunk size to ensure progress.
    InvalidOverlap {
        // The overlap provided.
        overlap: usize,
        // The chunk size.
        chunk_size: usize,
    },

    // Window capacity must be large enough for the requested smoothing parameters.
    InvalidWindowCapacity {
        // The window capacity provided.
        got: usize,
        // Minimum required window capacity.
        min: usize,
    },

    // Minimum points must be at least 2 and at most the window capacity.
    InvalidMinPoints {
        // The min_points provided.
        got: usize,
        // The window capacity.
        window_capacity: usize,
    },

    // Selected adapter does not support the requested feature (e.g., cross-validation).
    UnsupportedFeature {
        // Name of the adapter (e.g., "Streaming", "Online").
        adapter: &'static str,
        // Name of the unsupported feature.
        feature: &'static str,
    },

    // Parameter was set multiple times in the builder.
    DuplicateParameter {
        // Name of the parameter that was set multiple times.
        parameter: &'static str,
    },

    // Runtime execution error.
    RuntimeError(String),

    // Cell size must be in the range (0, 1].
    InvalidCell(f64),

    // Interpolation cell size requires more vertices than allowed limit.
    InsufficientVertices {
        // Estimated number of vertices required.
        required: usize,
        // Maximum number of vertices allowed.
        limit: usize,
        // The cell size that caused the overflow.
        cell: f64,
        // Whether the cell size was explicitly provided by the user.
        cell_provided: bool,
        // Whether the limit was explicitly provided by the user.
        limit_provided: bool,
    },

    // An invalid string value was passed for a configuration option.
    InvalidOption {
        // The name of the configuration option.
        option: &'static str,
        // The invalid value that was provided.
        value: String,
        // Comma-separated list of valid values for the option.
        valid: &'static str,
    },

    // Multiple invalid string option values were passed to the builder.
    //
    // Collects all parse errors from string builder methods and reports them together at `build()`.
    ParseErrors(Vec<LoessError>),
}

impl Display for LoessError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        match self {
            Self::EmptyInput => write!(f, "Input arrays are empty"),
            Self::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
            Self::MismatchedInputs { x_len, y_len } => {
                write!(f, "Length mismatch: x has {x_len} points, y has {y_len}")
            }
            Self::InvalidNumericValue(s) => write!(f, "Invalid numeric value: {s}"),
            Self::TooFewPoints { got, min } => {
                write!(f, "Too few points: got {got}, need at least {min}")
            }
            Self::InvalidFraction(frac) => {
                write!(f, "Invalid fraction: {frac} (must be > 0 and <= 1)")
            }
            Self::InvalidIterations(iter) => {
                write!(f, "Invalid iterations: {iter} (must be in [0, 1000])")
            }
            Self::InvalidIntervals(level) => {
                write!(f, "Invalid interval level: {level} (must be > 0 and < 1)")
            }
            Self::InvalidTolerance(tol) => {
                write!(f, "Invalid tolerance: {tol} (must be > 0 and finite)")
            }
            Self::InvalidChunkSize { got, min } => {
                write!(f, "Invalid chunk_size: {got} (must be at least {min})")
            }
            Self::InvalidOverlap {
                overlap,
                chunk_size,
            } => {
                write!(
                    f,
                    "Invalid overlap: {overlap} (must be less than chunk_size {chunk_size})"
                )
            }
            Self::InvalidWindowCapacity { got, min } => {
                write!(f, "Invalid window_capacity: {got} (must be at least {min})")
            }
            Self::InvalidMinPoints {
                got,
                window_capacity,
            } => {
                write!(
                    f,
                    "Invalid min_points: {got} (must be between 2 and window_capacity {window_capacity})"
                )
            }
            Self::UnsupportedFeature { adapter, feature } => {
                write!(f, "Adapter '{adapter}' does not support feature: {feature}")
            }
            Self::DuplicateParameter { parameter } => {
                write!(
                    f,
                    "Parameter '{parameter}' was set multiple times. Each parameter can only be configured once."
                )
            }
            Self::RuntimeError(msg) => write!(f, "Runtime error: {}", msg),
            Self::InvalidCell(cell) => {
                write!(f, "Invalid cell size: {cell} (must be in range (0, 1])")
            }
            Self::InsufficientVertices {
                required,
                limit,
                cell,
                cell_provided,
                limit_provided,
            } => {
                let cell_desc = if *cell_provided {
                    format!("user-provided cell size {cell}")
                } else {
                    format!("default cell size {cell}")
                };
                let limit_desc = if *limit_provided {
                    format!("user-provided limit {limit}")
                } else {
                    format!("default limit (N = {limit})")
                };

                if !*cell_provided && *limit_provided {
                    write!(
                        f,
                        "Insufficient vertices: {cell_desc} does not work with {limit_desc}. Try passing a larger cell size manually."
                    )
                } else {
                    write!(
                        f,
                        "Insufficient vertices: {cell_desc} requires ~{required} vertices, but {limit_desc} is too small"
                    )
                }
            }
            Self::InvalidOption {
                option,
                value,
                valid,
            } => {
                write!(
                    f,
                    "Invalid value '{value}' for '{option}'. Valid options: {valid}"
                )
            }
            Self::ParseErrors(errors) => {
                write!(f, "Multiple configuration errors ({} total):", errors.len())?;
                for (i, e) in errors.iter().enumerate() {
                    write!(f, " [{i}] {e}")?;
                }
                Ok(())
            }
        }
    }
}

#[cfg(feature = "std")]
impl Error for LoessError {}