1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
//! # LOWESS — Locally Weighted Scatterplot Smoothing for Rust
//!
//! The fastest, most robust, and most feature-complete language-agnostic
//! LOWESS (Locally Weighted Scatterplot Smoothing) implementation for **Rust**.
//!
//! ## What is LOWESS?
//!
//! LOWESS (Locally Weighted Scatterplot Smoothing) is a nonparametric regression
//! method that fits smooth curves through scatter plots. At each point, it fits
//! a weighted polynomial (typically linear) using nearby data points, with weights
//! decreasing smoothly with distance. This creates flexible, data-adaptive curves
//! without assuming a global functional form.
//!
//! ## Documentation
//!
//! > 📚 **Full Documentation**: [lowess.readthedocs.io](https://lowess.readthedocs.io/)
//! >
//! > Comprehensive guides, API references, and tutorials.
//!
//! ## Quick Start
//!
//! ### Typical Use
//!
//! ```rust
//! use lowess::prelude::*;
//!
//! let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
//! let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
//!
//! // Build the model
//! let model = Lowess::new()
//! .fraction(0.5) // Use 50% of data for each local fit
//! .iterations(3) // 3 robustness iterations
//! .adapter(Batch)
//! .build()?;
//!
//! // Fit the model to the data
//! let result = model.fit(&x, &y)?;
//!
//! println!("{}", result);
//! # Result::<(), LowessError>::Ok(())
//! ```
//!
//! ```text
//! Summary:
//! Data points: 5
//! Fraction: 0.5
//!
//! Smoothed Data:
//! X Y_smooth
//! --------------------
//! 1.00 2.00000
//! 2.00 4.10000
//! 3.00 5.90000
//! 4.00 8.20000
//! 5.00 9.80000
//! ```
//!
//! ### Full Features
//!
//! ```rust
//! use lowess::prelude::*;
//!
//! let x = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
//! let y = vec![2.1, 3.8, 6.2, 7.9, 10.3, 11.8, 14.1, 15.7];
//!
//! // Build model with all features enabled
//! let model = Lowess::new()
//! .fraction(0.5) // Use 50% of data for each local fit
//! .iterations(3) // 3 robustness iterations
//! .weight_function(Tricube) // Kernel function
//! .robustness_method(Bisquare) // Outlier handling
//! .delta(0.01) // Interpolation optimization
//! .zero_weight_fallback(UseLocalMean) // Fallback policy
//! .boundary_policy(Extend) // Boundary handling policy
//! .scaling_method(MAD) // Robust scale estimation
//! .auto_converge(1e-6) // Auto-convergence threshold
//! .confidence_intervals(0.95) // 95% confidence intervals
//! .prediction_intervals(0.95) // 95% prediction intervals
//! .return_diagnostics() // Fit quality metrics
//! .return_residuals() // Include residuals
//! .return_robustness_weights() // Include robustness weights
//! .cross_validate(KFold(5, &[0.3, 0.7]).seed(123)) // K-fold CV with 5 folds and 2 fraction options
//! .adapter(Batch) // Batch adapter
//! .build()?;
//!
//! let result = model.fit(&x, &y)?;
//! println!("{}", result);
//! # Result::<(), LowessError>::Ok(())
//! ```
//!
//! ```text
//! Summary:
//! Data points: 8
//! Fraction: 0.5
//! Robustness: Applied
//!
//! LOWESS Diagnostics:
//! RMSE: 0.191925
//! MAE: 0.181676
//! R^2: 0.998205
//! Residual SD: 0.297750
//! Effective DF: 8.00
//! AIC: -10.41
//! AICc: inf
//!
//! Smoothed Data:
//! X Y_smooth Std_Err Conf_Lower Conf_Upper Pred_Lower Pred_Upper Residual Rob_Weight
//! ----------------------------------------------------------------------------------------------------------------
//! 1.00 2.01963 0.389365 1.256476 2.782788 1.058911 2.980353 0.080368 1.0000
//! 2.00 4.00251 0.345447 3.325438 4.679589 3.108641 4.896386 -0.202513 1.0000
//! 3.00 5.99959 0.423339 5.169846 6.829335 4.985168 7.014013 0.200410 1.0000
//! 4.00 8.09859 0.489473 7.139224 9.057960 6.975666 9.221518 -0.198592 1.0000
//! 5.00 10.03881 0.551687 8.957506 11.120118 8.810073 11.267551 0.261188 1.0000
//! 6.00 12.02872 0.539259 10.971775 13.085672 10.821364 13.236083 -0.228723 1.0000
//! 7.00 13.89828 0.371149 13.170829 14.625733 12.965670 14.830892 0.201719 1.0000
//! 8.00 15.77990 0.408300 14.979631 16.580167 14.789441 16.770356 -0.079899 1.0000
//! ```
//!
//! ### Result and Error Handling
//!
//! The `fit` method returns a `Result<LowessResult<T>, LowessError>`.
//!
//! - **`Ok(LowessResult<T>)`**: Contains the smoothed data and diagnostics.
//! - **`Err(LowessError)`**: Indicates a failure (e.g., mismatched input lengths, insufficient data).
//!
//! The `?` operator is idiomatic:
//!
//! ```rust
//! use lowess::prelude::*;
//! # let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
//! # let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
//!
//! let model = Lowess::new().adapter(Batch).build()?;
//!
//! let result = model.fit(&x, &y)?;
//! // or to be more explicit:
//! // let result: LowessResult<f64> = model.fit(&x, &y)?;
//! # Result::<(), LowessError>::Ok(())
//! ```
//!
//! But you can also handle results explicitly:
//!
//! ```rust
//! use lowess::prelude::*;
//! # let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
//! # let y = vec![2.0, 4.1, 5.9, 8.2, 9.8];
//!
//! let model = Lowess::new().adapter(Batch).build()?;
//!
//! match model.fit(&x, &y) {
//! Ok(result) => {
//! // result is LowessResult<f64>
//! println!("Smoothed: {:?}", result.y);
//! }
//! Err(e) => {
//! // e is LowessError
//! eprintln!("Fitting failed: {}", e);
//! }
//! }
//! # Result::<(), LowessError>::Ok(())
//! ```
//!
//! ## Minimal Usage (no_std / Embedded)
//!
//! The crate supports `no_std` environments for embedded devices and resource-constrained systems.
//! Disable default features to remove the standard library dependency:
//!
//! ```toml
//! [dependencies]
//! lowess = { version = "0.5", default-features = false }
//! ```
//!
//! **Minimal example for embedded systems:**
//!
//! ```rust
//! # #[cfg(feature = "std")] {
//! use lowess::prelude::*;
//!
//! // In an embedded context (e.g., sensor data processing)
//! fn smooth_sensor_data() -> Result<(), LowessError> {
//! // Small dataset from sensor readings
//! let x = vec![1.0_f32, 2.0, 3.0, 4.0, 5.0];
//! let y = vec![2.1, 3.9, 6.2, 7.8, 10.1];
//!
//! // Build minimal model (no intervals, no diagnostics)
//! let model = Lowess::new()
//! .fraction(0.5)
//! .iterations(2) // Fewer iterations for speed
//! .adapter(Batch)
//! .build()?;
//!
//! // Fit the model
//! let result = model.fit(&x, &y)?;
//!
//! // Use smoothed values (result.y)
//! // ...
//!
//! Ok(())
//! }
//! # smooth_sensor_data().unwrap();
//! # }
//! ```
//!
//! **Tips for embedded/no_std usage:**
//! - Use `f32` instead of `f64` to reduce memory footprint
//! - Keep datasets small (< 1000 points)
//! - Disable optional features (intervals, diagnostics) to reduce code size
//! - Use fewer iterations (1-2) to reduce computation time
//! - Allocate buffers statically when possible to avoid heap fragmentation
//!
//! ## References
//!
//! - Cleveland, W. S. (1979). "Robust Locally Weighted Regression and Smoothing Scatterplots"
//! - Cleveland, W. S. (1981). "LOWESS: A Program for Smoothing Scatterplots by Robust Locally Weighted Regression"
//!
//! ## srrstats Compliance for rOpenSci Statistical Software Review
//!
//! @srrstats {G1.0} Statistical literature references documented above (Cleveland 1979, 1981).
//! @srrstats {G1.1} This package provides LOWESS smoothing, a nonparametric regression method
//! for fitting smooth curves to scatterplot data using locally weighted linear regression.
//! @srrstats {G1.4} All exported functions and types are documented with rustdoc comments.
//! @srrstats {G1.6} Performance characteristics documented: SIMD-optimized solvers, O(n*k)
//! complexity where k is the window size, supports streaming and online modes.
//!
//! ## License
//!
//! See the repository for license information and contribution guidelines.
extern crate alloc;
// Layer 1: Primitives - data structures and basic utilities.
// Layer 2: Math - pure mathematical functions.
// Layer 3: Algorithms - core LOWESS algorithms.
// Layer 4: Evaluation - post-processing and diagnostics.
// Layer 5: Engine - orchestration and execution control.
// Layer 6: Adapters - execution mode adapters.
// High-level fluent API for LOWESS smoothing.
// Standard LOWESS prelude.
// Internal modules for development and testing.
//
// This module re-exports internal modules for development and testing purposes.
// It is only available with the `dev` feature enabled.