pub fn sum_f64(values: &[f64]) -> f64 {
if values.len() < 8 {
return values.iter().sum();
}
#[cfg(feature = "simd")]
{
sum_f64_simd(values)
}
#[cfg(not(feature = "simd"))]
{
values.iter().sum()
}
}
pub fn avg_f64(values: &[f64]) -> Option<f64> {
if values.is_empty() {
return None;
}
Some(sum_f64(values) / values.len() as f64)
}
pub fn min_f64(values: &[f64]) -> Option<f64> {
if values.is_empty() {
return None;
}
if values.len() < 8 {
return values
.iter()
.copied()
.min_by(|a, b| a.partial_cmp(b).unwrap());
}
#[cfg(feature = "simd")]
{
Some(min_f64_simd(values))
}
#[cfg(not(feature = "simd"))]
{
values
.iter()
.copied()
.min_by(|a, b| a.partial_cmp(b).unwrap())
}
}
pub fn max_f64(values: &[f64]) -> Option<f64> {
if values.is_empty() {
return None;
}
if values.len() < 8 {
return values
.iter()
.copied()
.max_by(|a, b| a.partial_cmp(b).unwrap());
}
#[cfg(feature = "simd")]
{
Some(max_f64_simd(values))
}
#[cfg(not(feature = "simd"))]
{
values
.iter()
.copied()
.max_by(|a, b| a.partial_cmp(b).unwrap())
}
}
#[cfg(feature = "simd")]
fn sum_f64_simd(values: &[f64]) -> f64 {
use std::simd::{f64x4, prelude::SimdFloat};
let chunks = values.chunks_exact(4);
let remainder = chunks.remainder();
let mut sum_vec = f64x4::splat(0.0);
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
sum_vec += vec;
}
let mut sum = sum_vec.reduce_sum();
sum += remainder.iter().sum::<f64>();
sum
}
#[cfg(feature = "simd")]
fn min_f64_simd(values: &[f64]) -> f64 {
use std::simd::{f64x4, prelude::SimdFloat};
let chunks = values.chunks_exact(4);
let remainder = chunks.remainder();
let mut min_vec = f64x4::splat(f64::INFINITY);
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
min_vec = min_vec.simd_min(vec);
}
let mut min = min_vec.reduce_min();
for &v in remainder {
if v < min {
min = v;
}
}
min
}
#[cfg(feature = "simd")]
fn max_f64_simd(values: &[f64]) -> f64 {
use std::simd::{f64x4, prelude::SimdFloat};
let chunks = values.chunks_exact(4);
let remainder = chunks.remainder();
let mut max_vec = f64x4::splat(f64::NEG_INFINITY);
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
max_vec = max_vec.simd_max(vec);
}
let mut max = max_vec.reduce_max();
for &v in remainder {
if v > max {
max = v;
}
}
max
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum_f64() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(sum_f64(&values), 15.0);
}
#[test]
fn test_sum_f64_large() {
let values: Vec<f64> = (1..=1000).map(|i| i as f64).collect();
let expected: f64 = (1..=1000).sum();
assert_eq!(sum_f64(&values), expected);
}
#[test]
fn test_avg_f64() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(avg_f64(&values), Some(3.0));
}
#[test]
fn test_avg_f64_empty() {
let values: Vec<f64> = vec![];
assert_eq!(avg_f64(&values), None);
}
#[test]
fn test_min_f64() {
let values = vec![5.0, 2.0, 8.0, 1.0, 9.0];
assert_eq!(min_f64(&values), Some(1.0));
}
#[test]
fn test_max_f64() {
let values = vec![5.0, 2.0, 8.0, 1.0, 9.0];
assert_eq!(max_f64(&values), Some(9.0));
}
#[test]
fn test_min_max_empty() {
let values: Vec<f64> = vec![];
assert_eq!(min_f64(&values), None);
assert_eq!(max_f64(&values), None);
}
}