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
//! # anofox-forecast
//!
//! Time series forecasting library for Rust.
//!
//! Provides 35+ forecasting models including ARIMA, ETS, Theta,
//! and baseline methods, along with seasonality decomposition (STL/MSTL),
//! changepoint detection, and outlier detection.
//!
//! For comprehensive periodicity detection, see the
//! [fdars](https://crates.io/crates/fdars-core) crate.
//!
//! # Architecture Decisions
//!
//! ## Cross-Validation Split (`ts_cv_split`)
//!
//! Time series cross-validation with data leakage prevention is implemented in the
//! [forecast-extension](https://github.com/DataZooDE/forecast-extension) DuckDB extension
//! rather than in this crate. This section documents the rationale.
//!
//! ### Why CV Split is Not Part of `TimeSeries`
//!
//! The [`TimeSeries`](crate::core::TimeSeries) struct represents a single time series with
//! its values, timestamps, and metadata. Cross-validation splitting was considered as a
//! method on `TimeSeries` but was intentionally kept separate for these reasons:
//!
//! 1. **Cross-series coordination**: Fold generation is a global operation across multiple
//! series, not a per-series operation. CV requires consistent fold boundaries across all
//! series in a dataset.
//!
//! 2. **External feature handling**: Unknown future features like `stockout` flags or
//! `segment_id` changes are external columns that don't belong in the series data model.
//! These require schema-aware handling at the data layer.
//!
//! 3. **Data manipulation efficiency**: DuckDB's vectorized execution is more efficient for
//! the bulk data operations (filtering, joining, filling) that CV split requires.
//!
//! 4. **Schema flexibility**: SQL macros can handle arbitrary column schemas without
//! requiring Rust to know the schema at compile time.
//!
//! ### Component Distribution
//!
//! | Component | Location | Rationale |
//! |-----------|----------|-----------|
//! | Fold generation | DuckDB extension | Cross-series coordination, global operation |
//! | Train/test assignment | SQL/DuckDB | Simple comparison, vectorized execution |
//! | Unknown feature filling | Rust UDF via DuckDB | Per-series state tracking |
//! | Orchestration | SQL macro | Flexible, schema-agnostic |
//!
//! ### Using CV Functionality
//!
//! For time series cross-validation with data leakage prevention, use the `ts_cv_split`
//! function from the [forecast-extension](https://github.com/DataZooDE/forecast-extension):
//!
//! ```sql
//! -- Example: Generate CV folds with unknown feature handling
//! SELECT * FROM ts_cv_split(
//! my_data,
//! n_splits := 3,
//! horizon := 7,
//! unknown_features := ['stockout', 'segment_id']
//! );
//! ```
//!
//! See [forecast-extension#54](https://github.com/DataZooDE/forecast-extension/issues/54)
//! for implementation details.
//!
//! ### Future Considerations
//!
//! If per-series CV semantics become necessary in Rust (e.g., for standalone use without
//! DuckDB), the fold generation logic could be extracted:
//!
//! ```rust,ignore
//! pub struct CvFoldGenerator {
//! n_splits: usize,
//! horizon: usize,
//! gap: usize,
//! }
//!
//! impl CvFoldGenerator {
//! pub fn folds(&self, series_len: usize) -> Vec<usize> {
//! // Returns training end indices for each fold
//! }
//! }
//! ```
//!
//! This would allow fold generation to be shared while keeping data manipulation
//! in the appropriate layer (SQL for multi-series datasets, Rust for single-series use).
// Allow some clippy warnings for cleaner code in specific cases
// is_multiple_of is unstable on WASM
// Prevent use of parallel feature on WASM targets (rayon requires OS threads)
compile_error!;
pub use ;