use crate::mask::GenericMask as _;
use super::*;
#[inline(always)]
pub fn newtons_method<V: FloatVector, P: Policy, F>(
mut x: V,
tolerance: V,
mut bounds: Option<(V, V)>,
mut f: F,
) -> (V, V::Mask)
where
F: FnMut(V) -> (V, V),
{
let min_is_negative = match bounds.as_ref() {
None => V::Mask::FALSY,
Some((min, max)) => {
x = x.clamp(*min, *max);
let min_neg = f(*min).0.is_negative();
debug_assert!(
(min_neg ^ f(*max).0.is_negative()).all(),
"newtons_method: bounds do not bracket a root (f(min) and f(max) must have opposite signs)"
);
min_neg
}
};
let mut converged = V::Mask::FALSY;
for _ in 0..P::POLICY.max_iterations {
let (y, y_prime) = f(x);
let mut stop = y.abs().cmp_le(tolerance);
let next_x = if let Some((ref mut min, ref mut max)) = bounds {
let same_sign_as_max = y.is_negative() ^ min_is_negative;
if const { P::POLICY.check_overflow } {
let live = y.is_finite();
*min = ((!same_sign_as_max) & live).select(x, *min);
*max = (same_sign_as_max & live).select(x, *max);
} else {
*min = same_sign_as_max.select(*min, x);
*max = same_sign_as_max.select(x, *max);
}
let width = *max - *min;
let x_bisection = width.mul_adde(V::HALF, *min);
stop |= width.cmp_le(tolerance);
stop |= x_bisection.cmp_le(*min) | x_bisection.cmp_ge(*max);
if stop.all() {
return (x, stop);
}
let x_newton = x - (y / y_prime);
let inside_bounds = x_newton.cmp_gt(*min) & x_newton.cmp_lt(*max);
inside_bounds.select(x_newton, x_bisection)
} else {
if stop.all() {
return (x, stop);
}
x - (y / y_prime)
};
converged = stop;
x = stop.select(x, next_x);
}
(x, converged)
}
#[inline(always)]
pub fn sum_f<V: FloatVector, P: Policy, F>(tolerance: V, start: i64, end: i64, mut f: F) -> Result<V, V>
where
F: FnMut(i64) -> V,
{
let mut sum = V::ZERO;
let mut c = V::ZERO; let mut n = start;
let mut converged = false;
let mut _iter = 0usize;
while _iter < P::POLICY.max_iterations {
_iter += 1;
if n >= end {
break;
}
let mut delta = f(n);
let abs_delta = delta.abs();
if abs_delta.cmp_le(tolerance).all() {
converged = true;
break;
}
let t = sum + delta;
if const { P::POLICY.use_compensation } {
sum.abs().cmp_lt(abs_delta).swap(&mut sum, &mut delta);
c += (sum - t) + delta;
}
sum = t;
n += 1;
}
if const { P::POLICY.use_compensation } {
sum += c; }
match converged {
true => Ok(sum),
false => Err(sum),
}
}
#[inline(always)]
pub fn prod_f<V: FloatVector, P: Policy, F>(tolerance: V, start: i64, end: i64, mut f: F) -> Result<V, V>
where
F: FnMut(i64) -> V,
{
let mut prod = V::ONE;
let mut n = start;
let mut _iter = 0usize;
while _iter < P::POLICY.max_iterations {
_iter += 1;
if n >= end {
break;
}
let new_prod = prod * f(n);
let delta = new_prod - prod;
if delta.abs().cmp_le(tolerance).all() {
return Ok(prod);
}
prod = new_prod;
n += 1;
}
Err(prod)
}
#[inline(always)]
pub fn aitken_sum<V: FloatVector, P: Policy, F>(tolerance: V, start: i64, end: i64, mut f: F) -> Result<V, V>
where
F: FnMut(i64) -> V,
{
let mut sum = V::ZERO;
let mut c = V::ZERO;
let mut s0 = V::ZERO;
let mut s1 = V::ZERO;
let mut s2;
let mut n = start;
let mut best = V::ZERO;
let mut phase = 0u32;
let mut _iter = 0usize;
while _iter < P::POLICY.max_iterations {
_iter += 1;
if n >= end {
break;
}
let mut term = f(n);
let t = sum + term;
if const { P::POLICY.use_compensation } {
let abs_term = term.abs();
sum.abs().cmp_lt(abs_term).swap(&mut sum, &mut term);
c += (sum - t) + term;
}
sum = t;
n += 1;
let res = if const { P::POLICY.use_compensation } {
sum + c
} else {
sum
};
match phase {
0 => {
s0 = res;
phase = 1;
continue;
}
1 => {
s1 = res;
phase = 2;
continue;
}
_ => {
s2 = res;
}
}
let d1 = s1 - s0; let d2 = s2 - s1; let denom = d2 - d1;
let denom_ok = denom.abs().cmp_gt(tolerance);
let a0 = (d2 * d2) / denom;
let accelerated = s2 - a0;
best = denom_ok.select(accelerated, s2);
if a0.zz(denom_ok).abs().cmp_le(tolerance).all() {
return Ok(best);
}
s0 = s1;
s1 = s2;
}
if phase < 2 {
best = if const { P::POLICY.use_compensation } {
sum + c
} else {
sum
};
}
Err(best)
}
#[inline(always)]
pub fn reduce_in_place<V: Copy, F>(values: &mut [V], mut op: F)
where
F: FnMut(V, V) -> V,
{
let mut stride = 1;
while stride < values.len() {
let mut i = 0;
let next_stride = stride << 1;
while i + stride < values.len() {
values[i] = op(values[i], values[i + stride]);
i += next_stride;
}
stride = next_stride;
}
}
#[inline(always)]
pub fn reduce_array<V: Copy, const N: usize, F>(mut values: [V; N], op: F) -> V
where
F: FnMut(V, V) -> V,
{
reduce_in_place(&mut values, op);
values[0]
}