use linreg::linear_regression;
pub(crate) mod utils;
use utils::*;
pub fn rssimple(x: &[f64]) -> f64 {
let n = x.len();
if n == 0 {
return 0.0;
}
let n_f64 = n as f64;
let inv_n = 1.0 / n_f64;
let x_mean: f64 = x.iter().sum::<f64>() * inv_n;
let mut cumsum = 0.0_f64;
let mut cum_min = 0.0_f64;
let mut cum_max = 0.0_f64;
let mut sum_sq = 0.0_f64;
for &val in x {
let d = val - x_mean;
cumsum += d;
cum_min = cum_min.min(cumsum);
cum_max = cum_max.max(cumsum);
sum_sq += d * d;
}
let std_dev = (sum_sq * inv_n).sqrt();
if std_dev < f64::EPSILON {
return 0.5; }
let rs = (cum_max - cum_min) / std_dev;
if rs < f64::EPSILON {
return 0.5;
}
rs.log2() / n_f64.log2()
}
pub fn rs_corrected(x: Vec<f64>) -> f64 {
let mut cap_x: Vec<f64> = vec![x.len() as f64];
let mut cap_y: Vec<f64> = vec![rscalc(&x)];
let mut n: Vec<u64> = vec![0, x.len() as u64 / 2, x.len() as u64];
while n[1] >= 8 {
let mut xl: Vec<f64> = vec![];
let mut yl: Vec<f64> = vec![];
for i in 1..n.len() {
let rs: f64 = rscalc(&x[((n[i - 1] + 1) as usize)..(n[i] as usize)]);
xl.push((n[i] - n[i - 1]) as f64);
yl.push(rs);
}
cap_x.push(mean(&xl));
cap_y.push(mean(&yl));
n = half(&n, x.len() as u64);
}
let cap_x_log: Vec<f64> = cap_x.iter().map(|a| a.ln()).collect();
let cap_y_log: Vec<f64> = cap_y.iter().map(|a| a.ln()).collect();
let (slope, _): (f64, f64) = linear_regression(&cap_x_log, &cap_y_log).unwrap();
slope
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rssimple_trending_series() {
let prices: Vec<f64> = (0..100).map(|i| 100.0 + i as f64 * 0.5).collect();
let h = rssimple(&prices);
assert!(h.is_finite(), "Hurst must be finite: {}", h);
assert!(h > 0.4, "Trending series should have H > 0.4: {}", h);
}
#[test]
fn test_rssimple_constant_series() {
let prices = vec![100.0; 50];
let h = rssimple(&prices);
assert_eq!(h, 0.5, "Constant series → H = 0.5");
}
#[test]
fn test_rssimple_empty() {
let h = rssimple(&[]);
assert_eq!(h, 0.0, "Empty series → 0.0");
}
#[test]
fn test_rssimple_single_element() {
let h = rssimple(&[100.0]);
assert_eq!(h, 0.5, "Single element → H = 0.5");
}
#[test]
fn test_rssimple_alternating_series() {
let prices: Vec<f64> = (0..200).map(|i| if i % 2 == 0 { 100.0 } else { 101.0 }).collect();
let h = rssimple(&prices);
assert!(h.is_finite(), "Hurst must be finite: {}", h);
assert!(h < 0.6, "Alternating series should have low H: {}", h);
}
#[test]
fn test_rssimple_five_elements_doc_example() {
let prices = vec![100.0, 101.0, 99.5, 102.0, 101.5];
let h = rssimple(&prices);
assert!(h >= 0.0 && h <= 1.5, "Hurst should be in reasonable range: {}", h);
}
#[test]
fn test_rssimple_two_elements() {
let h = rssimple(&[100.0, 200.0]);
assert!(h.is_finite(), "Two-element series must be finite: {}", h);
}
#[test]
fn test_rssimple_nan_in_series() {
let prices = vec![100.0, f64::NAN, 102.0, 101.0, 99.0];
let h = rssimple(&prices);
let _ = h;
}
#[test]
fn test_rssimple_negative_prices() {
let prices: Vec<f64> = (0..50).map(|i| -10.0 + i as f64 * 0.1).collect();
let h = rssimple(&prices);
assert!(h.is_finite(), "Negative prices must produce finite H: {}", h);
}
}