#![doc = include_str!("../README.md")]
use std::mem;
use std::ops::{AddAssign, SubAssign};
use std::borrow::Borrow;
#[derive(Default, Clone, Debug)]
pub struct FSum {
partials: Vec<f64>
}
impl FSum {
pub fn new() -> FSum {
FSum{ partials: Vec::new() }
}
pub fn with_value(initial_value: f64) -> FSum {
FSum{ partials: vec![initial_value] }
}
pub fn with_all<Bf64, InIter>(values: InIter) -> FSum
where Bf64: Borrow<f64>, InIter: IntoIterator<Item=Bf64>
{
let mut result = Self::new();
result.add_all(values);
result
}
pub fn add(&mut self, mut x: f64) -> &mut FSum {
let mut j = 0usize;
for i in 0..self.partials.len() {
let mut y: f64 = self.partials[i];
if x.abs() < y.abs() { mem::swap(&mut x, &mut y); }
let hi = x + y;
let lo = y - (hi - x);
if lo != 0.0 {
self.partials[j] = lo;
j += 1;
}
x = hi;
}
if j >= self.partials.len() {
self.partials.push(x);
} else {
self.partials[j] = x;
self.partials.truncate(j + 1);
}
self
}
pub fn add_all<InIter, Bf64>(&mut self, values: InIter) -> &mut FSum
where Bf64: Borrow<f64>, InIter: IntoIterator<Item=Bf64>
{
for x in values { self.add(*x.borrow()); }
self
}
pub fn value(&self) -> f64 {
let mut n = self.partials.len();
if n == 0 { return 0.0; }
n -= 1;
let mut total = self.partials[n];
if n == 0 { return total; }
loop { let old_total = total;
n -= 1;
let x = self.partials[n];
total = old_total + x;
if n == 0 { return total; }
let error = x - (total - old_total);
if error != 0.0 {
if (error < 0.0 && self.partials[n - 1] < 0.0) || (error > 0.0 && self.partials[n - 1] > 0.0) {
let y = error * 2.0;
let x = total + y;
if y == x - total { return x; }
}
return total;
}
};
}
pub fn reset(&mut self) -> &mut FSum {
self.partials.clear();
self
}
pub fn set(&mut self, value: f64) -> &mut FSum {
self.partials = vec![value];
self
}
}
impl AddAssign<f64> for FSum {
#[inline] fn add_assign(&mut self, other: f64) { self.add(other); }
}
impl SubAssign<f64> for FSum {
#[inline] fn sub_assign(&mut self, other: f64) { self.add(- other); }
}
impl From<f64> for FSum {
#[inline] fn from(initial_value: f64) -> Self {
Self::with_value(initial_value)
}
}
impl From<FSum> for f64 {
#[inline] fn from(fsum: FSum) -> Self {
fsum.value()
}
}
impl From<&FSum> for f64 {
#[inline] fn from(fsum: &FSum) -> Self {
fsum.value()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fsum() {
assert_eq!(FSum::new().add(2.0).add(3.0).value(), 5.0);
assert_eq!(FSum::with_value(2.0).add(3.0).value(), 5.0);
assert_eq!(FSum::with_all((0..10).map(|_| 0.1)).value(), 1.0);
assert_eq!(FSum::new().add(1e100).add(1.0).add(-1e100).value(), 1.0);
assert_eq!(FSum::with_all(&[1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50]).value(), 1e-100);
assert_eq!(FSum::with_all(&[-1e308, 1e308, 1e308]).value(), 1e308);
assert_eq!(FSum::with_all(&[1e308, -1e308, 1e308]).value(), 1e308);
}
#[test]
fn fsum_add_sub_assign_reset_into() {
let mut s: FSum = Default::default();
assert_eq!(s.value(), 0.0);
s += 1.0;
assert_eq!(s.value(), 1.0);
s += 2.0;
assert_eq!(s.value(), 3.0);
s -= 1.0;
assert_eq!(s.value(), 2.0);
s.reset();
assert_eq!(s.value(), 0.0);
s.set(5.0);
assert_eq!(s.value(), 5.0);
assert_eq!(5.0, s.into());
}
}