use crate::counters::{self, Outcome, Predicate};
use crate::expansion::{
ICC_ERR_BOUND_A, ICC_ERR_BOUND_B, ICC_ERR_BOUND_C, RESULT_ERR_BOUND, expansion_sum,
scale_expansion, two_product,
};
use crate::range::{Prepared, U_N, U_S, Window, prepare};
use crate::util::{sign_of, two_diff_tail, two_two_diff};
use crate::{NonFinite, Sign};
#[cfg(test)]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(test)]
pub(crate) static INCIRCLE_EXACT_COUNT: AtomicUsize = AtomicUsize::new(0);
#[inline]
fn incircle_filter_raw(pa: [f64; 2], pb: [f64; 2], pc: [f64; 2], pd: [f64; 2]) -> Option<Sign> {
let adx = pa[0] - pd[0];
let ady = pa[1] - pd[1];
let bdx = pb[0] - pd[0];
let bdy = pb[1] - pd[1];
let cdx = pc[0] - pd[0];
let cdy = pc[1] - pd[1];
let alift = adx * adx + ady * ady;
let blift = bdx * bdx + bdy * bdy;
let clift = cdx * cdx + cdy * cdy;
let bc = bdx * cdy - bdy * cdx;
let ca = cdx * ady - cdy * adx;
let ab = adx * bdy - ady * bdx;
let det = alift * bc + blift * ca + clift * ab;
let lift_mag = |xx: f64, yy: f64| (xx + U_N) + (yy + U_N);
let minor_mag = |p: f64, q: f64| (p.abs() + U_N) + (q.abs() + U_N);
let ma = lift_mag(adx * adx, ady * ady) * minor_mag(bdx * cdy, bdy * cdx) + U_N;
let mb = lift_mag(bdx * bdx, bdy * bdy) * minor_mag(cdx * ady, cdy * adx) + U_N;
let mc = lift_mag(cdx * cdx, cdy * cdy) * minor_mag(adx * bdy, ady * bdx) + U_N;
let bound = ICC_ERR_BOUND_A * ((ma + mb) + mc) + U_S;
if det > bound || -det > bound {
Some(sign_of(det))
} else {
None
}
}
#[must_use = "the in-circle result is the return value; discarding it is almost certainly a bug"]
pub fn incircle(pa: [f64; 2], pb: [f64; 2], pc: [f64; 2], pd: [f64; 2]) -> Result<Sign, NonFinite> {
if !pa
.iter()
.chain(&pb)
.chain(&pc)
.chain(&pd)
.all(|v| v.is_finite())
{
counters::record(Predicate::Incircle, Outcome::NonFinite);
return Err(NonFinite::new());
}
if let Some(sign) = incircle_filter_raw(pa, pb, pc, pd) {
counters::record(Predicate::Incircle, Outcome::Fast);
return Ok(sign);
}
let coords = [pa[0], pa[1], pb[0], pb[1], pc[0], pc[1], pd[0], pd[1]];
let (sa, sb, sc, sd) = match prepare(&coords, Window::INCIRCLE) {
Prepared::AsIs => (pa, pb, pc, pd),
Prepared::Scaled(s) => (
[pa[0] * s, pa[1] * s],
[pb[0] * s, pb[1] * s],
[pc[0] * s, pc[1] * s],
[pd[0] * s, pd[1] * s],
),
Prepared::OutOfWindow => return Ok(incircle_exact_integer(pa, pb, pc, pd)),
};
let result = incircle_in_window(sa, sb, sc, sd);
Ok(if result == Sign::Zero {
incircle_exact_integer(pa, pb, pc, pd)
} else {
counters::record(Predicate::Incircle, Outcome::Fast);
result
})
}
fn incircle_in_window(pa: [f64; 2], pb: [f64; 2], pc: [f64; 2], pd: [f64; 2]) -> Sign {
let adx = pa[0] - pd[0];
let ady = pa[1] - pd[1];
let bdx = pb[0] - pd[0];
let bdy = pb[1] - pd[1];
let cdx = pc[0] - pd[0];
let cdy = pc[1] - pd[1];
let alift = adx * adx + ady * ady;
let blift = bdx * bdx + bdy * bdy;
let clift = cdx * cdx + cdy * cdy;
let det = alift * (bdx * cdy - bdy * cdx)
+ blift * (cdx * ady - cdy * adx)
+ clift * (adx * bdy - ady * bdx);
let permanent = (bdx.abs() * cdy.abs() + bdy.abs() * cdx.abs()) * alift.abs()
+ (cdx.abs() * ady.abs() + cdy.abs() * adx.abs()) * blift.abs()
+ (adx.abs() * bdy.abs() + ady.abs() * bdx.abs()) * clift.abs();
let error_bound = ICC_ERR_BOUND_A * permanent;
if det > error_bound || -det > error_bound {
return sign_of(det);
}
incircle_adapt(pa, pb, pc, pd, permanent)
}
#[inline(always)]
fn fin_merge(
fin1: &mut [f64; 1152],
fin2: &mut [f64; 1152],
fin_len: usize,
active: usize,
correction: &[f64],
) -> usize {
if active == 0 {
expansion_sum(&fin1[..fin_len], correction, fin2)
} else {
expansion_sum(&fin2[..fin_len], correction, fin1)
}
}
#[allow(clippy::too_many_lines)]
fn incircle_adapt(pa: [f64; 2], pb: [f64; 2], pc: [f64; 2], pd: [f64; 2], permanent: f64) -> Sign {
let adx = pa[0] - pd[0];
let ady = pa[1] - pd[1];
let bdx = pb[0] - pd[0];
let bdy = pb[1] - pd[1];
let cdx = pc[0] - pd[0];
let cdy = pc[1] - pd[1];
let (bdxcdy1, bdxcdy0) = two_product(bdx, cdy);
let (cdxbdy1, cdxbdy0) = two_product(cdx, bdy);
let bc = two_two_diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0);
let (cdxady1, cdxady0) = two_product(cdx, ady);
let (adxcdy1, adxcdy0) = two_product(adx, cdy);
let ca = two_two_diff(cdxady1, cdxady0, adxcdy1, adxcdy0);
let (adxbdy1, adxbdy0) = two_product(adx, bdy);
let (bdxady1, bdxady0) = two_product(bdx, ady);
let ab = two_two_diff(adxbdy1, adxbdy0, bdxady1, bdxady0);
let mut axbc = [0.0_f64; 8]; let axbc_len = scale_expansion(&bc, adx, &mut axbc);
let mut axxbc = [0.0_f64; 16]; let axxbc_len = scale_expansion(&axbc[..axbc_len], adx, &mut axxbc);
let mut aybc = [0.0_f64; 8];
let aybc_len = scale_expansion(&bc, ady, &mut aybc);
let mut ayybc = [0.0_f64; 16];
let ayybc_len = scale_expansion(&aybc[..aybc_len], ady, &mut ayybc);
let mut adet = [0.0_f64; 32]; let adet_len = expansion_sum(&axxbc[..axxbc_len], &ayybc[..ayybc_len], &mut adet);
let mut bxca = [0.0_f64; 8];
let bxca_len = scale_expansion(&ca, bdx, &mut bxca);
let mut bxxca = [0.0_f64; 16];
let bxxca_len = scale_expansion(&bxca[..bxca_len], bdx, &mut bxxca);
let mut byca = [0.0_f64; 8];
let byca_len = scale_expansion(&ca, bdy, &mut byca);
let mut byyca = [0.0_f64; 16];
let byyca_len = scale_expansion(&byca[..byca_len], bdy, &mut byyca);
let mut bdet = [0.0_f64; 32];
let bdet_len = expansion_sum(&bxxca[..bxxca_len], &byyca[..byyca_len], &mut bdet);
let mut cxab = [0.0_f64; 8];
let cxab_len = scale_expansion(&ab, cdx, &mut cxab);
let mut cxxab = [0.0_f64; 16];
let cxxab_len = scale_expansion(&cxab[..cxab_len], cdx, &mut cxxab);
let mut cyab = [0.0_f64; 8];
let cyab_len = scale_expansion(&ab, cdy, &mut cyab);
let mut cyyab = [0.0_f64; 16];
let cyyab_len = scale_expansion(&cyab[..cyab_len], cdy, &mut cyyab);
let mut cdet = [0.0_f64; 32];
let cdet_len = expansion_sum(&cxxab[..cxxab_len], &cyyab[..cyyab_len], &mut cdet);
let mut abdet = [0.0_f64; 64]; let abdet_len = expansion_sum(&adet[..adet_len], &bdet[..bdet_len], &mut abdet);
let mut fin1 = [0.0_f64; 1152]; let mut fin2 = [0.0_f64; 1152];
let mut fin_len = expansion_sum(&abdet[..abdet_len], &cdet[..cdet_len], &mut fin1);
let mut det: f64 = fin1[..fin_len].iter().sum();
let error_bound = ICC_ERR_BOUND_B * permanent;
if det >= error_bound || -det >= error_bound {
return sign_of(det);
}
let adxtail = two_diff_tail(pa[0], pd[0], adx);
let adytail = two_diff_tail(pa[1], pd[1], ady);
let bdxtail = two_diff_tail(pb[0], pd[0], bdx);
let bdytail = two_diff_tail(pb[1], pd[1], bdy);
let cdxtail = two_diff_tail(pc[0], pd[0], cdx);
let cdytail = two_diff_tail(pc[1], pd[1], cdy);
if adxtail == 0.0
&& adytail == 0.0
&& bdxtail == 0.0
&& bdytail == 0.0
&& cdxtail == 0.0
&& cdytail == 0.0
{
return sign_of(det);
}
let error_bound = ICC_ERR_BOUND_C * permanent + RESULT_ERR_BOUND * det.abs();
det += (adx * adx + ady * ady)
* ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail))
+ 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)
+ (bdx * bdx + bdy * bdy)
* ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail))
+ 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)
+ (cdx * cdx + cdy * cdy)
* ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail))
+ 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx);
if det >= error_bound || -det >= error_bound {
return sign_of(det);
}
#[cfg(test)]
INCIRCLE_EXACT_COUNT.fetch_add(1, Ordering::Relaxed);
let mut tmp8 = [0.0_f64; 8]; let mut tmp8b = [0.0_f64; 8];
let mut tmp16 = [0.0_f64; 16]; let mut tmp16b = [0.0_f64; 16];
let mut tmp32 = [0.0_f64; 32];
let mut active: usize = 0;
if adxtail != 0.0 {
let tmp8_len = scale_expansion(&bc, adxtail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * adx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], adxtail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if adytail != 0.0 {
let tmp8_len = scale_expansion(&bc, adytail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * ady, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], adytail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if bdxtail != 0.0 {
let tmp8_len = scale_expansion(&ca, bdxtail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * bdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], bdxtail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if bdytail != 0.0 {
let tmp8_len = scale_expansion(&ca, bdytail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * bdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], bdytail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if cdxtail != 0.0 {
let tmp8_len = scale_expansion(&ab, cdxtail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * cdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], cdxtail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if cdytail != 0.0 {
let tmp8_len = scale_expansion(&ab, cdytail, &mut tmp8);
let tmp16_len = scale_expansion(&tmp8[..tmp8_len], 2.0 * cdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..tmp16_len]);
active ^= 1;
let tmp16b_len = scale_expansion(&tmp8[..tmp8_len], cdytail, &mut tmp16b);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16b[..tmp16b_len]);
active ^= 1;
}
if bdxtail != 0.0 || bdytail != 0.0 || cdxtail != 0.0 || cdytail != 0.0 {
let (bct, bct_len, bctt, bctt_len) =
if bdxtail != 0.0 || bdytail != 0.0 || cdxtail != 0.0 || cdytail != 0.0 {
let (p1, p0) = two_product(bdxtail, cdy);
let (q1, q0) = two_product(cdxtail, bdy);
let tmp4a = two_two_diff(p1, p0, q1, q0); let (p1, p0) = two_product(bdx, cdytail);
let (q1, q0) = two_product(cdx, bdytail);
let tmp4b = two_two_diff(p1, p0, q1, q0); let bct_len = expansion_sum(&tmp4a, &tmp4b, &mut tmp8);
let (p1, p0) = two_product(bdxtail, cdytail);
let (q1, q0) = two_product(bdytail, cdxtail);
let bctt = two_two_diff(p1, p0, q1, q0);
(tmp8, bct_len, bctt, 4usize)
} else {
let mut bct_z = [0.0_f64; 8];
bct_z[0] = 0.0;
let bctt_z = [0.0_f64; 4];
(bct_z, 1usize, bctt_z, 1usize)
};
{
let t16_len = scale_expansion(&bct[..bct_len], adx, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], adx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t16_len = scale_expansion(&bct[..bct_len], ady, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], ady, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&bctt[..bctt_len], adx, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], adx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&bctt[..bctt_len], ady, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], ady, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
if adxtail != 0.0 {
let t16b_len = scale_expansion(&bct[..bct_len], adxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * adx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&bctt[..bctt_len], adxtail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * adx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], adxtail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&bct[..bct_len], adxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], adxtail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
if adytail != 0.0 {
let t16b_len = scale_expansion(&bct[..bct_len], adytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * ady, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&bctt[..bctt_len], adytail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * ady, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], adytail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&bct[..bct_len], adytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], adytail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
}
if cdxtail != 0.0 || cdytail != 0.0 || adxtail != 0.0 || adytail != 0.0 {
let (cat, cat_len, catt, catt_len) =
if cdxtail != 0.0 || cdytail != 0.0 || adxtail != 0.0 || adytail != 0.0 {
let (p1, p0) = two_product(cdxtail, ady);
let (q1, q0) = two_product(adxtail, cdy);
let tmp4a = two_two_diff(p1, p0, q1, q0);
let (p1, p0) = two_product(cdx, adytail);
let (q1, q0) = two_product(adx, cdytail);
let tmp4b = two_two_diff(p1, p0, q1, q0);
let cat_len = expansion_sum(&tmp4a, &tmp4b, &mut tmp8);
let (p1, p0) = two_product(cdxtail, adytail);
let (q1, q0) = two_product(cdytail, adxtail);
let catt = two_two_diff(p1, p0, q1, q0);
(tmp8, cat_len, catt, 4usize)
} else {
let mut cat_z = [0.0_f64; 8];
cat_z[0] = 0.0;
let catt_z = [0.0_f64; 4];
(cat_z, 1usize, catt_z, 1usize)
};
{
let t16_len = scale_expansion(&cat[..cat_len], bdx, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], bdx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t16_len = scale_expansion(&cat[..cat_len], bdy, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], bdy, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&catt[..catt_len], bdx, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], bdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&catt[..catt_len], bdy, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], bdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
if bdxtail != 0.0 {
let t16b_len = scale_expansion(&cat[..cat_len], bdxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * bdx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&catt[..catt_len], bdxtail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * bdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], bdxtail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&cat[..cat_len], bdxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], bdxtail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
if bdytail != 0.0 {
let t16b_len = scale_expansion(&cat[..cat_len], bdytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * bdy, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&catt[..catt_len], bdytail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * bdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], bdytail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&cat[..cat_len], bdytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], bdytail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
}
if adxtail != 0.0 || adytail != 0.0 || bdxtail != 0.0 || bdytail != 0.0 {
let (abt, abt_len, abtt, abtt_len) =
if adxtail != 0.0 || adytail != 0.0 || bdxtail != 0.0 || bdytail != 0.0 {
let (p1, p0) = two_product(adxtail, bdy);
let (q1, q0) = two_product(bdxtail, ady);
let tmp4a = two_two_diff(p1, p0, q1, q0);
let (p1, p0) = two_product(adx, bdytail);
let (q1, q0) = two_product(bdx, adytail);
let tmp4b = two_two_diff(p1, p0, q1, q0);
let abt_len = expansion_sum(&tmp4a, &tmp4b, &mut tmp8);
let (p1, p0) = two_product(adxtail, bdytail);
let (q1, q0) = two_product(adytail, bdxtail);
let abtt = two_two_diff(p1, p0, q1, q0);
(tmp8, abt_len, abtt, 4usize)
} else {
let mut abt_z = [0.0_f64; 8];
abt_z[0] = 0.0;
let abtt_z = [0.0_f64; 4];
(abt_z, 1usize, abtt_z, 1usize)
};
{
let t16_len = scale_expansion(&abt[..abt_len], cdx, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], cdx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t16_len = scale_expansion(&abt[..abt_len], cdy, &mut tmp16);
let t32_len = scale_expansion(&tmp16[..t16_len], cdy, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&abtt[..abtt_len], cdx, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], cdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
{
let t8_len = scale_expansion(&abtt[..abtt_len], cdy, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], cdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
}
if cdxtail != 0.0 {
let t16b_len = scale_expansion(&abt[..abt_len], cdxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * cdx, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&abtt[..abtt_len], cdxtail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * cdx, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], cdxtail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&abt[..abt_len], cdxtail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], cdxtail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
if cdytail != 0.0 {
let t16b_len = scale_expansion(&abt[..abt_len], cdytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], 2.0 * cdy, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
let t8_len = scale_expansion(&abtt[..abtt_len], cdytail, &mut tmp8b);
let t16_len = scale_expansion(&tmp8b[..t8_len], 2.0 * cdy, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16_len = scale_expansion(&tmp8b[..t8_len], cdytail, &mut tmp16);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp16[..t16_len]);
active ^= 1;
let t16b_len = scale_expansion(&abt[..abt_len], cdytail, &mut tmp16b);
let t32_len = scale_expansion(&tmp16b[..t16b_len], cdytail, &mut tmp32);
fin_len = fin_merge(&mut fin1, &mut fin2, fin_len, active, &tmp32[..t32_len]);
active ^= 1;
}
}
if fin_len == 0 {
return Sign::Zero;
}
let fin_slice = if active == 0 {
&fin1[..fin_len]
} else {
&fin2[..fin_len]
};
sign_of(fin_slice[fin_len - 1])
}
fn incircle_exact_integer(pa: [f64; 2], pb: [f64; 2], pc: [f64; 2], pd: [f64; 2]) -> Sign {
counters::record(Predicate::Incircle, Outcome::Exact);
use crate::exact_int::f64_to_incircle_int;
let ax = f64_to_incircle_int(pa[0]);
let ay = f64_to_incircle_int(pa[1]);
let bx = f64_to_incircle_int(pb[0]);
let by = f64_to_incircle_int(pb[1]);
let cx = f64_to_incircle_int(pc[0]);
let cy = f64_to_incircle_int(pc[1]);
let dx = f64_to_incircle_int(pd[0]);
let dy = f64_to_incircle_int(pd[1]);
let adx = ax - dx;
let ady = ay - dy;
let bdx = bx - dx;
let bdy = by - dy;
let cdx = cx - dx;
let cdy = cy - dy;
let alift = adx * adx + ady * ady;
let blift = bdx * bdx + bdy * bdy;
let clift = cdx * cdx + cdy * cdy;
let det = alift * (bdx * cdy - bdy * cdx)
+ blift * (cdx * ady - cdy * adx)
+ clift * (adx * bdy - ady * bdx);
debug_assert!(
det.fits_in_signed_bits(8400),
"incircle Stage 4 overflow: width analysis violated; result sign may be incorrect"
);
if det.is_positive() {
Sign::Positive
} else if det.is_negative() {
Sign::Negative
} else {
counters::record(Predicate::Incircle, Outcome::ExactZero);
Sign::Zero
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::range_plus_one,
clippy::no_effect_underscore_binding
)]
mod tests {
use super::*;
use core::sync::atomic::Ordering;
fn reset_exact_count() {
INCIRCLE_EXACT_COUNT.store(0, Ordering::SeqCst);
}
#[test]
fn incircle_point_inside() {
assert_eq!(
incircle([0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.5, 0.5]),
Ok(Sign::Positive),
"pd=(0.5,0.5) is inside the circumcircle of (0,0),(1,0),(1,1)"
);
}
#[test]
fn incircle_point_outside() {
assert_eq!(
incircle([0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [2.0, 2.0]),
Ok(Sign::Negative),
"pd=(2,2) is outside the circumcircle of (0,0),(1,0),(1,1)"
);
}
#[test]
fn incircle_point_on_circle() {
assert_eq!(
incircle([0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.0, 0.0]),
Ok(Sign::Zero),
"pd = pa is on the circumcircle; expect Zero"
);
}
#[test]
fn incircle_circumcenter_is_inside() {
assert_eq!(
incircle([0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.5, 0.5]),
Ok(Sign::Positive),
"pd=(0.5,0.5) is the circumcenter of (0,0),(1,0),(0,1), strictly inside"
);
}
#[test]
fn incircle_exact_path_fires() {
reset_exact_count();
let (a, b) = (3.0_f64 / 5.0, 4.0_f64 / 5.0);
let pa = [a, b];
let pb = [b, -a];
let pc = [-b, a];
let pd = [-a, -b];
assert_eq!(incircle(pa, pb, pc, pd), Ok(Sign::Zero));
let count = INCIRCLE_EXACT_COUNT.load(Ordering::SeqCst);
assert!(
count > 0,
"Stage 3 must be reached on cocircular input with inexact differences (count {count})"
);
}
#[test]
fn incircle_non_finite_inputs_return_err() {
let inf = f64::INFINITY;
let nan = f64::NAN;
assert_eq!(
incircle([inf, 0.0], [1.0, 0.0], [1.0, 1.0], [0.5, 0.5]),
Err(NonFinite::new())
);
assert_eq!(
incircle([0.0, 0.0], [1.0, nan], [1.0, 1.0], [0.5, 0.5]),
Err(NonFinite::new())
);
assert_eq!(
incircle([0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [f64::NEG_INFINITY, 0.5]),
Err(NonFinite::new())
);
}
}