use crate::Real;
#[inline]
fn fast_two_sum<R: Real>(a: R, b: R) -> (R, R) {
let x = a + b;
let b_virtual = x - a;
(x, b - b_virtual)
}
#[inline]
fn two_sum<R: Real>(a: R, b: R) -> (R, R) {
let x = a + b;
let b_virtual = x - a;
let a_virtual = x - b_virtual;
let b_roundoff = b - b_virtual;
let a_roundoff = a - a_virtual;
(x, a_roundoff + b_roundoff)
}
#[inline]
fn two_diff<R: Real>(a: R, b: R) -> (R, R) {
let x = a - b;
let b_virtual = a - x;
let a_virtual = x + b_virtual;
let b_roundoff = b_virtual - b;
let a_roundoff = a - a_virtual;
(x, a_roundoff + b_roundoff)
}
#[inline]
fn split<R: Real>(a: R) -> (R, R) {
let c = R::SPLITTER * a;
let a_big = c - a;
let a_hi = c - a_big;
(a_hi, a - a_hi)
}
#[inline]
fn two_product<R: Real>(a: R, b: R) -> (R, R) {
let x = a * b;
let (a_hi, a_lo) = split(a);
let (b_hi, b_lo) = split(b);
let err1 = x - (a_hi * b_hi);
let err2 = err1 - (a_lo * b_hi);
let err3 = err2 - (a_hi * b_lo);
(x, (a_lo * b_lo) - err3)
}
#[inline]
fn two_two_diff<R: Real>(a1: R, a0: R, b1: R, b0: R) -> [R; 4] {
let (i, x0) = two_diff(a0, b0);
let (j, k) = two_sum(a1, i);
let (i2, x1) = two_diff(k, b1);
let (x3, x2) = two_sum(j, i2);
[x0, x1, x2, x3]
}
fn fast_expansion_sum<R: Real>(e: &[R], f: &[R], h: &mut [R]) -> Option<usize> {
let total = e.len().checked_add(f.len())?;
if h.len() < total {
return None;
}
let mut ei = 0usize;
let mut fi = 0usize;
fn take<R: Real>(e: &[R], ei: &mut usize, f: &[R], fi: &mut usize) -> Option<R> {
match (e.get(*ei), f.get(*fi)) {
(Some(&ev), Some(&fv)) => {
if ev.abs() <= fv.abs() {
*ei += 1;
Some(ev)
} else {
*fi += 1;
Some(fv)
}
}
(Some(&ev), None) => {
*ei += 1;
Some(ev)
}
(None, Some(&fv)) => {
*fi += 1;
Some(fv)
}
(None, None) => None,
}
}
let Some(g0) = take(e, &mut ei, f, &mut fi) else {
return Some(0);
};
let Some(g1) = take(e, &mut ei, f, &mut fi) else {
return Some(if g0 == R::ZERO {
0
} else {
*h.first_mut()? = g0;
1
});
};
let (mut q, first) = fast_two_sum(g1, g0);
let mut written = 0usize;
let emit = |value: R, h: &mut [R], written: &mut usize| {
if value != R::ZERO {
match h.get_mut(*written) {
Some(slot) => {
*slot = value;
*written += 1;
}
None => return false,
}
}
true
};
if !emit(first, h, &mut written) {
return None;
}
while let Some(g) = take(e, &mut ei, f, &mut fi) {
let (q_new, roundoff) = two_sum(q, g);
q = q_new;
if !emit(roundoff, h, &mut written) {
return None;
}
}
if q != R::ZERO || written == 0 {
*h.get_mut(written)? = q;
written += 1;
}
Some(written)
}
fn scale_expansion<R: Real>(e: &[R], b: R, h: &mut [R]) -> Option<usize> {
if h.len() < e.len().checked_mul(2)? {
return None;
}
let mut written = 0usize;
let push = |value: R, h: &mut [R], written: &mut usize| -> bool {
if value == R::ZERO {
return true;
}
match h.get_mut(*written) {
Some(slot) => {
*slot = value;
*written += 1;
true
}
None => false,
}
};
let Some((&first, rest)) = e.split_first() else {
return Some(0);
};
let (mut q, h1) = two_product(first, b);
if !push(h1, h, &mut written) {
return None;
}
for &e_i in rest {
let (t_hi, t_lo) = two_product(e_i, b);
let (q_sum, h_even) = two_sum(q, t_lo);
if !push(h_even, h, &mut written) {
return None;
}
let (h_odd, q_next) = fast_two_sum(t_hi, q_sum);
if !push(h_odd, h, &mut written) {
return None;
}
q = q_next;
}
if (q != R::ZERO || written == 0) && !push_final(q, h, &mut written) {
return None;
}
Some(written)
}
fn push_final<R: Real>(value: R, h: &mut [R], written: &mut usize) -> bool {
match h.get_mut(*written) {
Some(slot) => {
*slot = value;
*written += 1;
true
}
None => false,
}
}
#[inline]
fn ccw_error_bound_a<R: Real>() -> R {
let e = R::UNIT_ROUNDOFF;
(R::from_f64(3.0) + R::from_f64(16.0) * e) * e
}
#[must_use]
pub fn orient2d<R: Real>(a: [R; 2], b: [R; 2], c: [R; 2]) -> R {
let det_left = (a[0] - c[0]) * (b[1] - c[1]);
let det_right = (a[1] - c[1]) * (b[0] - c[0]);
let det = det_left - det_right;
let det_sum = if det_left > R::ZERO {
if det_right <= R::ZERO {
return det;
}
det_left + det_right
} else if det_left < R::ZERO {
if det_right >= R::ZERO {
return det;
}
-det_left - det_right
} else {
return det;
};
let error_bound = ccw_error_bound_a::<R>() * det_sum;
if det >= error_bound || -det >= error_bound {
return det;
}
orient2d_exact(a, b, c)
}
fn orient2d_exact<R: Real>(a: [R; 2], b: [R; 2], c: [R; 2]) -> R {
let (axby1, axby0) = two_product(a[0], b[1]);
let (axcy1, axcy0) = two_product(a[0], c[1]);
let a_term = two_two_diff(axby1, axby0, axcy1, axcy0);
let (bxcy1, bxcy0) = two_product(b[0], c[1]);
let (bxay1, bxay0) = two_product(b[0], a[1]);
let b_term = two_two_diff(bxcy1, bxcy0, bxay1, bxay0);
let (cxay1, cxay0) = two_product(c[0], a[1]);
let (cxby1, cxby0) = two_product(c[0], b[1]);
let c_term = two_two_diff(cxay1, cxay0, cxby1, cxby0);
let mut ab = [R::ZERO; 8];
let mut total = [R::ZERO; 12];
let Some(ab_len) = fast_expansion_sum(&a_term, &b_term, &mut ab) else {
return R::ZERO;
};
let Some(total_len) = fast_expansion_sum(ab.get(..ab_len).unwrap_or(&[]), &c_term, &mut total)
else {
return R::ZERO;
};
total_len
.checked_sub(1)
.and_then(|last| total.get(last).copied())
.unwrap_or(R::ZERO)
}
#[inline]
fn incircle_error_bound_a<R: Real>() -> R {
let e = R::UNIT_ROUNDOFF;
(R::from_f64(10.0) + R::from_f64(96.0) * e) * e
}
#[inline]
fn cross_exact<R: Real>(u: [R; 2], v: [R; 2]) -> [R; 4] {
let (p1, p0) = two_product(u[0], v[1]);
let (q1, q0) = two_product(u[1], v[0]);
two_two_diff(p1, p0, q1, q0)
}
#[inline]
fn negated<R: Real>(e: [R; 4]) -> [R; 4] {
[-e[0], -e[1], -e[2], -e[3]]
}
#[must_use]
pub fn incircle<R: Real>(a: [R; 2], b: [R; 2], c: [R; 2], d: [R; 2]) -> R {
let adx = a[0] - d[0];
let ady = a[1] - d[1];
let bdx = b[0] - d[0];
let bdy = b[1] - d[1];
let cdx = c[0] - d[0];
let cdy = c[1] - d[1];
let bdxcdy = bdx * cdy;
let cdxbdy = cdx * bdy;
let alift = adx * adx + ady * ady;
let cdxady = cdx * ady;
let adxcdy = adx * cdy;
let blift = bdx * bdx + bdy * bdy;
let adxbdy = adx * bdy;
let bdxady = bdx * ady;
let clift = cdx * cdx + cdy * cdy;
let det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady);
let permanent = (bdxcdy.abs() + cdxbdy.abs()) * alift
+ (cdxady.abs() + adxcdy.abs()) * blift
+ (adxbdy.abs() + bdxady.abs()) * clift;
let error_bound = incircle_error_bound_a::<R>() * permanent;
if det > error_bound || -det > error_bound {
return det;
}
incircle_exact(a, b, c, d)
}
fn incircle_exact<R: Real>(a: [R; 2], b: [R; 2], c: [R; 2], d: [R; 2]) -> R {
let ab = cross_exact(a, b);
let ac = cross_exact(a, c);
let ad = cross_exact(a, d);
let bc = cross_exact(b, c);
let bd = cross_exact(b, d);
let cd = cross_exact(c, d);
let mut minor = [R::ZERO; 12];
let mut term = [R::ZERO; 96];
let mut total = [R::ZERO; 384];
let mut total_len = 0usize;
let mut scratch = [R::ZERO; 384];
let contributions: [([R; 2], [[R; 4]; 3], bool); 4] = [
(a, [bc, negated(bd), cd], false),
(b, [ac, negated(ad), cd], true),
(c, [ab, negated(ad), bd], false),
(d, [ab, negated(ac), bc], true),
];
for (point, parts, negate_term) in contributions {
let Some(minor_len) = sum3(&parts[0], &parts[1], &parts[2], &mut minor) else {
return R::ZERO;
};
let Some(term_len) = lift(point, minor.get(..minor_len).unwrap_or(&[]), &mut term) else {
return R::ZERO;
};
if negate_term {
for slot in term.iter_mut().take(term_len) {
*slot = -*slot;
}
}
let Some(next) = fast_expansion_sum(
total.get(..total_len).unwrap_or(&[]),
term.get(..term_len).unwrap_or(&[]),
&mut scratch,
) else {
return R::ZERO;
};
match total.get_mut(..next) {
Some(dst) => match scratch.get(..next) {
Some(src) => dst.copy_from_slice(src),
None => return R::ZERO,
},
None => return R::ZERO,
}
total_len = next;
}
total_len
.checked_sub(1)
.and_then(|last| total.get(last).copied())
.unwrap_or(R::ZERO)
}
fn sum3<R: Real>(e: &[R; 4], f: &[R; 4], g: &[R; 4], out: &mut [R; 12]) -> Option<usize> {
let mut ef = [R::ZERO; 8];
let ef_len = fast_expansion_sum(e, f, &mut ef)?;
fast_expansion_sum(ef.get(..ef_len).unwrap_or(&[]), g, out)
}
fn lift<R: Real>(point: [R; 2], minor: &[R], out: &mut [R; 96]) -> Option<usize> {
let mut once = [R::ZERO; 24];
let mut x_squared = [R::ZERO; 48];
let mut y_squared = [R::ZERO; 48];
let once_len = scale_expansion(minor, point[0], &mut once)?;
let x_len = scale_expansion(once.get(..once_len)?, point[0], &mut x_squared)?;
let once_len = scale_expansion(minor, point[1], &mut once)?;
let y_len = scale_expansion(once.get(..once_len)?, point[1], &mut y_squared)?;
fast_expansion_sum(x_squared.get(..x_len)?, y_squared.get(..y_len)?, out)
}
#[cfg(test)]
mod tests;