use axiolid_contracts::Sign;
use crate::expansion::{two_product, two_sum};
#[inline]
#[must_use]
fn fast_two_sum(a: f64, b: f64) -> (f64, f64) {
let sum = a + b;
let b_virtual = sum - a;
(sum, b - b_virtual)
}
#[must_use]
pub fn grow_expansion(e: &[f64], b: f64) -> Vec<f64> {
let mut out = Vec::with_capacity(e.len() + 1);
let mut carry = b;
for &component in e {
let (sum, error) = two_sum(carry, component);
if error != 0.0 {
out.push(error);
}
carry = sum;
}
if carry != 0.0 || out.is_empty() {
out.push(carry);
}
out
}
#[must_use]
pub fn expansion_sum(a: &[f64], b: &[f64]) -> Vec<f64> {
let mut result = a.to_vec();
for &component in b {
result = grow_expansion(&result, component);
}
result
}
#[must_use]
pub fn scale_expansion(e: &[f64], b: f64) -> Vec<f64> {
let mut out = Vec::with_capacity(e.len() * 2);
let mut carry = 0.0;
for &component in e {
let (product, product_error) = two_product(component, b);
let (sum, error) = two_sum(carry, product_error);
if error != 0.0 {
out.push(error);
}
let (new_carry, hi_error) = fast_two_sum(product, sum);
if hi_error != 0.0 {
out.push(hi_error);
}
carry = new_carry;
}
if carry != 0.0 || out.is_empty() {
out.push(carry);
}
out
}
#[must_use]
pub fn expansion_product(a: &[f64], b: &[f64]) -> Vec<f64> {
let mut out = Vec::from([0.0]);
for &component in b {
out = expansion_sum(&out, &scale_expansion(a, component));
}
out
}
#[must_use]
pub fn negate_expansion(e: &[f64]) -> Vec<f64> {
e.iter().map(|c| -c).collect()
}
#[must_use]
pub fn expansion_sign(e: &[f64]) -> Sign {
for &component in e.iter().rev() {
if component > 0.0 {
return Sign::Positive;
}
if component < 0.0 {
return Sign::Negative;
}
}
Sign::Zero
}