use super::*;
#[test]
fn ht_core_short_input_returns_warmup_only() {
for n in [0usize, 1, CORE_START, CORE_START - 1] {
let price: Vec<f64> = (0..n).map(|i| 100.0 + i as f64).collect();
let bars = ht_core(&price);
assert_eq!(bars.len(), n);
assert!(bars.iter().all(|b| b.smooth_period == 0.0 && b.q1 == 0.0));
}
}
#[test]
fn dcphase_zero_imaginary_carry_branch() {
assert_eq!(
(TWO_PI / 2.0).cos(),
-1.0,
"cos(π) must be exactly -1 for the cancellation"
);
let mut up = DcPhase::new();
let _ = up.push(5.0, 2.0); let p_up = up.push(5.0, 2.0); assert!(p_up.is_finite());
let mut down = DcPhase::new();
let _ = down.push(-5.0, 2.0);
let p_down = down.push(-5.0, 2.0); assert!(p_down.is_finite());
let p_flat = up.push(5.0, 0.4); assert!(p_flat.is_finite());
}
fn series(n: usize) -> Vec<f64> {
(0..n)
.map(|i| {
let t = i as f64;
100.0 + 0.05 * t + 6.0 * (t * 0.30).sin() + 2.0 * (t * 0.11).cos()
})
.collect()
}
fn assert_bits(a: &[f64], b: &[f64], what: &str) {
assert_eq!(a.len(), b.len(), "{what}: length");
for (i, (x, y)) in a.iter().zip(b).enumerate() {
assert!(
x.to_bits() == y.to_bits() || (x.is_nan() && y.is_nan()),
"{what}: bar {i}: resume {x:?} != full {y:?}",
);
}
}
fn assert_near(a: &[f64], b: &[f64], what: &str) {
assert_eq!(a.len(), b.len(), "{what}: length");
for (i, (x, y)) in a.iter().zip(b).enumerate() {
let ok = (x.is_nan() && y.is_nan()) || (x - y).abs() <= 1e-9 + 1e-9 * y.abs();
assert!(ok, "{what}: bar {i}: resume {x:?} !~= full {y:?}");
}
}
#[test]
fn ht_resume_is_bit_identical_to_full() {
let price = series(250);
for &from in &[64usize, 65, 100, 200, 249] {
let head = &price[..from];
let st = ht_core_state(head).unwrap();
let (v, _) = ht_dcperiod_resume(&price, from, &st).unwrap();
assert_bits(&v, &ht_dcperiod(&price)[from..], "dcperiod");
let (v, _) = ht_phasor_resume(&price, false, from, &st).unwrap();
assert_bits(&v, &ht_phasor(&price).0[from..], "phasor.inphase");
let (v, _) = ht_phasor_resume(&price, true, from, &st).unwrap();
assert_bits(&v, &ht_phasor(&price).1[from..], "phasor.quadrature");
let st = ht_dcphase_state(head).unwrap();
let (v, _) = ht_dcphase_resume(&price, from, &st).unwrap();
assert_bits(&v, &ht_dcphase(&price)[from..], "dcphase");
let st = ht_sine_state(head).unwrap();
let (v, _) = ht_sine_resume(&price, false, from, &st).unwrap();
assert_bits(&v, &ht_sine(&price).0[from..], "sine.sine");
let (v, _) = ht_sine_resume(&price, true, from, &st).unwrap();
assert_bits(&v, &ht_sine(&price).1[from..], "sine.leadsine");
let st = ht_trendline_state(head).unwrap();
let (v, _) = ht_trendline_resume(&price, from, &st).unwrap();
assert_near(&v, &ht_trendline(&price)[from..], "trendline");
let st = ht_trendmode_state(head).unwrap();
let (v, _) = ht_trendmode_resume(&price, from, &st).unwrap();
assert_near(&v, &ht_trendmode(&price)[from..], "trendmode");
let st = mama_state(head, 0.5, 0.05).unwrap();
let (v, _) = mama_resume(&price, 0.5, 0.05, false, from, &st).unwrap();
assert_bits(&v, &mama(&price, 0.5, 0.05).0[from..], "mama");
let (v, _) = mama_resume(&price, 0.5, 0.05, true, from, &st).unwrap();
assert_bits(&v, &mama(&price, 0.5, 0.05).1[from..], "mama.fama");
}
}
#[test]
fn ht_resume_chains_bit_identical() {
let price = series(160);
let full = ht_dcphase(&price);
let mut state = ht_dcphase_state(&price[..150]).unwrap();
let mut chained = Vec::with_capacity(price.len() - 150);
for from in 150..price.len() {
let (v, st) = ht_dcphase_resume(&price[..from + 1], from, &state).unwrap();
assert_eq!(v.len(), 1);
chained.push(v[0]);
state = st;
}
assert_bits(&chained, &full[150..], "chained dcphase");
}
#[test]
fn ht_resume_declines_below_warmup() {
let price = series(120);
let st = ht_core_state(&price).unwrap();
assert!(ht_dcperiod_resume(&price, CORE_START, &st).is_none());
assert!(ht_phasor_resume(&price, false, CORE_START, &st).is_none());
let st = ht_dcphase_state(&price).unwrap();
assert!(ht_dcphase_resume(&price, CORE_START, &st).is_none());
assert!(ht_sine_resume(&price, true, CORE_START, &st).is_none());
let st = ht_trendline_state(&price).unwrap();
assert!(ht_trendline_resume(&price, SMOOTH_PRICE_SIZE - 1, &st).is_none());
let st = ht_trendmode_state(&price).unwrap();
assert!(ht_trendmode_resume(&price, SMOOTH_PRICE_SIZE - 1, &st).is_none());
let tiny = series(CORE_START);
assert!(ht_core_state(&tiny).is_none());
assert!(ht_dcphase_state(&tiny).is_none());
assert!(ht_trendline_state(&tiny).is_none());
assert!(ht_trendmode_state(&tiny).is_none());
assert!(mama_state(&tiny, 0.5, 0.05).is_none());
}
#[test]
fn ht_resume_declines_on_short_state() {
let price = series(120);
let stub = vec![0.0; CORE_STATE_LEN - 1];
assert!(ht_dcperiod_resume(&price, 20, &stub).is_none());
let core_only = vec![0.0; CORE_STATE_LEN];
assert!(ht_dcphase_resume(&price, 20, &core_only).is_none());
assert!(ht_sine_resume(&price, false, 20, &core_only).is_none());
assert!(ht_trendline_resume(&price, SMOOTH_PRICE_SIZE, &core_only).is_none());
assert!(ht_trendmode_resume(&price, SMOOTH_PRICE_SIZE, &core_only).is_none());
assert!(mama_resume(&price, 0.5, 0.05, false, 20, &core_only).is_none());
}