pub fn sum(data: &[f64], period: usize) -> Vec<f64> {
let n = data.len();
let mut out = vec![f64::NAN; n];
if period == 0 || period > n {
return out;
}
let mut total = 0.0;
for &x in &data[..period - 1] {
total += x;
}
let mut trailing = 0;
#[allow(clippy::explicit_counter_loop)] for i in (period - 1)..n {
total += data[i];
out[i] = total;
total -= data[trailing];
trailing += 1;
}
out
}
pub fn maxindex(data: &[f64], period: usize) -> Vec<f64> {
let n = data.len();
let mut out = vec![f64::NAN; n];
if period == 0 || period > n {
return out;
}
let mut hi_idx = 0usize;
let mut hi = f64::NEG_INFINITY;
let mut first = true;
let mut trailing = 0usize;
#[allow(clippy::explicit_counter_loop)] for today in (period - 1)..n {
let tmp = data[today];
if first || hi_idx < trailing {
let win = &data[trailing..=today];
let mut h = win[0];
let mut hidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val > h {
h = val;
hidx = trailing + off;
}
}
hi = h;
hi_idx = hidx;
first = false;
} else if tmp >= hi {
hi_idx = today;
hi = tmp;
}
out[today] = hi_idx as f64;
trailing += 1;
}
out
}
pub fn minindex(data: &[f64], period: usize) -> Vec<f64> {
let n = data.len();
let mut out = vec![f64::NAN; n];
if period == 0 || period > n {
return out;
}
let mut lo_idx = 0usize;
let mut lo = f64::INFINITY;
let mut first = true;
let mut trailing = 0usize;
#[allow(clippy::explicit_counter_loop)] for today in (period - 1)..n {
let tmp = data[today];
if first || lo_idx < trailing {
let win = &data[trailing..=today];
let mut l = win[0];
let mut lidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val < l {
l = val;
lidx = trailing + off;
}
}
lo = l;
lo_idx = lidx;
first = false;
} else if tmp <= lo {
lo_idx = today;
lo = tmp;
}
out[today] = lo_idx as f64;
trailing += 1;
}
out
}
pub fn maxindex_final_state(data: &[f64], period: usize) -> Option<Vec<f64>> {
let n = data.len();
if period == 0 || period > n {
return None;
}
let mut hi_idx = 0usize;
let mut hi = f64::NEG_INFINITY;
let mut first = true;
let mut trailing = 0usize;
#[allow(clippy::explicit_counter_loop)] for today in (period - 1)..n {
let tmp = data[today];
if first || hi_idx < trailing {
let win = &data[trailing..=today];
let mut h = win[0];
let mut hidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val > h {
h = val;
hidx = trailing + off;
}
}
hi = h;
hi_idx = hidx;
first = false;
} else if tmp >= hi {
hi_idx = today;
hi = tmp;
}
trailing += 1;
}
Some(vec![hi_idx as f64, hi])
}
pub fn maxindex_resume(
data: &[f64],
period: usize,
from: usize,
origin: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let n = data.len();
if period == 0 || from < period - 1 || from > n {
return None;
}
let mut hi_idx = (state[0] as usize).saturating_sub(origin);
let mut hi = state[1];
let mut out = Vec::with_capacity(n - from);
for today in from..n {
let trailing = today - (period - 1);
let tmp = data[today];
if hi_idx < trailing {
let win = &data[trailing..=today];
let mut h = win[0];
let mut hidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val > h {
h = val;
hidx = trailing + off;
}
}
hi = h;
hi_idx = hidx;
} else if tmp >= hi {
hi_idx = today;
hi = tmp;
}
out.push((hi_idx + origin) as f64);
}
Some((out, vec![(hi_idx + origin) as f64, hi]))
}
pub fn minindex_final_state(data: &[f64], period: usize) -> Option<Vec<f64>> {
let n = data.len();
if period == 0 || period > n {
return None;
}
let mut lo_idx = 0usize;
let mut lo = f64::INFINITY;
let mut first = true;
let mut trailing = 0usize;
#[allow(clippy::explicit_counter_loop)] for today in (period - 1)..n {
let tmp = data[today];
if first || lo_idx < trailing {
let win = &data[trailing..=today];
let mut l = win[0];
let mut lidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val < l {
l = val;
lidx = trailing + off;
}
}
lo = l;
lo_idx = lidx;
first = false;
} else if tmp <= lo {
lo_idx = today;
lo = tmp;
}
trailing += 1;
}
Some(vec![lo_idx as f64, lo])
}
pub fn minindex_resume(
data: &[f64],
period: usize,
from: usize,
origin: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let n = data.len();
if period == 0 || from < period - 1 || from > n {
return None;
}
let mut lo_idx = (state[0] as usize).saturating_sub(origin);
let mut lo = state[1];
let mut out = Vec::with_capacity(n - from);
for today in from..n {
let trailing = today - (period - 1);
let tmp = data[today];
if lo_idx < trailing {
let win = &data[trailing..=today];
let mut l = win[0];
let mut lidx = trailing;
for (off, &val) in win.iter().enumerate().skip(1) {
if val < l {
l = val;
lidx = trailing + off;
}
}
lo = l;
lo_idx = lidx;
} else if tmp <= lo {
lo_idx = today;
lo = tmp;
}
out.push((lo_idx + origin) as f64);
}
Some((out, vec![(lo_idx + origin) as f64, lo]))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::indicators::test_support::*;
#[test]
fn index_resume_is_bit_identical_to_full() {
let data = series(120);
let period = 30usize;
let max_full = maxindex(&data, period);
let min_full = minindex(&data, period);
for &from in &[period, period + 1, 40, 60, 119] {
let st = maxindex_final_state(&data[..from], period).unwrap();
let (tail, _) = maxindex_resume(&data, period, from, 0, &st).unwrap();
assert_bits(&tail, &max_full[from..], "maxindex");
let st = minindex_final_state(&data[..from], period).unwrap();
let (tail, _) = minindex_resume(&data, period, from, 0, &st).unwrap();
assert_bits(&tail, &min_full[from..], "minindex");
}
}
#[test]
fn index_resume_emits_absolute_positions_with_origin() {
let data = series(120);
let period = 30usize;
let cut = 20usize; let sub = &data[cut..];
let max_full = maxindex(&data, period); let min_full = minindex(&data, period);
let from_sub = 40usize; let head_orig = &data[..cut + from_sub];
let st = maxindex_final_state(head_orig, period).unwrap();
let (tail, end) = maxindex_resume(sub, period, from_sub, cut, &st).unwrap();
assert_bits(&tail, &max_full[cut + from_sub..], "maxindex origin");
assert_eq!(end[0], *max_full.last().unwrap(), "maxindex end pos");
let st = minindex_final_state(head_orig, period).unwrap();
let (tail, end) = minindex_resume(sub, period, from_sub, cut, &st).unwrap();
assert_bits(&tail, &min_full[cut + from_sub..], "minindex origin");
assert_eq!(end[0], *min_full.last().unwrap(), "minindex end pos");
}
#[test]
fn index_guards_decline() {
let data = series(120);
assert!(maxindex_final_state(&data, 200).is_none()); assert!(minindex_final_state(&data, 200).is_none()); let st = maxindex_final_state(&data, 10).unwrap();
assert!(maxindex_resume(&data, 10, 5, 0, &st).is_none()); let st = minindex_final_state(&data, 10).unwrap();
assert!(minindex_resume(&data, 10, 5, 0, &st).is_none()); }
}