offroad 0.0.1-alpha

2D offsetting for arc polylines.
Documentation
#![allow(dead_code)]

use rand::distr::{Distribution, Uniform};
use rand::rngs::StdRng;

use crate::segment::{segment, Segment};
use crate::{arc::arc_circle_parametrization, point::point, Arc};

const ALMOST_EQUAL_C: u64 = 0x8000_0000_0000_0000 as u64;
const ALMOST_EQUAL_CI: i64 = ALMOST_EQUAL_C as i64;

#[inline]
pub fn almost_equal_as_int(a: f64, b: f64, ulps: i64) -> bool {
    debug_assert!(a.is_finite());
    debug_assert!(b.is_finite());
    if a.signum() != b.signum() {
        return a == b;
    }
    let mut a_i: i64 = a.to_bits() as i64;
    let mut b_i: i64 = b.to_bits() as i64;

    if a_i < 0i64 {
        a_i = ALMOST_EQUAL_CI - a_i;
    }
    if b_i < 0i64 {
        b_i = ALMOST_EQUAL_CI - b_i;
    }

    if (a_i - b_i).abs() <= ulps {
        return true;
    }
    return false;
}

pub fn close_enough(a: f64, b: f64, eps: f64) -> bool {
    return (a - b).abs() < eps;
}

pub fn perturbed_ulps_as_int(f: f64, c: i64) -> f64 {
    debug_assert!(!(f == 0.0 && c == -1));
    let mut f_i: i64 = f.to_bits() as i64;
    f_i += c;
    f64::from_bits(f_i as u64)
}

#[inline]
pub fn next(ind: usize, size: usize) -> usize {
    if (ind + 1) < size {
        return ind + 1;
    }
    0
}

#[inline]
pub fn prev(ind: usize, size: usize) -> usize {
    if ind > 0 {
        return ind - 1;
    }
    size - 1
}

#[cfg(test)]
mod test_next_prev {
    use super::*;

    #[test]
    fn test_next() {
        assert_eq!(next(0, 3), 1);
        assert_eq!(next(1, 3), 2);
        assert_eq!(next(2, 3), 0);
        assert_eq!(next(3, 3), 0);
    }

    #[test]
    fn test_prev() {
        assert_eq!(prev(2, 3), 1);
        assert_eq!(prev(1, 3), 0);
        assert_eq!(prev(0, 3), 2);
    }
}

#[cfg(test)]
mod test_almost_equal_as_int {
    use super::*;

    #[test]
    fn test_almost_equal_as_int_nearby() {
        let result: bool = almost_equal_as_int(2.0, 1.999999999999999, 10);
        assert_eq!(result, true);
        let result: bool = almost_equal_as_int(-2.0, -1.999999999999999, 10);
        assert_eq!(result, true);

        let result: bool = almost_equal_as_int(2.0, 1.999999999999998, 10);
        assert_eq!(result, true);
        let result: bool = almost_equal_as_int(-2.0, -1.999999999999998, 10);
        assert_eq!(result, true);

        let result: bool = almost_equal_as_int(1.999999999999998, 2.0, 10);
        assert_eq!(result, true);
        let result: bool = almost_equal_as_int(-1.999999999999998, -2.0, 10);
        assert_eq!(result, true);
    }

    #[test]
    fn test_almost_equal_as_int_distant() {
        let result: bool = almost_equal_as_int(2.0, 1.999999999999997, 10);
        assert_eq!(result, false);
        let result: bool = almost_equal_as_int(-2.0, -1.999999999999997, 10);
        assert_eq!(result, false);

        let result: bool = almost_equal_as_int(1.999999999999997, 2.0, 10);
        assert_eq!(result, false);
        let result: bool = almost_equal_as_int(-1.999999999999997, -2.0, 10);
        assert_eq!(result, false);
    }

    #[test]
    fn test_almost_equal_as_int_limits() {
        let mut f_u: u64 = f64::MAX.to_bits();
        f_u -= 2;
        let f_f = f64::from_bits(f_u);
        let result: bool = almost_equal_as_int(f64::MAX, f_f, 3);
        assert_eq!(result, true);

        let mut f_u: u64 = f64::MAX.to_bits();
        f_u -= 4;
        let f_f = f64::from_bits(f_u);
        let result: bool = almost_equal_as_int(f64::MAX, f_f, 3);
        assert_eq!(result, false);

        let mut f_u: u64 = f64::MIN.to_bits();
        f_u -= 2;
        let f_f = f64::from_bits(f_u);
        let result: bool = almost_equal_as_int(f64::MIN, f_f, 3);
        assert_eq!(result, true);

        let mut f_u: u64 = f64::MIN.to_bits();
        f_u -= 4;
        let f_f = f64::from_bits(f_u);
        let result: bool = almost_equal_as_int(f64::MIN, f_f, 3);
        assert_eq!(result, false);
    }

    #[test]
    fn test_almost_equal_as_int_some_numbers() {
        let result: bool = almost_equal_as_int(100.0, -300.0, 10);
        assert_eq!(result, false);
    }

    #[test]
    fn test_print() {
        print_numbers();
        assert!(true);
    }

    fn print_number(f: f64, o: i64) {
        let mut f_i: i64 = f.to_bits() as i64;
        f_i += o;
        println!("{:.20} Ox{:X} {:.}", f, f_i, f_i);
    }

    pub fn print_numbers() {
        let f: f64 = 2.0f64;
        print_number(f, 0);
        println!("");
        let f: f64 = 1.999999999999998;
        for i in -10..=10i64 {
            print_number(f, i);
        }
        println!("");

        let f: f64 = 0.0;
        print_number(f, 0 as i64);
        let o: i64 = i64::from_ne_bytes(0x8000_0000_0000_0000u64.to_ne_bytes());
        print_number(f, o);
        println!("");

        for i in 0..=3i64 {
            print_number(f, i);
        }
        println!("");

        let c_i: i64 = i64::from_ne_bytes(0x8000_0000_0000_0000u64.to_ne_bytes());
        for i in 0..=3i64 {
            print_number(f, i + c_i);
        }
        println!("");
    }

    #[test]
    fn test_perturbed_ulps_as_int() {
        let t = 1.0;
        let tt = perturbed_ulps_as_int(t, -1);
        let res = almost_equal_as_int(t, tt, 1);

        assert_eq!(res, true);

        let t = 1.0;
        let tt = perturbed_ulps_as_int(t, -1000);
        let res = almost_equal_as_int(t, tt, 1000);
        assert_eq!(res, true);

        let t = f64::MAX;
        let tt = perturbed_ulps_as_int(t, -1000);
        let res = almost_equal_as_int(t, tt, 1000);
        assert_eq!(res, true);

        let t = f64::MAX;
        let tt = perturbed_ulps_as_int(t, -1000000000);
        let res = almost_equal_as_int(t, tt, 1000000000);
        assert_eq!(res, true);
    }

    #[test]
    fn test_positive_negative_zero() {
        assert!(almost_equal_as_int(-0f64, 0f64, 0));
    }
}

#[cfg(test)]
mod test_root {}

#[inline]
pub fn diff_of_prod(a: f64, b: f64, c: f64, d: f64) -> f64 {
    let cd = c * d;
    let err = (-c).mul_add(d, cd);
    let dop = a.mul_add(b, -cd);
    return dop + err;
}
#[inline]
pub fn sum_of_prod(a: f64, b: f64, c: f64, d: f64) -> f64 {
    let cd = c * d;
    let err = c.mul_add(d, -cd);
    let sop = a.mul_add(b, cd);
    return sop + err;
}

#[inline]
pub fn min_5(a: f64, b: f64, c: f64, d: f64, e: f64) -> f64 {
    return a.min(b).min(c).min(d).min(e);
}

#[inline]
pub fn min_4(a: f64, b: f64, c: f64, d: f64) -> f64 {
    return a.min(b).min(c).min(d);
}

#[inline]
pub fn min_3(a: f64, b: f64, c: f64) -> f64 {
    return a.min(b).min(c);
}

pub fn random_arc(wa: f64, wb: f64, ha: f64, hb: f64, b: f64, rng: &mut StdRng) -> Arc {
    let range_w = Uniform::new(wa, wb).unwrap();
    let range_h = Uniform::new(ha, hb).unwrap();
    let bulge = if b != 0.0 {
        let range_b = Uniform::new(-b, b).unwrap();
        range_b.sample(rng)
    } else {
        0.0
    };
    let x0 = range_w.sample(rng);
    let y0 = range_h.sample(rng);
    let x1 = range_w.sample(rng);
    let y1 = range_h.sample(rng);
    arc_circle_parametrization(point(x0, y0), point(x1, y1), bulge)
}

pub fn random_segment(wa: f64, wb: f64, ha: f64, hb: f64, rng: &mut StdRng) -> Segment {
    let range_w = Uniform::new(wa, wb).unwrap();
    let range_h = Uniform::new(ha, hb).unwrap();
    let x0 = range_w.sample(rng);
    let y0 = range_h.sample(rng);
    let x1 = range_w.sample(rng);
    let y1 = range_h.sample(rng);
    segment(point(x0, y0), point(x1, y1))
}

#[cfg(test)]
mod test_diff_of_prod {
    use crate::point::point;

    use super::*;

    const _0: f64 = 0f64;
    const _1: f64 = 0f64;
    const _2: f64 = 0f64;

    #[test]
    fn test_diff_of_prod0() {
        let p0 = point(10000.0, 10000.0);
        let p1 = point(-10001.0, -10000.0);
        let res0 = p0.perp(p1);
        let res1 = diff_of_prod(p0.x, p1.y, p0.y, p1.x);
        assert_eq!(res0, res1);
    }

    #[test]
    fn test_diff_of_prod1() {
        let p0 = point(100000.0, 100000.0);
        let p1 = point(-100001.0, -100000.0);
        let res0 = p0.perp(p1);
        let res1 = diff_of_prod(p0.x, p1.y, p0.y, p1.x);
        assert_eq!(res0, res1);
    }
}