1pub const DEFAULT_SEASONAL: usize = 7;
59
60#[derive(Debug, Clone, PartialEq)]
62pub enum StlError {
63 OptionOutOfRange {
66 option: &'static str,
68 requirement: &'static str,
70 got: f64,
72 min: Option<f64>,
74 exclusive_min: Option<f64>,
76 max: Option<f64>,
78 },
79 EvenSpan {
81 option: &'static str,
83 got: usize,
85 },
86 TooFewObservations {
88 needed: usize,
90 got: usize,
92 },
93 ValueNotFinite {
95 index: usize,
97 },
98}
99
100impl core::fmt::Display for StlError {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 match self {
103 StlError::OptionOutOfRange {
104 option,
105 requirement,
106 got,
107 ..
108 } => write!(f, "stl: `{option}` must be {requirement} (got {got})"),
109 StlError::EvenSpan { option, got } => write!(
110 f,
111 "stl: `{option}` must be odd — a Loess span is centred on its point (got {got})"
112 ),
113 StlError::TooFewObservations { needed, got } => write!(
114 f,
115 "stl: needs at least two full cycles, {needed} values (got {got})"
116 ),
117 StlError::ValueNotFinite { index } => {
118 write!(f, "stl: the value at index {index} is not finite")
119 }
120 }
121 }
122}
123
124impl std::error::Error for StlError {}
125
126#[derive(Debug, Clone, PartialEq)]
128pub struct StlResult {
129 pub trend: Vec<f64>,
131 pub seasonal: Vec<f64>,
133 pub residual: Vec<f64>,
135 pub weights: Vec<f64>,
138 pub trend_strength: f64,
140 pub seasonal_strength: f64,
142 pub period: usize,
144 pub seasonal_span: usize,
146 pub trend_span: usize,
148 pub low_pass_span: usize,
150}
151
152#[derive(Debug, Clone, PartialEq)]
167pub struct Stl {
168 period: usize,
169 seasonal: usize,
170 trend: Option<usize>,
171 low_pass: Option<usize>,
172 seasonal_degree: usize,
173 trend_degree: usize,
174 low_pass_degree: usize,
175 robust: bool,
176 inner_iterations: Option<usize>,
177 outer_iterations: Option<usize>,
178}
179
180impl Stl {
181 pub fn new(period: usize) -> Self {
183 Self {
184 period,
185 seasonal: DEFAULT_SEASONAL,
186 trend: None,
187 low_pass: None,
188 seasonal_degree: 1,
189 trend_degree: 1,
190 low_pass_degree: 1,
191 robust: false,
192 inner_iterations: None,
193 outer_iterations: None,
194 }
195 }
196
197 pub fn seasonal(mut self, span: usize) -> Self {
200 self.seasonal = span;
201 self
202 }
203
204 pub fn trend(mut self, span: usize) -> Self {
206 self.trend = Some(span);
207 self
208 }
209
210 pub fn low_pass(mut self, span: usize) -> Self {
212 self.low_pass = Some(span);
213 self
214 }
215
216 pub fn seasonal_degree(mut self, degree: usize) -> Self {
218 self.seasonal_degree = degree;
219 self
220 }
221
222 pub fn trend_degree(mut self, degree: usize) -> Self {
224 self.trend_degree = degree;
225 self
226 }
227
228 pub fn low_pass_degree(mut self, degree: usize) -> Self {
230 self.low_pass_degree = degree;
231 self
232 }
233
234 pub fn robust(mut self, robust: bool) -> Self {
236 self.robust = robust;
237 self
238 }
239
240 pub fn inner_iterations(mut self, n: usize) -> Self {
242 self.inner_iterations = Some(n);
243 self
244 }
245
246 pub fn outer_iterations(mut self, n: usize) -> Self {
248 self.outer_iterations = Some(n);
249 self
250 }
251
252 pub fn default_trend_span(period: usize, seasonal: usize) -> usize {
256 let t = (1.5 * period as f64 / (1.0 - 1.5 / seasonal as f64)).ceil() as usize;
257 if t % 2 == 0 {
258 t + 1
259 } else {
260 t
261 }
262 }
263
264 pub fn default_low_pass_span(period: usize) -> usize {
267 let l = period + 1;
268 if l % 2 == 0 {
269 l + 1
270 } else {
271 l
272 }
273 }
274
275 pub fn decompose(&self, data: &[f64]) -> Result<StlResult, StlError> {
282 let period = self.period;
283 if period < 2 {
284 return Err(StlError::OptionOutOfRange {
285 option: "period",
286 requirement: "at least 2",
287 got: period as f64,
288 min: Some(2.0),
289 exclusive_min: None,
290 max: None,
291 });
292 }
293 let span = |option: &'static str, got: usize, above_period: bool| {
294 if got < 3 || (above_period && got <= period) {
295 let (min, exclusive_min, requirement) = if above_period && period >= 3 {
296 (None, Some(period as f64), "odd and above the period")
297 } else {
298 (Some(3.0), None, "odd and at least 3")
299 };
300 return Err(StlError::OptionOutOfRange {
301 option,
302 requirement,
303 got: got as f64,
304 min,
305 exclusive_min,
306 max: None,
307 });
308 }
309 if got % 2 == 0 {
310 return Err(StlError::EvenSpan { option, got });
311 }
312 Ok(got)
313 };
314 let seasonal = span("seasonal", self.seasonal, false)?;
315 let trend = span(
316 "trend",
317 self.trend
318 .unwrap_or_else(|| Self::default_trend_span(period, seasonal)),
319 true,
320 )?;
321 let low_pass = span(
322 "low_pass",
323 self.low_pass
324 .unwrap_or_else(|| Self::default_low_pass_span(period)),
325 true,
326 )?;
327 for (option, degree) in [
328 ("seasonal_degree", self.seasonal_degree),
329 ("trend_degree", self.trend_degree),
330 ("low_pass_degree", self.low_pass_degree),
331 ] {
332 if degree > 1 {
333 return Err(StlError::OptionOutOfRange {
334 option,
335 requirement: "0 or 1",
336 got: degree as f64,
337 min: Some(0.0),
338 exclusive_min: None,
339 max: Some(1.0),
340 });
341 }
342 }
343 let inner = self
344 .inner_iterations
345 .unwrap_or(if self.robust { 2 } else { 5 });
346 if inner == 0 {
347 return Err(StlError::OptionOutOfRange {
348 option: "inner_iterations",
349 requirement: "at least 1",
350 got: 0.0,
351 min: Some(1.0),
352 exclusive_min: None,
353 max: None,
354 });
355 }
356 let outer = self
357 .outer_iterations
358 .unwrap_or(if self.robust { 15 } else { 0 });
359
360 let n = data.len();
361 if n < 2 * period {
362 return Err(StlError::TooFewObservations {
363 needed: 2 * period,
364 got: n,
365 });
366 }
367 if let Some(index) = data.iter().position(|v| !v.is_finite()) {
368 return Err(StlError::ValueNotFinite { index });
369 }
370
371 let spans = Spans {
372 period,
373 seasonal,
374 trend,
375 low_pass,
376 seasonal_degree: self.seasonal_degree,
377 trend_degree: self.trend_degree,
378 low_pass_degree: self.low_pass_degree,
379 };
380 let mut trend_c = vec![0.0; n];
381 let mut seasonal_c = vec![0.0; n];
382 let mut weights = vec![1.0; n];
383 let mut use_weights = false;
384 let mut pass = 0;
385 loop {
386 inner_loop(
387 data,
388 &spans,
389 inner,
390 use_weights.then_some(weights.as_slice()),
391 &mut seasonal_c,
392 &mut trend_c,
393 );
394 pass += 1;
395 if pass > outer {
396 break;
397 }
398 let fit: Vec<f64> = trend_c
399 .iter()
400 .zip(&seasonal_c)
401 .map(|(t, s)| t + s)
402 .collect();
403 robustness_weights(data, &fit, &mut weights);
404 use_weights = true;
405 }
406 if outer == 0 {
407 weights.iter_mut().for_each(|w| *w = 1.0);
408 }
409
410 let residual: Vec<f64> = (0..n)
411 .map(|i| data[i] - trend_c[i] - seasonal_c[i])
412 .collect();
413 let var_r = variance(&residual);
414 let strength = |component: &[f64]| {
415 let sum: Vec<f64> = component
416 .iter()
417 .zip(&residual)
418 .map(|(c, r)| c + r)
419 .collect();
420 let v = variance(&sum);
421 if v > 0.0 {
422 (1.0 - var_r / v).max(0.0)
423 } else {
424 0.0
425 }
426 };
427 Ok(StlResult {
428 trend_strength: strength(&trend_c),
429 seasonal_strength: strength(&seasonal_c),
430 trend: trend_c,
431 seasonal: seasonal_c,
432 residual,
433 weights,
434 period,
435 seasonal_span: seasonal,
436 trend_span: trend,
437 low_pass_span: low_pass,
438 })
439 }
440}
441
442struct Spans {
443 period: usize,
444 seasonal: usize,
445 trend: usize,
446 low_pass: usize,
447 seasonal_degree: usize,
448 trend_degree: usize,
449 low_pass_degree: usize,
450}
451
452fn variance(x: &[f64]) -> f64 {
453 let n = x.len() as f64;
454 let mean = x.iter().sum::<f64>() / n;
455 x.iter().map(|v| (v - mean) * (v - mean)).sum::<f64>() / n
456}
457
458fn inner_loop(
461 y: &[f64],
462 s: &Spans,
463 passes: usize,
464 rw: Option<&[f64]>,
465 seasonal: &mut [f64],
466 trend: &mut [f64],
467) {
468 let n = y.len();
469 let np = s.period;
470 let mut detrended = vec![0.0; n];
471 let mut cycle = vec![0.0; n + 2 * np];
472 let mut low = vec![0.0; n];
473 for _ in 0..passes {
474 for i in 0..n {
475 detrended[i] = y[i] - trend[i];
476 }
477 cycle_subseries(
478 &detrended,
479 np,
480 s.seasonal,
481 s.seasonal_degree,
482 rw,
483 &mut cycle,
484 );
485 let filtered = low_pass_filter(&cycle, np);
486 loess(&filtered, s.low_pass, s.low_pass_degree, None, &mut low);
487 for i in 0..n {
488 seasonal[i] = cycle[np + i] - low[i];
489 detrended[i] = y[i] - seasonal[i];
490 }
491 loess(&detrended, s.trend, s.trend_degree, rw, trend);
492 }
493}
494
495fn cycle_subseries(
498 y: &[f64],
499 np: usize,
500 span: usize,
501 degree: usize,
502 rw: Option<&[f64]>,
503 out: &mut [f64],
504) {
505 let n = y.len();
506 let mut sub = Vec::with_capacity(n / np + 1);
507 let mut sub_w = Vec::with_capacity(n / np + 1);
508 let mut smooth = Vec::with_capacity(n / np + 3);
509 let mut work = vec![0.0; n / np + 1];
510 for j in 0..np {
511 sub.clear();
512 sub_w.clear();
513 sub.extend((j..n).step_by(np).map(|i| y[i]));
514 if let Some(rw) = rw {
515 sub_w.extend((j..n).step_by(np).map(|i| rw[i]));
516 }
517 let w = rw.map(|_| sub_w.as_slice());
518 let k = sub.len();
519 smooth.clear();
520 smooth.resize(k + 2, 0.0);
521 loess(&sub, span, degree, w, &mut smooth[1..=k]);
522 let right = span.min(k);
523 smooth[0] = local_fit(&sub, span, degree, 0.0, 1, right, w, &mut work).unwrap_or(smooth[1]);
524 let left = if k >= span { k - span + 1 } else { 1 };
525 smooth[k + 1] = local_fit(&sub, span, degree, (k + 1) as f64, left, k, w, &mut work)
526 .unwrap_or(smooth[k]);
527 for (m, v) in smooth.iter().enumerate() {
528 out[m * np + j] = *v;
529 }
530 }
531}
532
533fn low_pass_filter(x: &[f64], np: usize) -> Vec<f64> {
536 moving_average(&moving_average(&moving_average(x, np), np), 3)
537}
538
539fn moving_average(x: &[f64], len: usize) -> Vec<f64> {
541 let out_len = x.len() + 1 - len;
542 let flen = len as f64;
543 let mut out = Vec::with_capacity(out_len);
544 let mut v: f64 = x[..len].iter().sum();
545 out.push(v / flen);
546 for k in 1..out_len {
547 v = v - x[k - 1] + x[k + len - 1];
548 out.push(v / flen);
549 }
550 out
551}
552
553fn loess(y: &[f64], span: usize, degree: usize, rw: Option<&[f64]>, out: &mut [f64]) {
555 let n = y.len();
556 if n < 2 {
557 out[0] = y[0];
558 return;
559 }
560 let mut work = vec![0.0; n];
561 if span >= n {
562 for i in 1..=n {
563 out[i - 1] =
564 local_fit(y, span, degree, i as f64, 1, n, rw, &mut work).unwrap_or(y[i - 1]);
565 }
566 return;
567 }
568 let half = span.div_ceil(2);
569 let (mut left, mut right) = (1, span);
570 for i in 1..=n {
571 if i > half && right != n {
572 left += 1;
573 right += 1;
574 }
575 out[i - 1] =
576 local_fit(y, span, degree, i as f64, left, right, rw, &mut work).unwrap_or(y[i - 1]);
577 }
578}
579
580#[allow(clippy::too_many_arguments)]
583fn local_fit(
584 y: &[f64],
585 span: usize,
586 degree: usize,
587 xs: f64,
588 left: usize,
589 right: usize,
590 rw: Option<&[f64]>,
591 w: &mut [f64],
592) -> Option<f64> {
593 let n = y.len();
594 let range = (n - 1) as f64;
595 let mut h = (xs - left as f64).max(right as f64 - xs);
596 if span > n {
597 h += ((span - n) / 2) as f64;
598 }
599 let h9 = 0.999 * h;
600 let h1 = 0.001 * h;
601 let mut a = 0.0;
602 for j in left..=right {
603 let r = (j as f64 - xs).abs();
604 let mut wj = 0.0;
605 if r <= h9 {
606 wj = if r <= h1 {
607 1.0
608 } else {
609 (1.0 - (r / h).powi(3)).powi(3)
610 };
611 if let Some(rw) = rw {
612 wj *= rw[j - 1];
613 }
614 a += wj;
615 }
616 w[j - 1] = wj;
617 }
618 if a <= 0.0 {
619 return None;
620 }
621 for wj in &mut w[left - 1..right] {
622 *wj /= a;
623 }
624 if h > 0.0 && degree > 0 {
625 let a: f64 = (left..=right).map(|j| w[j - 1] * j as f64).sum();
626 let b = xs - a;
627 let c: f64 = (left..=right)
628 .map(|j| w[j - 1] * (j as f64 - a) * (j as f64 - a))
629 .sum();
630 if c.sqrt() > 0.001 * range {
631 let b = b / c;
632 for j in left..=right {
633 w[j - 1] *= b * (j as f64 - a) + 1.0;
634 }
635 }
636 }
637 Some((left..=right).map(|j| w[j - 1] * y[j - 1]).sum())
638}
639
640fn robustness_weights(y: &[f64], fit: &[f64], rw: &mut [f64]) {
642 let n = y.len();
643 let r: Vec<f64> = y.iter().zip(fit).map(|(a, b)| (a - b).abs()).collect();
644 let mut sorted = r.clone();
645 let m1 = n / 2; let m2 = n - m1 - 1; sorted.sort_by(|a, b| a.total_cmp(b));
648 let cmad = 3.0 * (sorted[m1] + sorted[m2]);
649 let scale = y.iter().fold(0.0_f64, |m, v| m.max(v.abs()));
650 if cmad <= 1e-12 * scale {
651 rw.iter_mut().for_each(|w| *w = 1.0);
654 return;
655 }
656 let c9 = 0.999 * cmad;
657 let c1 = 0.001 * cmad;
658 for (w, &ri) in rw.iter_mut().zip(&r) {
659 *w = if ri <= c1 {
660 1.0
661 } else if ri <= c9 {
662 let u = ri / cmad;
663 (1.0 - u * u).powi(2)
664 } else {
665 0.0
666 };
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673
674 fn seasonal_line(n: usize) -> Vec<f64> {
675 let pattern = [2.0, -1.0, 0.5, -1.5];
676 (0..n)
677 .map(|i| 0.3 * i as f64 + pattern[i % 4] + 0.05 * (((i * 7919) % 13) as f64 - 6.0))
678 .collect()
679 }
680
681 #[test]
682 fn components_add_up_to_the_series() {
683 let y = seasonal_line(40);
684 let r = Stl::new(4).decompose(&y).unwrap();
685 for (i, v) in y.iter().enumerate() {
686 assert!((r.trend[i] + r.seasonal[i] + r.residual[i] - v).abs() < 1e-12);
687 }
688 assert_eq!((r.seasonal_span, r.trend_span, r.low_pass_span), (7, 9, 5));
689 assert!(r.weights.iter().all(|&w| w == 1.0));
690 }
691
692 #[test]
693 fn default_spans_follow_statsmodels() {
694 assert_eq!(Stl::default_trend_span(12, 7), 23);
695 assert_eq!(Stl::default_trend_span(7, 7), 15);
696 assert_eq!(Stl::default_low_pass_span(12), 13);
697 assert_eq!(Stl::default_low_pass_span(7), 9);
698 }
699
700 #[test]
701 fn refusals_name_the_option() {
702 let y = seasonal_line(40);
703 assert!(matches!(
704 Stl::new(1).decompose(&y),
705 Err(StlError::OptionOutOfRange {
706 option: "period",
707 ..
708 })
709 ));
710 assert!(matches!(
711 Stl::new(4).seasonal(8).decompose(&y),
712 Err(StlError::EvenSpan {
713 option: "seasonal",
714 got: 8
715 })
716 ));
717 assert!(matches!(
718 Stl::new(4).trend(3).decompose(&y),
719 Err(StlError::OptionOutOfRange { option: "trend", exclusive_min: Some(e), .. }) if e == 4.0
720 ));
721 assert!(matches!(
722 Stl::new(4).trend_degree(2).decompose(&y),
723 Err(StlError::OptionOutOfRange {
724 option: "trend_degree",
725 ..
726 })
727 ));
728 assert_eq!(
729 Stl::new(4).decompose(&y[..7]),
730 Err(StlError::TooFewObservations { needed: 8, got: 7 })
731 );
732 let mut bad = y.clone();
733 bad[5] = f64::NAN;
734 assert_eq!(
735 Stl::new(4).decompose(&bad),
736 Err(StlError::ValueNotFinite { index: 5 })
737 );
738 }
739
740 #[test]
741 fn robust_pass_isolates_an_outlier() {
742 let mut y = seasonal_line(48);
743 y[21] += 50.0;
744 let plain = Stl::new(4).decompose(&y).unwrap();
745 let robust = Stl::new(4).robust(true).decompose(&y).unwrap();
746 assert!(robust.weights[21] < 0.01);
747 assert!(robust.residual[21] > 45.0);
748 assert!(robust.residual[21] > plain.residual[21]);
749 }
750
751 #[test]
752 fn two_cycles_fit_exactly_and_rounding_marks_no_outlier() {
753 let y = seasonal_line(24);
756 let r = Stl::new(12).robust(true).decompose(&y).unwrap();
757 assert!(r.residual.iter().all(|v| v.abs() < 1e-9));
758 assert!(r.weights.iter().all(|&w| w == 1.0));
759 }
760
761 #[test]
762 fn unit_and_offset_do_not_change_the_shape() {
763 let y = seasonal_line(48);
764 let base = Stl::new(4).robust(true).decompose(&y).unwrap();
765 for scale in [1e-9, 1e9] {
766 let ys: Vec<f64> = y.iter().map(|v| v * scale + 7.0 * scale).collect();
767 let r = Stl::new(4).robust(true).decompose(&ys).unwrap();
768 for i in 0..y.len() {
769 assert!((r.seasonal[i] / scale - base.seasonal[i]).abs() < 1e-6);
770 assert!((r.weights[i] - base.weights[i]).abs() < 1e-9);
771 }
772 assert!((r.seasonal_strength - base.seasonal_strength).abs() < 1e-9);
773 }
774 }
775}
776
777#[cfg(test)]
781#[allow(clippy::approx_constant)] mod statsmodels_reference {
783 use super::*;
784
785 const Y_A: [f64; 60] = [
788 -0.837, 1.092, 2.924, 2.522, 2.875, 1.517, -0.253, -2.621, -1.958, -1.908, -2.977, -2.431,
789 0.283, 2.564, 4.129, 3.407, 3.442, 2.796, 1.797, -1.15, -1.333, -3.02, -1.687, -0.955,
790 -0.182, 2.155, 3.519, 4.249, 3.793, 3.02, 2.088, -0.256, -2.699, -1.328, -0.227, -0.623,
791 0.974, 4.007, 3.766, 5.139, 4.737, 3.505, 1.892, 0.397, -1.058, -0.66, -1.369, 0.869,
792 1.651, 3.504, 5.64, 5.463, 6.604, 3.893, 1.852, 1.122, 0.75, -1.866, 0.37, 0.427,
793 ];
794 const T_A: [f64; 60] = [
795 -0.7146485637078752,
796 -0.6164832210335263,
797 -0.5181516830760083,
798 -0.4196844754873599,
799 -0.32110131859995555,
800 -0.2225207692561986,
801 -0.12423555675326292,
802 -0.02655075377094378,
803 0.07045746328083816,
804 0.16696728502205307,
805 0.26338291889548854,
806 0.36043534415399964,
807 0.44834507397027185,
808 0.5280747524929036,
809 0.6002229363186898,
810 0.6611364297563962,
811 0.7062536526796435,
812 0.7365967978054677,
813 0.7584153148362388,
814 0.7761606513272636,
815 0.7923364502503742,
816 0.8093936697027934,
817 0.8296387158227432,
818 0.8518631642150369,
819 0.8773150661668782,
820 0.9087084512521527,
821 0.9464431041493069,
822 0.9938060179447082,
823 1.054557457023869,
824 1.1249976100247374,
825 1.199865521544602,
826 1.2737985030102073,
827 1.3436229324321316,
828 1.4106131175610293,
829 1.47365203587191,
830 1.530351119602024,
831 1.5832977086243996,
832 1.6333441280837124,
833 1.6832582510569378,
834 1.7331639399795236,
835 1.7828201268627029,
836 1.8322863129013094,
837 1.8800526846196015,
838 1.9286762638050863,
839 1.9832437306954993,
840 2.0422080162708522,
841 2.1022504858694147,
842 2.163628155127377,
843 2.2235596908381847,
844 2.268879067727309,
845 2.3095696049217467,
846 2.3490127808421275,
847 2.387720572510347,
848 2.4257236700994116,
849 2.462893961270545,
850 2.4991492682583005,
851 2.534496788787009,
852 2.568912499320822,
853 2.6022119100174876,
854 2.634401344550984,
855 ];
856 const S_A: [f64; 60] = [
857 -0.17020102326156605,
858 1.726556436052315,
859 3.5192952736717285,
860 2.801246475765134,
861 2.916301498311998,
862 1.8679901171016524,
863 0.385417573060896,
864 -2.4616280221274254,
865 -2.1589307740556993,
866 -2.624717947693237,
867 -2.9033791431503775,
868 -2.569979272275229,
869 -0.3949369581430479,
870 1.747341176718214,
871 3.1565219997842857,
872 2.96737239548915,
873 2.9255075319079604,
874 1.8498863004764663,
875 0.4439005079073706,
876 -2.0819707336913336,
877 -2.5733476274600338,
878 -2.8396345182460423,
879 -2.716653100846297,
880 -2.232532099802241,
881 -0.5813597218343396,
882 1.7742640717146578,
883 2.846471756328533,
884 3.1091422937578663,
885 2.977866441638085,
886 1.8125842519758864,
887 0.44065198659717053,
888 -1.7333062411783062,
889 -2.877358310844143,
890 -3.1228573040388334,
891 -2.5814859038818416,
892 -1.923662331769904,
893 -0.6589242066436268,
894 1.654448134865381,
895 2.7902213581215958,
896 3.196646615959359,
897 3.378743148206909,
898 1.6664508812510805,
899 0.03202021680374002,
900 -1.5119953659601402,
901 -2.6801711348329444,
902 -3.4754184948832547,
903 -2.53811618842274,
904 -1.8778570095140745,
905 -0.6882272489291716,
906 1.5713092637842123,
907 2.769808959128488,
908 3.291131276516374,
909 3.8327348075558714,
910 1.5116377387724362,
911 -0.4415786081405177,
912 -1.3117640740058498,
913 -2.3837945654928134,
914 -3.8267872984984574,
915 -2.570245955232843,
916 -1.8449407343136188,
917 ];
918 const W_A: [f64; 60] = [
919 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
920 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
921 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
922 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
923 ];
924
925 const Y_B: [f64; 61] = [
927 -0.025, 1.069, 3.35, 3.942, -1.766, -2.181, -2.153, 0.516, 2.39, 3.566, 2.573, -0.231,
928 -2.106, -0.692, 1.693, 3.686, 4.933, 2.177, -0.175, -1.992, 22.633, 0.344, 3.065, 3.292,
929 1.623, 1.014, -1.099, -1.114, 0.943, 3.337, 4.413, 2.255, 0.203, 0.065, -1.017, 2.144,
930 5.494, 3.122, 2.931, 1.845, -18.96, 0.318, 1.661, 5.137, 6.434, 3.989, 0.746, -0.527,
931 -0.577, 2.835, 4.683, 6.083, 3.598, 2.246, -0.686, 0.157, 2.89, 4.609, 6.722, 4.243, 0.62,
932 ];
933 const T_B: [f64; 61] = [
934 -0.3442510947669004,
935 -0.23062424435746,
936 -0.11685262949421589,
937 -0.002469718500161184,
938 0.11274093104960016,
939 0.2289590590691011,
940 0.34669621930681893,
941 0.4641410268250529,
942 0.5918511164309425,
943 0.7336052455122047,
944 0.884323396730363,
945 1.0360398044200754,
946 1.1636505625530387,
947 1.245837797738338,
948 1.286580011423615,
949 1.290432042200402,
950 1.2442565697083043,
951 1.164855851233538,
952 1.063971152139175,
953 0.9472392292523064,
954 0.8329130621863098,
955 0.7639822496461729,
956 0.7514682659961501,
957 0.771524888489271,
958 0.8125694910960958,
959 0.8849167375990827,
960 0.9743782676420534,
961 1.0741244472560494,
962 1.1874184134193109,
963 1.3058037914844787,
964 1.424472836211481,
965 1.5601850416849212,
966 1.7151903867377953,
967 1.8770424109280648,
968 2.044406635246177,
969 2.2091025127247335,
970 2.3597781235451203,
971 2.4870176685979795,
972 2.580461292190404,
973 2.6573004568075813,
974 2.7290947382698336,
975 2.768976483362874,
976 2.775035332744085,
977 2.7573688301410217,
978 2.7140131730950867,
979 2.645656622357796,
980 2.58230075551713,
981 2.524365699956627,
982 2.4667235014472038,
983 2.4239646079783688,
984 2.4138328106340237,
985 2.428357847754513,
986 2.453585556625788,
987 2.4814970344726954,
988 2.513165475314854,
989 2.5480943246238934,
990 2.5842123463425395,
991 2.620406502455848,
992 2.6563542843093986,
993 2.6922064934988774,
994 2.728112204519468,
995 ];
996 const S_B: [f64; 61] = [
997 0.2806980187795315,
998 1.4424643683777076,
999 3.309859801264952,
1000 1.8893345562968902,
1001 -1.66195680288314,
1002 -2.9614845621636157,
1003 -2.2475741103014277,
1004 0.15409820365643145,
1005 1.7409003514457582,
1006 3.1842055432617626,
1007 1.5373450003572735,
1008 -1.5168859483966926,
1009 -2.7663839335106224,
1010 -2.28021361493963,
1011 0.02868900722678074,
1012 2.0190783320351633,
1013 3.055816690497411,
1014 1.209301757094185,
1015 -1.392748079058499,
1016 -2.557150028631557,
1017 -2.3283217690321734,
1018 -0.08277082758805875,
1019 2.26725469072666,
1020 2.957666606262043,
1021 0.8714716745433282,
1022 -1.3087917264876636,
1023 -2.257299363938695,
1024 -2.3825384207403197,
1025 -0.20064860020793335,
1026 2.3923190059595694,
1027 3.049359773786675,
1028 0.7423574645215033,
1029 -1.3291972803458882,
1030 -2.27500508802811,
1031 -2.5646136176361476,
1032 -0.11024987307195383,
1033 2.408543693561008,
1034 3.283056252051252,
1035 0.8446694985351011,
1036 -1.4320403635697274,
1037 -2.602957908650004,
1038 -2.63457485800498,
1039 0.06978123588918689,
1040 2.338058755805587,
1041 3.537084619549874,
1042 1.0453567720871335,
1043 -1.6473847818099971,
1044 -2.8964683776519093,
1045 -2.637053599801231,
1046 0.22227977439498894,
1047 2.2364162812135167,
1048 3.7987107994003124,
1049 1.2751337860004317,
1050 -1.877933876664946,
1051 -3.218492433488227,
1052 -2.620328624516707,
1053 0.3863930871033018,
1054 2.1108508097586216,
1055 4.051933593412693,
1056 1.5187065616313047,
1057 -2.114415902711208,
1058 ];
1059 const W_B: [f64; 61] = [
1060 0.9997292269068923,
1061 0.9695652397933096,
1062 0.9809542316008585,
1063 0.0,
1064 0.9423651229627944,
1065 0.734405988773694,
1066 0.9232852137137939,
1067 0.9917886807301683,
1068 0.9940206965090626,
1069 0.8889394114902743,
1070 0.9971333240611042,
1071 0.9303323027332928,
1072 0.760428447475915,
1073 0.86637004183294,
1074 0.8339724328930439,
1075 0.8424500018058209,
1076 0.6052806385563598,
1077 0.9432511306815423,
1078 0.9808039178471991,
1079 0.8567328788380366,
1080 0.0,
1081 0.8849392676251042,
1082 0.9985147579627649,
1083 0.7986949677342619,
1084 0.9943515347120641,
1085 0.0,
1086 0.9664450636558382,
1087 0.9614963970696023,
1088 0.9989195146322842,
1089 0.852399171466217,
1090 0.9951604693611221,
1091 0.9977196196085271,
1092 0.9408384687736601,
1093 0.7908358409889987,
1094 0.7534909697893347,
1095 0.9901271716333155,
1096 0.5527536287838176,
1097 0.0,
1098 0.7648624635843374,
1099 0.6529578612993352,
1100 0.0,
1101 0.95046361562721,
1102 0.12304637167915942,
1103 0.9961449380002448,
1104 0.9534801123689413,
1105 0.8917773805691143,
1106 0.9607518121979802,
1107 0.9769105086278805,
1108 0.8343505718889977,
1109 0.945216049382716,
1110 0.999470548492894,
1111 0.9760149956852816,
1112 0.9807071000485312,
1113 0.0,
1114 0.9997986989258324,
1115 0.9469564672618057,
1116 0.9967764725460029,
1117 0.9830052938078254,
1118 0.9999124350801569,
1119 0.9991755348009493,
1120 0.9999553771693069,
1121 ];
1122
1123 const Y_C: [f64; 50] = [
1125 2.899, 1.404, -1.09, -1.191, 1.22, 1.92, 1.528, -1.227, -1.219, 0.985, 4.068, 3.003, 0.249,
1126 0.273, 2.171, 1.173, 0.425, -0.886, -2.126, -0.761, 1.133, 0.238, -0.288, 1.395, 3.725,
1127 4.073, 1.126, -2.636, -4.191, -1.998, -1.086, -2.35, -4.801, -6.16, -4.845, -3.834, -4.53,
1128 -4.438, -4.914, -4.304, -2.863, -3.678, -5.134, -4.719, -2.169, -1.517, -2.931, -4.141,
1129 -4.018, -1.645,
1130 ];
1131 const T_C: [f64; 50] = [
1132 0.3595203697419831,
1133 0.4133930642182823,
1134 0.4747768840530127,
1135 0.5435546680555853,
1136 0.619198114753108,
1137 0.7006157218307316,
1138 0.7857221424193703,
1139 0.8726106252710931,
1140 0.9722872255626283,
1141 1.0672531388379005,
1142 1.1095123655999835,
1143 1.1087515830857375,
1144 1.062241411605581,
1145 0.9564646591506943,
1146 0.7940533028867663,
1147 0.5869080747761846,
1148 0.37508509647620825,
1149 0.23031218932473474,
1150 0.21030891092760245,
1151 0.3074701544826551,
1152 0.46153339094123064,
1153 0.5941559397386557,
1154 0.6383212264487284,
1155 0.5719836910961416,
1156 0.42021275781920436,
1157 0.19694515652981875,
1158 -0.1280104380675565,
1159 -0.5930481811158689,
1160 -1.2029650206895672,
1161 -1.9105559538321204,
1162 -2.6131289498929537,
1163 -3.1972277710202404,
1164 -3.6137910639406168,
1165 -3.8913542357283624,
1166 -4.084695546244734,
1167 -4.232297509050723,
1168 -4.336838455231511,
1169 -4.3695194142421405,
1170 -4.307424797070813,
1171 -4.163872109579644,
1172 -3.9851002160601596,
1173 -3.815136335577719,
1174 -3.665168545308158,
1175 -3.5115010353483163,
1176 -3.352547491530393,
1177 -3.189082529735992,
1178 -3.022825774562644,
1179 -2.8554492683085404,
1180 -2.687940406880207,
1181 -2.520467223905384,
1182 ];
1183 const S_C: [f64; 50] = [
1184 1.9811770199303513,
1185 0.9964325672477123,
1186 -1.6639496911020202,
1187 -1.7074363882518788,
1188 0.48439109227970223,
1189 1.8971925211052052,
1190 0.8767641970404536,
1191 -1.5650572383620416,
1192 -1.6403984718625118,
1193 0.5075879485704992,
1194 1.8265999571068101,
1195 0.7716356997912741,
1196 -1.4679414387841518,
1197 -1.5869990529831526,
1198 0.5123046197969641,
1199 1.7642672260377779,
1200 0.6856967005677134,
1201 -1.361321906507085,
1202 -1.5368582227494896,
1203 0.4993615211265455,
1204 1.6909560314383185,
1205 0.5994208438145355,
1206 -1.2267892658829993,
1207 -1.468209067376339,
1208 0.4848383868349154,
1209 1.5872641684260307,
1210 0.4792404577037621,
1211 -1.1167226637194017,
1212 -1.3821272181795852,
1213 0.4995775453353193,
1214 1.540497098999799,
1215 0.3958723226446683,
1216 -1.0968850533070467,
1217 -1.33581145863842,
1218 0.5583095598817691,
1219 1.50199003911766,
1220 0.32930446900491595,
1221 -1.0679208315418778,
1222 -1.300179407712935,
1223 0.5912549540997699,
1224 1.4462761765144987,
1225 0.2639949024928305,
1226 -1.0226430045502393,
1227 -1.2463805712810467,
1228 0.624199874857744,
1229 1.3748658333196466,
1230 0.18963002569731405,
1231 -0.9811266241591107,
1232 -1.1788791887612886,
1233 0.6766480651519423,
1234 ];
1235 const W_C: [f64; 50] = [
1236 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1237 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1238 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1239 ];
1240
1241 const Y_D: [f64; 37] = [
1243 0.39, 1.758, 0.311, -3.652, -0.216, 1.701, 0.579, -2.206, 0.144, 3.245, 1.514, -3.3, 0.043,
1244 3.213, -0.027, -0.615, 0.923, 2.396, 0.684, -2.364, 1.723, 4.237, 1.198, -1.383, 0.675,
1245 4.491, 1.556, -1.63, -0.445, 5.039, 1.742, -1.983, 1.801, 3.564, 2.798, -1.145, 2.008,
1246 ];
1247 const T_D: [f64; 37] = [
1248 -0.29061594267302715,
1249 -0.2973927741153516,
1250 -0.3073351786431117,
1251 -0.33505220397377183,
1252 -0.3528489557335598,
1253 -0.19649320522216404,
1254 0.0015922709096244866,
1255 0.2563307771874918,
1256 0.46800059701173313,
1257 0.4614461082021752,
1258 0.39318706163134826,
1259 0.3623442483985528,
1260 0.31390915172430783,
1261 0.43178562855612257,
1262 0.6346582301858984,
1263 0.6933269615676654,
1264 0.6858712436708,
1265 0.6810799527108491,
1266 0.6961762130679717,
1267 0.8353162435221444,
1268 1.0504968974462212,
1269 1.2171926624324578,
1270 1.3034891280694905,
1271 1.3002424793726404,
1272 1.284409768450904,
1273 1.232090942519993,
1274 1.1317427109376321,
1275 1.144457822901661,
1276 1.1614598274190113,
1277 1.182347001119035,
1278 1.3048667475329754,
1279 1.4304187646720594,
1280 1.5053123019505208,
1281 1.6567582985022575,
1282 1.727099183257099,
1283 1.7613622559962399,
1284 1.7873551899593902,
1285 ];
1286 const S_D: [f64; 37] = [
1287 0.11612195647986145,
1288 2.286570671419279,
1289 0.43281480402315803,
1290 -2.823946636684542,
1291 0.10299028357493027,
1292 2.3068525711905274,
1293 0.3950957416846182,
1294 -2.7864712784457293,
1295 0.07385800252794973,
1296 2.342457241902704,
1297 0.33444143049657177,
1298 -2.71854857507006,
1299 -0.003007898777023888,
1300 2.456249404673185,
1301 0.1469183033101349,
1302 -2.6506653723197506,
1303 0.0741383971341427,
1304 2.581125321312145,
1305 -0.04872975464060914,
1306 -2.590364519446663,
1307 -0.09283995102014814,
1308 2.811044525732896,
1309 0.0684953240208349,
1310 -2.750899327511905,
1311 -0.33664736335837303,
1312 2.896990376996522,
1313 0.30837041696305617,
1314 -2.9085954324631014,
1315 -0.28606373645371885,
1316 2.858851132982773,
1317 0.3834687089169155,
1318 -2.9563709321899485,
1319 -0.2944567709915975,
1320 2.860919506781294,
1321 0.4185738278504357,
1322 -2.985719585141617,
1323 -0.2970207951936875,
1324 ];
1325 const W_D: [f64; 37] = [
1326 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1327 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1328 1.0,
1329 ];
1330
1331 const Y_E: [f64; 72] = [
1333 4.042, 5.551, 4.218, 3.099, 4.548, 7.424, 9.549, 8.295, 7.601, 3.885, 4.435, 6.254, 6.491,
1334 5.891, 2.247, 0.91, 2.648, 6.619, 7.529, 4.301, 1.437, 0.36, 3.008, 5.856, 5.425, 4.295,
1335 2.234, 2.376, 4.021, 5.98, 8.462, 6.309, 4.615, 4.104, 7.024, 9.063, 9.708, 10.862, 10.036,
1336 9.95, 11.243, 12.526, 13.671, 11.613, 6.923, 5.188, 8.301, 11.248, 12.66, 12.045, 9.566,
1337 7.377, 7.505, 9.268, 10.486, 10.731, 7.876, 8.43, 9.713, 10.987, 12.515, 11.422, 8.938,
1338 9.57, 10.861, 13.347, 14.964, 12.097, 10.042, 8.738, 10.763, 13.8,
1339 ];
1340 const T_E: [f64; 72] = [
1341 3.339306089958577,
1342 3.936040801996553,
1343 4.513634527373273,
1344 5.075725970051677,
1345 5.614832651267695,
1346 6.0683623780139815,
1347 6.181311572026499,
1348 6.153886286287091,
1349 6.070148485580003,
1350 5.898473948102176,
1351 5.541241355166631,
1352 5.111083944943771,
1353 4.66813222273837,
1354 4.288545143975496,
1355 4.034269325238091,
1356 3.890909374187796,
1357 3.73906765585241,
1358 3.560939709361493,
1359 3.459624331809137,
1360 3.4403711504012495,
1361 3.3946846794457546,
1362 3.34982867709257,
1363 3.3939078100554814,
1364 3.5084558197784657,
1365 3.700409001638404,
1366 3.8968127769838627,
1367 4.072908914767636,
1368 4.341796781310583,
1369 4.702158733733236,
1370 5.058445212925836,
1371 5.401853515859843,
1372 5.806971010092806,
1373 6.268468654449328,
1374 6.756668732663874,
1375 7.360134551973213,
1376 8.13906742863687,
1377 9.002951248471529,
1378 9.818516724457417,
1379 10.506075819602279,
1380 11.063411373712176,
1381 11.39344614357849,
1382 11.465363428807176,
1383 11.220689137195905,
1384 10.853423936605795,
1385 10.644705192861233,
1386 10.486584943448353,
1387 10.331462836588795,
1388 10.227850324730847,
1389 10.28987472663667,
1390 10.14422469058004,
1391 9.847429251258013,
1392 9.51545630322874,
1393 9.240702219063252,
1394 9.059832640212925,
1395 9.074539040286753,
1396 9.279917912435625,
1397 9.571662000982107,
1398 9.84376783217375,
1399 10.059888991860799,
1400 10.236556699656727,
1401 10.434106025265493,
1402 10.656183133857752,
1403 10.937951965978689,
1404 11.261390761658166,
1405 11.520065205611745,
1406 11.684189475376927,
1407 11.737357833534594,
1408 11.708505384958219,
1409 11.704033954115422,
1410 11.693595803020095,
1411 11.677894141351029,
1412 11.664305406011493,
1413 ];
1414 const S_E: [f64; 72] = [
1415 1.1126902137899433,
1416 1.8379682033882991,
1417 -0.927071577446163,
1418 -2.3238633529327313,
1419 -1.2284197104748877,
1420 1.5359778414382725,
1421 1.4047229533612233,
1422 1.595614520920293,
1423 -1.1454993343666757,
1424 -2.3518690349787548,
1425 -1.0445088219928764,
1426 1.5379476124877398,
1427 1.6769094513632663,
1428 1.362520392555553,
1429 -1.3345685339187126,
1430 -2.3565301285315505,
1431 -0.8721897999797636,
1432 1.5089071688733984,
1433 1.9441842262045688,
1434 1.1480801878829157,
1435 -1.5183913666279765,
1436 -2.3466488367937175,
1437 -0.7123780044790777,
1438 1.4804547629483278,
1439 2.136443552326483,
1440 0.894099525883592,
1441 -1.5957807491918263,
1442 -2.3225593071949175,
1443 -0.5925688623681842,
1444 1.447935936177117,
1445 2.117685328503887,
1446 0.7537995877402939,
1447 -1.3190812555563058,
1448 -2.128220934647446,
1449 -0.7219658188317613,
1450 1.1528277157911888,
1451 2.1215467318319248,
1452 0.8568272917558482,
1453 -1.1341572582869293,
1454 -2.010951974927005,
1455 -0.8899197129330984,
1456 0.8950277113474407,
1457 2.077046539284205,
1458 1.0388518177054709,
1459 -1.1293932849701909,
1460 -1.8717932049623573,
1461 -0.9320068913964269,
1462 0.8334259895662277,
1463 2.1350820675942908,
1464 1.007931965545156,
1465 -1.3013537516608538,
1466 -1.9021995095323365,
1467 -0.9084178171162189,
1468 1.0310998679169345,
1469 2.2578451026966886,
1470 0.943348384895613,
1471 -1.4520157504566429,
1472 -2.018746437089826,
1473 -0.916577749317464,
1474 1.2427566354732489,
1475 2.3936208032606547,
1476 0.8754973995565433,
1477 -1.6296243983051377,
1478 -2.1453100006550945,
1479 -0.8896514111482849,
1480 1.4675882585751068,
1481 2.546493075368808,
1482 0.7809012506411024,
1483 -1.835016183294741,
1484 -2.2928259604857235,
1485 -0.835198338818193,
1486 1.7229189005107237,
1487 ];
1488 const W_E: [f64; 72] = [
1489 0.9401165139649331,
1490 0.9876068156092807,
1491 0.876950773282701,
1492 0.9612801236722237,
1493 0.9919584851011588,
1494 0.9881418586247281,
1495 0.20743174403561665,
1496 0.9170418564428926,
1497 0.0,
1498 0.9644944849112693,
1499 0.9992069563405819,
1500 0.953852508099292,
1501 0.9968581477424536,
1502 0.9812457709849693,
1503 0.9376624898765162,
1504 0.8797447877694339,
1505 0.9828637757719135,
1506 0.4144038623780093,
1507 0.12027826338340111,
1508 0.9708344475119918,
1509 0.9361147893912269,
1510 0.8740040807275263,
1511 0.9667270170094414,
1512 0.7833753726323832,
1513 0.9423509675665717,
1514 0.9273683194305313,
1515 0.9826285464756584,
1516 0.9604072839051773,
1517 0.997720180476704,
1518 0.9148685738764122,
1519 0.7529768396354195,
1520 0.9808597866668539,
1521 0.9660631941661675,
1522 0.9172071367009891,
1523 0.9538031560745991,
1524 0.9840571222159182,
1525 0.4771059260415243,
1526 0.9890107263787425,
1527 0.8683971207372099,
1528 0.7673469315935755,
1529 0.838306888028481,
1530 0.9916911211086208,
1531 0.9570748598884854,
1532 0.9759546419486816,
1533 0.0,
1534 0.0,
1535 0.665610573243546,
1536 0.9890288666540932,
1537 0.982736338927162,
1538 0.7703251192512738,
1539 0.7070474163811469,
1540 0.9826181433631739,
1541 0.8012852544333962,
1542 0.8027683208231016,
1543 0.7922314772664304,
1544 0.9223648278373678,
1545 0.9815822781413498,
1546 0.890377388772185,
1547 0.9020001884367043,
1548 0.9268013694133308,
1549 0.9700890121951367,
1550 0.9962159725017129,
1551 0.957911345947805,
1552 0.9374306237042292,
1553 0.9835909068631806,
1554 0.9882631495511387,
1555 0.8626900372898038,
1556 0.9529699167079625,
1557 0.9908436728118781,
1558 0.8695666651620589,
1559 0.9980522765504592,
1560 0.9480112199625286,
1561 ];
1562
1563 const Y_F: [f64; 45] = [
1565 1.056, 3.197, 3.911, -0.245, -4.601, -2.297, 1.244, 1.754, 2.532, 0.779, -3.543, -1.519,
1566 1.139, 3.565, 3.235, 25.844, -2.516, -2.279, 1.154, 4.023, 4.291, 1.902, -2.419, -3.06,
1567 1.374, 4.344, 4.052, 2.651, -1.69, -1.157, -15.845, 3.859, 4.079, 0.858, -1.596, -0.349,
1568 1.416, 4.74, 4.014, 1.605, 0.355, -0.696, 3.752, 4.817, 4.537,
1569 ];
1570 const T_F: [f64; 45] = [
1571 0.5612109998433703,
1572 0.40904308280921975,
1573 0.3063556110383589,
1574 0.22385919710585547,
1575 0.09288356206653685,
1576 -0.06107135622732654,
1577 -0.10624822414054202,
1578 0.014656325116133434,
1579 0.08007451660115499,
1580 0.17480881836228845,
1581 0.34546558881282635,
1582 1.0492469568824512,
1583 2.732084319389148,
1584 4.266955192867556,
1585 4.889599645363789,
1586 4.967180414397434,
1587 4.925277894417086,
1588 4.40743780439494,
1589 3.0939602933044923,
1590 1.6068942617149302,
1591 1.0262740696598924,
1592 0.9947585154941324,
1593 1.0073511748106747,
1594 1.0672624282725802,
1595 1.1447399240254628,
1596 1.2797580965614856,
1597 1.0521677462295975,
1598 0.08610739958879997,
1599 -0.9072612628388329,
1600 -1.3994989371162496,
1601 -1.5683716226276025,
1602 -1.589653319134234,
1603 -1.164231654289145,
1604 -0.0933757185688997,
1605 1.0720090219362588,
1606 1.534751267723855,
1607 1.6603674495583198,
1608 1.8085935594848528,
1609 1.910476451507037,
1610 2.0833589830238215,
1611 2.3070527257477664,
1612 2.4249224569681234,
1613 2.586710395846874,
1614 2.7919327883617777,
1615 2.9968909089345734,
1616 ];
1617 const S_F: [f64; 45] = [
1618 -0.08345334568748934,
1619 1.6366687579701773,
1620 2.2622933452398972,
1621 5.431115109096656,
1622 -5.3922930507010385,
1623 -3.505289669825159,
1624 -0.6191111992026053,
1625 1.8810402778161728,
1626 2.3220436132227444,
1627 4.834300782291442,
1628 -4.774952916421866,
1629 -3.3041102488029876,
1630 -1.1313723504797544,
1631 2.122313273146454,
1632 2.378702967592607,
1633 4.219940286112908,
1634 -4.157014623089618,
1635 -3.102247585149322,
1636 -1.623120106864433,
1637 2.3587764651119216,
1638 2.4301355561852147,
1639 3.5983008476292553,
1640 -3.5404359142344077,
1641 -2.902168758924804,
1642 -2.0928761240257416,
1643 2.5935060023538785,
1644 2.480730507566989,
1645 2.9521352217578634,
1646 -2.9210860698571364,
1647 -2.699118331152398,
1648 -2.5475910193111697,
1649 2.830743247941963,
1650 2.534882383818799,
1651 2.2798838568181043,
1652 -2.2968557478495795,
1653 -2.4888933119395698,
1654 -2.997565358026707,
1655 3.073266962087194,
1656 2.5949563104032625,
1657 1.5759042735441489,
1658 -1.667681434069707,
1659 -2.270656579310069,
1660 -3.434954163903007,
1661 3.3192723414156355,
1662 2.6589654682730384,
1663 ];
1664 const W_F: [f64; 45] = [
1665 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1666 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1667 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1668 ];
1669
1670 fn check(y: &[f64], t: &[f64], s: &[f64], w: &[f64], stl: Stl) {
1671 let r = stl.decompose(y).unwrap();
1672 let scale = y.iter().fold(0.0_f64, |m, v| m.max(v.abs()));
1673 let d = |a: &[f64], b: &[f64]| {
1674 a.iter()
1675 .zip(b)
1676 .fold(0.0_f64, |m, (x, y)| m.max((x - y).abs()))
1677 };
1678 let (dt, ds, dw) = (d(&r.trend, t), d(&r.seasonal, s), d(&r.weights, w));
1679 assert!(dt <= 1e-10 * scale && ds <= 1e-10 * scale && dw <= 1e-10);
1680 }
1681
1682 #[test]
1683 fn matches_statsmodels() {
1684 check(&Y_A, &T_A, &S_A, &W_A, Stl::new(12));
1685 }
1686 #[test]
1687 fn matches_statsmodels_b() {
1688 check(&Y_B, &T_B, &S_B, &W_B, Stl::new(7).robust(true));
1689 check(
1690 &Y_C,
1691 &T_C,
1692 &S_C,
1693 &W_C,
1694 Stl::new(5).seasonal(11).trend(17).low_pass(7),
1695 );
1696 }
1697 #[test]
1698 fn matches_statsmodels_d() {
1699 check(
1700 &Y_D,
1701 &T_D,
1702 &S_D,
1703 &W_D,
1704 Stl::new(4)
1705 .seasonal_degree(0)
1706 .trend_degree(0)
1707 .low_pass_degree(0),
1708 );
1709 }
1710 #[test]
1711 fn matches_statsmodels_e() {
1712 check(&Y_E, &T_E, &S_E, &W_E, Stl::new(6).robust(true).seasonal(9));
1713 }
1714 #[test]
1715 fn matches_statsmodels_f() {
1716 check(&Y_F, &T_F, &S_F, &W_F, Stl::new(6).seasonal(35));
1717 }
1718}