#![cfg(feature = "parallel")]
use adaq_talib::math_ops::{minmax_index_parallel, minmax_index_serial, minmax_parallel, minmax_serial};
use adaq_talib::momentum::{stoch_f_parallel, stoch_f_serial, willr_parallel, willr_serial};
use adaq_talib::overlap::{midpoint_parallel, midpoint_serial};
fn lcg_input(n: usize, seed: u64) -> Vec<f64> {
let mut x = seed;
(0..n)
.map(|_| {
x = x
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
f64::from_bits((x >> 11) | 0x3FF0000000000000) - 1.0
})
.collect()
}
fn assert_eq_1to1(name: &str, period: usize, a: &[f64], b: &[f64]) {
assert_eq!(a.len(), b.len(), "{name}: length mismatch");
for (i, (x, y)) in a.iter().zip(b).enumerate() {
if x.is_nan() && y.is_nan() {
continue; }
assert!(
!(x.is_nan() ^ y.is_nan()),
"{name} period={period} idx={i}: one side NaN (serial={x}, parallel={y})"
);
let tol = 1e-8 * x.abs().max(y.abs()) + 1e-10;
assert!(
(x - y).abs() <= tol,
"{name} period={period} idx={i}: serial={x} parallel={y} (diff={})",
(x - y).abs()
);
}
}
#[test]
fn midpoint_parallel_matches_serial() {
let data = lcg_input(50_000, 0x2545F491_4F6CDD1D);
for period in [1usize, 2, 3, 5, 10, 14, 30, 64] {
let serial = midpoint_serial(&data, period).unwrap();
let parallel = midpoint_parallel(&data, period).unwrap();
assert_eq_1to1("midpoint", period, &serial, ¶llel);
}
}
#[test]
fn midpoint_parallel_small_input_is_serial() {
let data = lcg_input(1024, 0x2545F491_4F6CDD1D);
let serial = midpoint_serial(&data, 5).unwrap();
let parallel = midpoint_parallel(&data, 5).unwrap();
assert_eq_1to1("midpoint", 5, &serial, ¶llel);
}
#[test]
fn minmax_parallel_matches_serial() {
let data = lcg_input(50_000, 0x9E3779B97F4A7C15);
for period in [1usize, 2, 3, 5, 10, 14, 30, 64] {
let s = minmax_serial(&data, period).unwrap();
let p = minmax_parallel(&data, period).unwrap();
assert_eq_1to1("minmax.min", period, &s.min, &p.min);
assert_eq_1to1("minmax.max", period, &s.max, &p.max);
}
}
#[test]
fn minmax_index_parallel_matches_serial() {
let data = lcg_input(50_000, 0xBF58476D1CE4E5B9);
for period in [1usize, 2, 3, 5, 10, 14, 30, 64] {
let s = minmax_index_serial(&data, period).unwrap();
let p = minmax_index_parallel(&data, period).unwrap();
assert_eq_1to1("minmax_index.min_idx", period, &s.min_idx, &p.min_idx);
assert_eq_1to1("minmax_index.max_idx", period, &s.max_idx, &p.max_idx);
}
}
#[test]
fn willr_parallel_matches_serial() {
let high = lcg_input(50_000, 0xC2B2AE3D27D4EB4F);
let low = lcg_input(50_000, 0x27D4EB4FC2B2AE3D);
let close = lcg_input(50_000, 0x165667B19E3779F9);
for period in [5usize, 14, 30] {
let s = willr_serial(&high, &low, &close, period).unwrap();
let p = willr_parallel(&high, &low, &close, period).unwrap();
assert_eq_1to1("willr", period, &s, &p);
}
}
#[test]
fn stoch_f_parallel_matches_serial() {
let high = lcg_input(50_000, 0x7F4A7C159E3779B9);
let low = lcg_input(50_000, 0x1CE4E5B9BF58476D);
let close = lcg_input(50_000, 0xD1CE4E5B97F4A7C1);
for (fk, fd) in [(5usize, 3usize), (14, 3), (30, 5)] {
let s = stoch_f_serial(&high, &low, &close, fk, fd).unwrap();
let p = stoch_f_parallel(&high, &low, &close, fk, fd).unwrap();
assert_eq_1to1("stoch_f.fast_k", fk, &s.fast_k, &p.fast_k);
assert_eq_1to1("stoch_f.fast_d", fk, &s.fast_d, &p.fast_d);
}
}