use super::basic::mean;
pub fn count_above(series: &[f64], threshold: f64) -> usize {
series.iter().filter(|&&x| x > threshold).count()
}
pub fn count_below(series: &[f64], threshold: f64) -> usize {
series.iter().filter(|&&x| x < threshold).count()
}
pub fn count_above_mean(series: &[f64]) -> usize {
if series.is_empty() {
return 0;
}
let m = mean(series);
series.iter().filter(|&&x| x > m).count()
}
pub fn count_below_mean(series: &[f64]) -> usize {
if series.is_empty() {
return 0;
}
let m = mean(series);
series.iter().filter(|&&x| x < m).count()
}
pub fn number_peaks(series: &[f64], support: usize) -> usize {
if series.len() < 2 * support + 1 || support == 0 {
return 0;
}
let mut count = 0;
for i in support..(series.len() - support) {
let is_peak = (1..=support).all(|j| series[i] > series[i - j] && series[i] > series[i + j]);
if is_peak {
count += 1;
}
}
count
}
pub fn number_crossing_m(series: &[f64], m: f64) -> usize {
if series.len() < 2 {
return 0;
}
series
.windows(2)
.filter(|w| (w[0] <= m && w[1] > m) || (w[0] > m && w[1] <= m))
.count()
}
pub fn longest_strike_above_mean(series: &[f64]) -> usize {
if series.is_empty() {
return 0;
}
let m = mean(series);
longest_strike(series, |x| x > m)
}
pub fn longest_strike_below_mean(series: &[f64]) -> usize {
if series.is_empty() {
return 0;
}
let m = mean(series);
longest_strike(series, |x| x < m)
}
fn longest_strike<F>(series: &[f64], predicate: F) -> usize
where
F: Fn(f64) -> bool,
{
let mut max_strike = 0;
let mut current_strike = 0;
for &x in series {
if predicate(x) {
current_strike += 1;
max_strike = max_strike.max(current_strike);
} else {
current_strike = 0;
}
}
max_strike
}
pub fn first_location_of_maximum(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let max_val = series.iter().copied().fold(f64::NEG_INFINITY, f64::max);
let pos = series.iter().position(|&x| x == max_val).unwrap_or(0);
pos as f64 / series.len() as f64
}
pub fn first_location_of_minimum(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let min_val = series.iter().copied().fold(f64::INFINITY, f64::min);
let pos = series.iter().position(|&x| x == min_val).unwrap_or(0);
pos as f64 / series.len() as f64
}
pub fn last_location_of_maximum(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let max_val = series.iter().copied().fold(f64::NEG_INFINITY, f64::max);
let reversed_pos = series.iter().rev().position(|&x| x == max_val).unwrap_or(0);
1.0 - reversed_pos as f64 / series.len() as f64
}
pub fn last_location_of_minimum(series: &[f64]) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let min_val = series.iter().copied().fold(f64::INFINITY, f64::min);
let reversed_pos = series.iter().rev().position(|&x| x == min_val).unwrap_or(0);
1.0 - reversed_pos as f64 / series.len() as f64
}
pub fn has_duplicate(series: &[f64]) -> bool {
if series.len() < 2 {
return false;
}
let mut sorted = series.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
sorted.windows(2).any(|w| (w[0] - w[1]).abs() < 1e-10)
}
pub fn has_duplicate_max(series: &[f64]) -> bool {
if series.len() < 2 {
return false;
}
let max_val = series.iter().copied().fold(f64::NEG_INFINITY, f64::max);
series
.iter()
.filter(|&&x| (x - max_val).abs() < 1e-10)
.count()
> 1
}
pub fn has_duplicate_min(series: &[f64]) -> bool {
if series.len() < 2 {
return false;
}
let min_val = series.iter().copied().fold(f64::INFINITY, f64::min);
series
.iter()
.filter(|&&x| (x - min_val).abs() < 1e-10)
.count()
> 1
}
pub fn index_mass_quantile(series: &[f64], q: f64) -> f64 {
if series.is_empty() {
return f64::NAN;
}
let q = q.clamp(0.0, 1.0);
let abs_values: Vec<f64> = series.iter().map(|x| x.abs()).collect();
let total_mass: f64 = abs_values.iter().sum();
if total_mass < 1e-10 {
return 0.0;
}
let target = q * total_mass;
let mut cumsum = 0.0;
for (i, &v) in abs_values.iter().enumerate() {
cumsum += v;
if cumsum >= target {
return (i + 1) as f64 / series.len() as f64;
}
}
1.0
}
pub fn value_count(series: &[f64], value: f64) -> usize {
series
.iter()
.filter(|&&x| (x - value).abs() < 1e-10)
.count()
}
pub fn range_count(series: &[f64], min: f64, max: f64) -> usize {
series.iter().filter(|&&x| x >= min && x <= max).count()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn count_above_basic() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(count_above(&series, 3.0), 2); assert_eq!(count_above(&series, 0.0), 5);
assert_eq!(count_above(&series, 10.0), 0);
}
#[test]
fn count_below_basic() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(count_below(&series, 3.0), 2); assert_eq!(count_below(&series, 10.0), 5);
assert_eq!(count_below(&series, 0.0), 0);
}
#[test]
fn count_above_below_empty() {
assert_eq!(count_above(&[], 0.0), 0);
assert_eq!(count_below(&[], 0.0), 0);
}
#[test]
fn count_above_mean_basic() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0]; assert_eq!(count_above_mean(&series), 2); }
#[test]
fn count_below_mean_basic() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0]; assert_eq!(count_below_mean(&series), 2); }
#[test]
fn count_above_below_mean_empty() {
assert_eq!(count_above_mean(&[]), 0);
assert_eq!(count_below_mean(&[]), 0);
}
#[test]
fn count_above_below_mean_constant() {
let series = vec![5.0; 10];
assert_eq!(count_above_mean(&series), 0);
assert_eq!(count_below_mean(&series), 0);
}
#[test]
fn number_peaks_basic() {
let series = vec![1.0, 2.0, 5.0, 2.0, 1.0];
assert_eq!(number_peaks(&series, 1), 1);
assert_eq!(number_peaks(&series, 2), 1);
}
#[test]
fn number_peaks_multiple() {
let series = vec![1.0, 3.0, 1.0, 3.0, 1.0, 3.0, 1.0];
assert_eq!(number_peaks(&series, 1), 3); }
#[test]
fn number_peaks_plateau() {
let series = vec![1.0, 3.0, 3.0, 3.0, 1.0];
assert_eq!(number_peaks(&series, 1), 0);
}
#[test]
fn number_peaks_short() {
assert_eq!(number_peaks(&[], 1), 0);
assert_eq!(number_peaks(&[1.0], 1), 0);
assert_eq!(number_peaks(&[1.0, 2.0], 1), 0);
}
#[test]
fn number_peaks_zero_support() {
let series = vec![1.0, 2.0, 3.0, 2.0, 1.0];
assert_eq!(number_peaks(&series, 0), 0);
}
#[test]
fn number_crossing_m_basic() {
let series = vec![-1.0, 1.0, -1.0, 1.0, -1.0];
assert_eq!(number_crossing_m(&series, 0.0), 4);
}
#[test]
fn number_crossing_m_no_crossing() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(number_crossing_m(&series, 0.0), 0);
}
#[test]
fn number_crossing_m_short() {
assert_eq!(number_crossing_m(&[], 0.0), 0);
assert_eq!(number_crossing_m(&[1.0], 0.0), 0);
}
#[test]
fn longest_strike_above_mean_basic() {
let series = vec![1.0, 5.0, 5.0, 5.0, 1.0, 5.0, 1.0]; assert_eq!(longest_strike_above_mean(&series), 3);
}
#[test]
fn longest_strike_below_mean_basic() {
let series = vec![5.0, 1.0, 1.0, 5.0, 5.0]; assert_eq!(longest_strike_below_mean(&series), 2);
}
#[test]
fn longest_strike_empty() {
assert_eq!(longest_strike_above_mean(&[]), 0);
assert_eq!(longest_strike_below_mean(&[]), 0);
}
#[test]
fn longest_strike_constant() {
let series = vec![5.0; 10];
assert_eq!(longest_strike_above_mean(&series), 0);
assert_eq!(longest_strike_below_mean(&series), 0);
}
#[test]
fn first_location_of_maximum_basic() {
let series = vec![1.0, 5.0, 3.0, 5.0, 2.0]; assert_relative_eq!(first_location_of_maximum(&series), 0.2, epsilon = 1e-10);
}
#[test]
fn last_location_of_maximum_basic() {
let series = vec![1.0, 5.0, 3.0, 5.0, 2.0]; assert_relative_eq!(last_location_of_maximum(&series), 0.8, epsilon = 1e-10);
}
#[test]
fn first_location_of_minimum_basic() {
let series = vec![5.0, 1.0, 3.0, 1.0, 4.0]; assert_relative_eq!(first_location_of_minimum(&series), 0.2, epsilon = 1e-10);
}
#[test]
fn last_location_of_minimum_basic() {
let series = vec![5.0, 1.0, 3.0, 1.0, 4.0]; assert_relative_eq!(last_location_of_minimum(&series), 0.8, epsilon = 1e-10);
}
#[test]
fn location_empty() {
assert!(first_location_of_maximum(&[]).is_nan());
assert!(last_location_of_maximum(&[]).is_nan());
assert!(first_location_of_minimum(&[]).is_nan());
assert!(last_location_of_minimum(&[]).is_nan());
}
#[test]
fn location_single() {
assert_relative_eq!(first_location_of_maximum(&[5.0]), 0.0, epsilon = 1e-10);
assert_relative_eq!(last_location_of_maximum(&[5.0]), 1.0, epsilon = 1e-10);
}
#[test]
fn has_duplicate_true() {
let series = vec![1.0, 2.0, 3.0, 2.0, 4.0];
assert!(has_duplicate(&series));
}
#[test]
fn has_duplicate_false() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert!(!has_duplicate(&series));
}
#[test]
fn has_duplicate_short() {
assert!(!has_duplicate(&[]));
assert!(!has_duplicate(&[1.0]));
}
#[test]
fn has_duplicate_max_true() {
let series = vec![1.0, 5.0, 3.0, 5.0, 2.0];
assert!(has_duplicate_max(&series));
}
#[test]
fn has_duplicate_max_false() {
let series = vec![1.0, 5.0, 3.0, 4.0, 2.0];
assert!(!has_duplicate_max(&series));
}
#[test]
fn has_duplicate_min_true() {
let series = vec![5.0, 1.0, 3.0, 1.0, 2.0];
assert!(has_duplicate_min(&series));
}
#[test]
fn has_duplicate_min_false() {
let series = vec![5.0, 1.0, 3.0, 2.0, 4.0];
assert!(!has_duplicate_min(&series));
}
#[test]
fn has_duplicate_max_min_short() {
assert!(!has_duplicate_max(&[]));
assert!(!has_duplicate_max(&[1.0]));
assert!(!has_duplicate_min(&[]));
assert!(!has_duplicate_min(&[1.0]));
}
#[test]
fn index_mass_quantile_uniform() {
let series = vec![1.0, 1.0, 1.0, 1.0, 1.0];
assert_relative_eq!(index_mass_quantile(&series, 0.5), 0.6, epsilon = 1e-10);
}
#[test]
fn index_mass_quantile_boundaries() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_relative_eq!(index_mass_quantile(&series, 0.0), 0.2, epsilon = 1e-10);
assert_relative_eq!(index_mass_quantile(&series, 1.0), 1.0, epsilon = 1e-10);
}
#[test]
fn index_mass_quantile_empty() {
assert!(index_mass_quantile(&[], 0.5).is_nan());
}
#[test]
fn index_mass_quantile_zeros() {
let series = vec![0.0; 5];
assert_relative_eq!(index_mass_quantile(&series, 0.5), 0.0, epsilon = 1e-10);
}
#[test]
fn value_count_basic() {
let series = vec![1.0, 2.0, 1.0, 3.0, 1.0];
assert_eq!(value_count(&series, 1.0), 3);
assert_eq!(value_count(&series, 2.0), 1);
assert_eq!(value_count(&series, 5.0), 0);
}
#[test]
fn value_count_empty() {
assert_eq!(value_count(&[], 1.0), 0);
}
#[test]
fn range_count_basic() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(range_count(&series, 2.0, 4.0), 3); assert_eq!(range_count(&series, 0.0, 10.0), 5);
assert_eq!(range_count(&series, 10.0, 20.0), 0);
}
#[test]
fn range_count_empty() {
assert_eq!(range_count(&[], 0.0, 10.0), 0);
}
#[test]
fn range_count_boundaries() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(range_count(&series, 2.0, 2.0), 1); }
}