1use std::fmt::Debug;
2
3use nalgebra::{DMatrix, DVector, RealField, Scalar};
4use num_traits::Float;
5
6use crate::distrib::Regression;
7use crate::Error;
8
9pub(crate) mod adf;
23pub(crate) mod dickeyfuller;
24
25#[derive(Debug, Clone)]
27pub struct Report<F: Debug + Clone> {
28 pub test_statistic: F,
30 pub size: usize,
32}
33
34pub(crate) fn prepare<F: RealField + Scalar + Float>(
38 y: &DVector<F>,
39 n: usize,
40 regression: Regression,
41) -> Result<(DVector<F>, DMatrix<F>, usize), Error> {
42 let y_len = y.len();
43
44 if y_len <= n + 1 {
45 return Err(Error::NotEnoughSamples);
46 }
47
48 let y_t_1_full = y.clone().remove_row(y_len - 1);
51
52 let delta_y = y.clone().remove_row(0) - &y_t_1_full;
56
57 let delta_y_output = delta_y.clone().remove_rows(0, n);
60
61 let mut x = DMatrix::zeros(y_len - n - 1, n + 1);
66
67 let y_t_1 = y_t_1_full.remove_rows(0, n);
69 x.column_mut(0).copy_from(&y_t_1);
70
71 if n > 0 {
73 let mut delta_y_shifted = delta_y.clone().remove_row(delta_y.len() - 1);
74
75 for i in 0..n {
76 let col = delta_y_shifted.clone().remove_rows(0, n - i - 1);
77 x.column_mut(i + 1).copy_from(&col);
78 let delta_y_shifted_len = delta_y_shifted.len();
79 delta_y_shifted = delta_y_shifted.remove_row(delta_y_shifted_len - 1);
80 }
81 }
82
83 if regression != Regression::NoConstantNoTrend {
84 let constant = F::from(1.0).ok_or(Error::ConversionFailed)?;
86 let a = vec![constant; x.nrows()];
87 x.extend(a)
88 }
89
90 if regression == Regression::ConstantAndTrend {
91 let tt: Result<Vec<F>, crate::Error> = (1..x.nrows() + 1)
93 .map(|i| F::from(i as f64).ok_or(Error::ConversionFailed))
94 .collect();
95 match tt {
96 Ok(tt) => x.extend(tt),
97 Err(_) => return Err(Error::ConversionFailed),
98 };
99 }
100
101 Ok((delta_y_output.into_owned(), x, y_len - n - 1))
102}
103
104#[cfg(test)]
105mod tests {
106 use nalgebra::{DMatrix, Matrix, Vector};
107
108 use crate::distrib::Regression;
109
110 #[test]
111 fn test_prepare_constant() {
112 let sz = 10;
114 let n = 2;
115
116 let y = vec![1., 3., 6., 10., 15., 21., 28., 36., 45., 55.];
117 assert_eq!(y.len(), sz);
118
119 let row_count = sz - n - 1;
120 let column_count = n + 2;
121
122 let expected = DMatrix::from_row_slice(
123 row_count,
124 column_count,
125 &[
126 6., 3., 2., 1., 10., 4., 3., 1., 15., 5., 4., 1., 21., 6., 5., 1., 28., 7., 6., 1., 36., 8., 7., 1., 45., 9., 8., 1., ],
135 );
136
137 let y = Matrix::from(y);
138
139 let regression = Regression::Constant;
140
141 let (delta_y, x, sz_) = super::prepare(&y, n, regression).unwrap();
143
144 assert_eq!(sz_, sz - n - 1);
146
147 assert_eq!(delta_y, Vector::from(vec![4., 5., 6., 7., 8., 9., 10.]));
149
150 assert_eq!(
151 x.shape(),
152 (row_count, column_count),
153 "The shape of x was not as expected"
154 );
155 assert_eq!(
156 x, expected,
157 "The output matrix x did not match the expected result"
158 );
159 }
160
161 #[test]
162 fn test_prepare_constant_and_trend() {
163 let sz = 10;
165 let n = 2;
166
167 let y = vec![1., 3., 6., 10., 15., 21., 28., 36., 45., 55.];
168 assert_eq!(y.len(), sz);
169
170 let row_count = sz - n - 1;
171 let column_count = n + 3;
172
173 let expected = DMatrix::from_row_slice(
174 row_count,
175 column_count,
176 &[
177 6., 3., 2., 1., 1., 10., 4., 3., 1., 2., 15., 5., 4., 1., 3., 21., 6., 5., 1., 4., 28., 7., 6., 1., 5., 36., 8., 7., 1., 6., 45., 9., 8., 1., 7., ],
186 );
187
188 let y = Matrix::from(y);
189
190 let regression = Regression::ConstantAndTrend;
191
192 let (delta_y, x, sz_) = super::prepare(&y, n, regression).unwrap();
194
195 assert_eq!(sz_, sz - n - 1);
197
198 assert_eq!(delta_y, Vector::from(vec![4., 5., 6., 7., 8., 9., 10.]));
200
201 assert_eq!(
202 x.shape(),
203 (row_count, column_count),
204 "The shape of x was not as expected"
205 );
206 assert_eq!(
207 x, expected,
208 "The output matrix x did not match the expected result"
209 );
210 }
211
212 #[test]
213 fn test_prepare_no_constant_no_trend() {
214 let sz = 10;
216 let n = 2;
217
218 let y = vec![1., 3., 6., 10., 15., 21., 28., 36., 45., 55.];
219 assert_eq!(y.len(), sz);
220
221 let row_count = sz - n - 1;
222 let column_count = n + 1;
223
224 let expected = DMatrix::from_row_slice(
225 row_count,
226 column_count,
227 &[
228 6., 3., 2., 10., 4., 3., 15., 5., 4., 21., 6., 5., 28., 7., 6., 36., 8., 7., 45., 9., 8., ],
237 );
238
239 let y = Matrix::from(y);
240 let regression = Regression::NoConstantNoTrend;
241
242 let (delta_y, x, sz_) = super::prepare(&y, n, regression).unwrap();
244
245 assert_eq!(sz_, sz - n - 1);
247
248 assert_eq!(delta_y, Vector::from(vec![4., 5., 6., 7., 8., 9., 10.]));
250 assert_eq!(
251 x.shape(),
252 (row_count, column_count),
253 "The shape of x was not as expected"
254 );
255 assert_eq!(
256 x, expected,
257 "The output matrix x did not match the expected result"
258 );
259 }
260
261 #[test]
262 fn test_prepare_minimum_size_00() {
263 let n = 0;
264
265 let y: Vec<f32> = vec![];
266
267 let y = Matrix::from(y);
268
269 let res = super::prepare(&y, n, Regression::Constant);
270 assert!(res.is_err());
271 }
272
273 #[test]
274 fn test_prepare_minimum_size_0() {
275 let n = 0;
276
277 let y = vec![1.];
278
279 let y = Matrix::from(y);
280
281 let res = super::prepare(&y, n, Regression::Constant);
282 assert!(res.is_err());
283 }
284
285 #[test]
286 fn test_prepare_minimum_size_1() {
287 let n = 1;
288
289 let y = vec![1.];
290
291 let y = Matrix::from(y);
292
293 let res = super::prepare(&y, n, Regression::Constant);
294 assert!(res.is_err());
295 }
296
297 #[test]
298 fn test_prepare_minimum_size_2() {
299 let n = 1;
300
301 let y = vec![1., 3.];
302
303 let y = Matrix::from(y);
304
305 let res = super::prepare(&y, n, Regression::Constant);
306 assert!(res.is_err());
307 }
308
309 #[test]
310 fn test_prepare_minimum_size_3() {
311 let n = 2;
312
313 let y = vec![1., 3.];
314
315 let y = Matrix::from(y);
316
317 let res = super::prepare(&y, n, Regression::Constant);
318 assert!(res.is_err());
319 }
320}