# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0]

### Overview

- The main goal of this release is to transform the project into a core LOWESS implementation, removing all dependencies and adapting the API to be more generic and flexible. For this matter, we will drop the ndarray and rayon dependencies, refactor the code to simplify the flow, and adapt the API to be more generic and flexible.
- The performance is improved from 4-16× faster performance than Python's statsmodels in the previous version to 4-29× faster performance than Python's statsmodels.
- The API is simplified and made more generic and flexible.
- Benchmarking and validation code is moved to a separate branch.
- Various other improvements are made to improve performance, ease of use, and maintainability.
- The MRSV is reduced from 1.86 to 1.85 (the minimum for Rust edition 2024), making the crate more compatible with other crates.

### Changed

- Changed the license from MIT to dual AGPL-3.0 and Commercial License.
- Improved the ci.yml file to ease the release process.
- The documentation is improved significantly, with better and more centered examples, diagrams, and tables.
- The LOC count is reduced from 3863 to 3263, making the codebase more maintainable.
- More validation logic is added to ensure the library's correctness.

### Added

- Added a Makefile to ease the development process.
- New examples are added to ease the usage of the library.

### Removed

- Removed `rayon` and `ndarray` dependencies completely.
- Removed convenicnece re-exports all over the codebase.
- Removed all validation and comparison code.
- Removed all benchmarking code.

## [0.3.0]

### Changed

- Updated the Rust version to 1.86.0.
- Updated the criterion version to 0.8.
- Modified the features:
  - In default (std) mode: ndarray/std and rayon are included by default.
  - In no_std mode: ndarray (no std) is included by default, but rayon is not included.
  - Now, instead of having default, ndarray, and parallel features, we simply have a default std build and a no-std build (cargo build --no-default-features).
  - This will simplify the build process, ensuring in a std build the user gets the optimal performance (but with the option of disabling rayon during runtime, i.e., 'parallel = false'), while also providing a no_std build for users who need to compile without std.
- Updated the project structure in CONTRIBUTING.md.
- Improved documentation.
- Updated the benchmarking results.

### Fixed

- Now the no-std build compiles and runs successfully.

### Removed

- Removed the sequential, parallel, and ndarray adaptors. Now, they are part of standard adaptor.

## [0.2.0]

### Changed

#### Architecture & Organization

- Restructured project to reduce intra-module dependencies for improved maintainability (see CONTRIBUTING.md for new structure).

#### API & Terminology

- Renamed "quartic" kernel to "biweight" to match standard statistical terminology (implementation unchanged)
- Renamed `compute_robust_weights` to `compute_bisquare_weights` for specificity
- Renamed `compute_robust_scale` to `compute_mad_scale` for clarity
- Added `HealthCheckConfig` for customizable thresholds in `health_check_with()`, keeping `health_check()` backward-compatible

#### Algorithm Improvements

- Cross-validation now uses true k-fold validation (parallelized when appropriate) then refits on full data with selected fraction
- When both CV and auto-convergence enabled, selected fraction is re-fit using auto-convergence instead of fixed iterations
- Online LOWESS now performs O(span) incremental local WLS updates per insertion instead of re-fitting entire window
- Streaming mode now honors `builder.delta` parameter (previously disabled)
- Parallel execution now uses delta-aware path for large datasets (n ≥ PARALLEL_THRESHOLD)

#### Performance Optimizations

- Merged `compute_weight_fast` and `compute_weight` with early-return optimization for bounded kernels
- Reuse single preallocated weights buffer in sequential path to reduce memory allocations
- Precompute current point's kernel weight to avoid recomputation in standard error calculation
- Extract LOWESS loop into `lowess_parallel_impl` to deduplicate parallel entry points
- Parallelize residual computation in parallel implementation for n ≥ PARALLEL_THRESHOLD
- Replace runtime bandwidth checks with debug-only assertions in hot loops
- Truncate Gaussian kernel at |u| > 6.0 for speed (returns tiny positive weight to avoid zero normalization issues)
- Added `PI_OVER_2` constant to avoid recomputing in cosine kernel

#### Numerical Stability & Robustness

- Use stable sort in `sort_by_x` to preserve relative order of tied x-values for determinism
- Scale threshold now uses `max(SCALE_THRESHOLD * mean_abs, MIN_TUNED_SCALE)` to prevent vanishing thresholds
- `compute_mad_scale` returns small positive epsilon instead of zero when MAD == 0
- Fixed Gaussian kernel conversion to treat failed `to_f64` as infinite distance (zero weight) instead of zero distance
- Guard against tiny bandwidths in `OnlineLowess` with `min_bandwidth` threshold (1e-10)
- Clamp and validate `max_iterations` (treat 0 as 1, cap at 1000)
- Guard `compute_intervals` against non-finite interval widths by replacing with small epsilon
- `WeightParams::new` validates/clamps bandwidth and computes h1/h9 for numerical safety

#### Standard Error & Confidence Intervals

- Fixed leverage calculation using precomputed current-point weight for normalization
- Fixed standard error computation: use effective sample size (sum of weights) minus model parameters for degrees of freedom
- Fixed effective_df computation: derive leverages as (SE / residual_sd)² instead of summing SE²
- Enforce confidence-level validation: `approximate_z_score` returns Result and propagates errors
- Fixed `validate_standard_errors` to allow zero values and cap at 10×y_range
- Added input validation to interval functions: verify lengths, handle zero-length, propagate errors

#### Window & Index Management

- Introduced `WindowBounds` struct to replace separate left/right parameters
- `compute_weights` returns rightmost included index, used by `fit_point`
- Zero weight buffer from `left..n` at start to prevent stale weights
- Defensive bounds check in `fit_point` returns None for out-of-range indices
- `update_window` checks and clamps indices, returns safely on invalid input

#### Delta & Interpolation

- Fixed `interpolate_gap` to handle duplicate x endpoints by averaging fits
- Clarified `skip_close_points` index calculation using `next = last + 1 + pos`
- Added optional `fitted_mask` parameter to `skip_close_points` for future optimization
- Prevent division-by-zero in `interpolate_prediction` for duplicate bracketing x values

### Streaming & Chunking

- Fixed overlap handling: correctly detect pre-overlapped input to avoid duplication
- Added overlap/chunk size validation
- Clamp overlap to at most half chunk size
- Handle zero/empty chunk sizes to prevent infinite partitioning
- `ChunkedLowessBuilder::process_chunks` now non-consuming (`&self`) with per-chunk validation
- Support both sequential and parallel execution without consuming builder

#### Cross-Validation

- Centralized training-set construction with `build_subset_from_indices`
- Updated k-fold/LOOCV to use centralized construction, eliminating duplicate slicing
- Prevent panic by defensively handling empty `cv_scores` with safe default index

#### Code Clarity

- Extracted magic literal `2.0` into `LINEAR_PARAMS` constant for intercept + slope
- Renamed `kernel_idx` to `kernel_value` for accuracy
- Simplified `is_effectively_zero` to use scale-relative epsilon threshold
- Added explicit bandwidth validation to `compute_distance_weights`
- Clarified determinism notes in `parallel.rs`
- Documented Effective Degrees of Freedom calculation
- Added warning about computational expense of `compute_effective_df`
- Added debug assertion to `initialize_window`

#### Build & Dependencies

- Unified `fit_impl` with runtime parallel selection, removing duplicate cfg-gated variants
- Relaxed ndarray `fit_ndarray` bounds: removed unnecessary Send/Sync/'static constraints
- Added max limit for `set_num_threads`

### Removed

#### Deprecated Functions

- Removed global standard-error fallback (σ_resid / √n)
- Removed duplicate `compute_robust_weights` from `kernel.rs`
- Removed `find_rightmost_point` and `compute_range` (now handled by `compute_weights`)
- Removed `compute_weight_batch` (no planned use)
- Removed `compute_bisquare_weights` (replaced by single `compute_bisquare_weight` API)
- Removed unused diagnostic functions: `are_weights_uniform`, `effective_sample_size`
- Removed unimplemented functions: `compute_leverage`, `compute_effective_df`, `compute_residual_variance`, `weighted_polynomial_fit`, `locally_constant_fit`
- Removed duplicate `normalize_weights` and `compute_weighted_average` from `regression.rs`
- Removed `recommended_for_lowess` and `most_efficient` from `kernel.rs`

### Fixed

- Kernel efficiency calculation now returns accurate values (previously off by <0.005)
- Gaussian kernel conversion handles extreme values correctly
- Streaming overlap logic prevents duplicate/missing points
- Parallel builds now consistent with non-parallel builds (unified `fit_impl`)
- Zero-weight fallbacks now use correct window bounds from `compute_weights`

## [0.1.0]

### Added

#### Core Functionality

- Initial implementation of LOWESS (Locally Weighted Scatterplot Smoothing) algorithm based on Cleveland (1979)
- Type-safe builder pattern API via `Lowess::new()` for ergonomic configuration
- Support for `f32` and `f64` floating-point types through generic `Float` trait
- Robust smoothing via iteratively reweighted least squares (IRLS) with configurable iterations
- Deterministic output with reproducible results for fixed inputs and configuration

#### Weight Functions & Kernels

- Seven kernel weight functions:
  - Tricube (default, Cleveland's original)
  - Epanechnikov
  - Gaussian
  - Uniform
  - Quartic
  - Cosine
  - Triangle
- Kernel metadata via `WeightFunctionInfo` trait

#### Statistical Features

- Per-point standard error estimation
- Confidence intervals (mean response) at configurable levels (e.g., 95%)
- Prediction intervals (new observations) at configurable levels
- Optional residual computation
- Robustness weights output from IRLS iterations

#### Diagnostics

- Comprehensive fit diagnostics including:
  - Root Mean Squared Error (RMSE)
  - Mean Absolute Error (MAE)
  - R² (coefficient of determination)
  - Akaike Information Criterion (AIC)
  - Corrected AIC (AICc) for small samples
  - Effective degrees of freedom
  - Count of downweighted points

#### Cross-Validation

- Automatic fraction selection via cross-validation
- Multiple CV strategies:
  - Simple train/test split (default)
  - K-fold cross-validation
  - Leave-one-out cross-validation (LOOCV)
- CV score reporting for model selection transparency

#### Performance Optimizations

- Delta-based interpolation for dense data (reduces O(n²) to effectively O(n×k))
- Configurable delta threshold (auto-calculates to 1% of x-range by default)
- Auto-convergence for IRLS iterations with configurable tolerance
- Early stopping when fitted values stabilize

#### Memory-Efficient Processing

- `StreamingLowess` for chunk-based processing of large datasets
- `OnlineLowess` for real-time sliding window smoothing
- `ChunkedLowess` for processing data in predefined chunks
- All streaming variants support the full configuration API

#### Optional Features

- **`parallel`**: Multi-threaded cross-validation and fitting via Rayon
  - Parallel CV across candidate fractions
  - Configurable chunk sizes for parallel processing
  - Linear speedup with available cores
- **`ndarray`**: Seamless integration with ndarray ecosystem
  - `fit_ndarray()` for direct ndarray input
  - Conversion methods: `to_ndarray()`, `as_ndarray()`
  - Support for confidence and prediction intervals in ndarray format
- **`std`** (default): Standard library support
  - Disable for `no_std` compatibility (requires `alloc`)

#### Error Handling

- Explicit `LowessError` enum with variants:
  - `EmptyInput`: Empty input arrays
  - `MismatchedInputs`: Different lengths for x and y
  - `InvalidFraction`: Fraction not in (0, 1]
  - `InvalidDelta`: Negative delta value
  - `InvalidNumericValue`: NaN or infinite values in inputs
  - `TooFewPoints`: Insufficient data for smoothing
  - `InvalidConfidenceLevel`: Confidence level not in (0, 1)
- No panics in release builds (defensive programming with debug assertions)
- Graceful degradation with documented fallback behaviors

#### Numerical Stability

- MAD (Median Absolute Deviation) for robust scale estimation
- Fallback to mean absolute residual when MAD ≈ 0
- Minimum tuned scale clamping to avoid division by zero
- Zero-weight neighborhood handling with configurable policies:
  - `UseLocalMean`: Fallback to local average
  - `ReturnOriginal`: Use original y value
  - `ReturnNone`: Propagate failure
- Uniform weight fallback when all kernel weights are zero
- Machine-epsilon aware comparisons in `is_effectively_zero()`

#### Documentation

- Comprehensive crate-level documentation with:
  - Conceptual overview of LOWESS
  - Parameter descriptions and typical ranges
  - Output structure documentation
  - Error handling guidance
  - Performance and operational best practices
  - Determinism and numeric safety guarantees
- Module-level documentation for all public modules:
  - `builder`: Builder pattern and result types
  - `core`: Main fitting algorithm
  - `kernel`: Weight functions
  - `regression`: Weighted least squares
  - `robustness`: IRLS implementation
  - `confidence`: Interval estimation
  - `streaming`: Memory-efficient variants
  - `utils`: Input validation and helpers
  - `parallel`: Parallel execution (feature-gated)
- Function-level documentation with examples
- Production usage guidelines and monitoring recommendations

#### Convenience Functions

- `lowess()`: One-line smoothing with defaults
- `lowess_with_fraction()`: Quick smoothing with custom fraction
- `lowess_robust()`: Pre-configured robust smoothing (5 iterations)
- `Lowess::robust()`: Builder preset for robust fitting

### Implementation Details

#### Algorithm Fidelity

- Faithful implementation of Cleveland (1979) algorithm
- Compatible with R's `stats::lowess()` function
- Bisquare (Tukey's biweight) robustness weighting
- Tricube kernel as default (Cleveland's original choice)
- MAD-based scale estimation with 1.4826 correction factor

#### Performance Characteristics

- O(n²) complexity for basic fitting
- O(n×k) effective complexity with delta optimization (k ≪ n for dense data)
- Linear speedup with parallel cross-validation (when `parallel` feature enabled)
- Minimal allocations through buffer reuse in hot paths

#### Testing

- Comprehensive unit tests for all modules
- Integration tests for end-to-end workflows
- Edge case coverage (empty inputs, single points, identical values, outliers)
- Numerical stability tests (near-zero scales, extreme values)
- Cross-validation correctness tests

#### Platform Support

- All Rust targets with `f32`/`f64` support
- `no_std` compatible with `alloc` (disable default features)
- Tested on Linux, macOS, and Windows
- MSRV (Minimum Supported Rust Version): 1.70.0

### Notes

This initial release provides a production-ready LOWESS implementation with advanced features beyond traditional ports:

- Statistical intervals and diagnostics for scientific computing
- Streaming variants for large-scale data processing
- Parallel execution for computational efficiency
- Comprehensive error handling and numerical stability
- Extensive documentation with domain-specific guidance (genomics, bioinformatics)

The API is designed for stability and follows Rust best practices. Future releases will maintain backward compatibility according to semantic versioning.
