use cfg_if::cfg_if;
use num_traits::Float;
#[cfg(feature = "parallel")]
use std::ops::Add;
#[cfg(feature = "parallel")]
use rayon::iter::plumbing::Reducer;
pub mod traits;
use self::traits::{Round3, TwoProduct, TwoSum};
#[derive(Copy, Clone, Debug)]
pub struct Dekker;
impl<F> TwoSum<F> for Dekker
where
F: Float,
{
#[inline]
fn two_sum(a: F, b: F) -> (F, F) {
let x = a + b;
let y = (a - x) + b;
(x, y)
}
}
#[derive(Copy, Clone, Debug)]
pub struct Neumaier;
impl<F> TwoSum<F> for Neumaier
where
F: Float,
{
#[inline]
fn two_sum(a: F, b: F) -> (F, F) {
if a.abs() >= b.abs() {
Dekker::two_sum(a, b)
} else {
Dekker::two_sum(b, a)
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Knuth;
impl<F> TwoSum<F> for Knuth
where
F: Float,
{
#[inline]
fn two_sum(a: F, b: F) -> (F, F) {
let x = a + b;
let z = x - a;
let y = (a - (x - z)) + (b - z);
(x, y)
}
}
pub fn two_sum<F>(a: F, b: F) -> (F, F)
where
F: Float,
{
Knuth::two_sum(a, b)
}
cfg_if! {
if #[cfg(feature = "fma")] {
#[inline]
pub fn two_product<F>(a: F, b: F) -> (F, F)
where F: TwoProduct
{
let x = a * b;
let y = a.mul_add(b, -x);
(x, y)
}
} else {
#[inline]
pub fn two_product<F>(x: F, y: F) -> (F, F)
where F: TwoProduct
{
let a = x * y;
let (x1, x2) = x.split();
let (y1, y2) = y.split();
let b = x2 * y2 - (((a - x1 * y1) - x2 * y1) - x1 * y2);
(a, b)
}
}
}
#[inline]
pub fn round3<F>(s0: F, s1: F, s2: F) -> F
where
F: Round3,
{
debug_assert!(s0 == s0 + s1);
debug_assert!(s1 == s1 + s2);
if s1.has_half_ulp_form() && s1.sign() == s2.sign() {
s0 + if s1.is_sign_positive() {
s1.next()
} else {
s1.prev()
}
} else {
s0
}
}
#[cfg(feature = "parallel")]
#[derive(Copy, Clone, Debug)]
pub struct AddReducer;
#[cfg(feature = "parallel")]
impl<Acc> Reducer<Acc> for AddReducer
where
Acc: Add<Acc, Output = Acc>,
{
#[inline]
fn reduce(self, left: Acc, right: Acc) -> Acc {
left + right
}
}