int-interval 0.8.1

A small, no_std half-open interval algebra library for primitive integer types.
Documentation
// -----------------------------------------------------------------------------
// @generated by xtask/codegen (signed)
// DO NOT EDIT MANUALLY.
// Changes will be overwritten.
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// @generated by xtask/codegen
// DO NOT EDIT MANUALLY.
// Changes will be overwritten.
// -----------------------------------------------------------------------------

use super::*;

mod unit_tests {
    use super::*;

    fn span(lo: i64, hi: i64) -> I64CO {
        I64CO::try_new(lo, hi).unwrap()
    }

    #[test]
    fn no_overlap() {
        let a = span(1, 4);
        let b = span(6, 8);

        match a.difference(b) {
            ZeroOneTwo::One(x) => assert_eq!(x, a),
            _ => panic!(),
        }
    }

    #[test]
    fn cut_middle() {
        let a = span(1, 10);
        let b = span(4, 6);

        match a.difference(b) {
            ZeroOneTwo::Two(l, r) => {
                assert_eq!(l, span(1, 4));
                assert_eq!(r, span(6, 10));
            }
            _ => panic!(),
        }
    }

    #[test]
    fn cover_all() {
        let a = span(3, 6);
        let b = span(1, 10);

        match a.difference(b) {
            ZeroOneTwo::Zero => {}
            _ => panic!(),
        }
    }

    #[test]
    fn identical_intervals() {
        let a = span(3, 6);

        match a.difference(a) {
            ZeroOneTwo::Zero => {}
            _ => panic!(),
        }
    }
}

mod prop_tests {
    use std::{vec, vec::Vec};

    use super::*;
    use proptest::prelude::*;

    fn span(a: i64, b: i64) -> Option<I64CO> {
        let lo = a.min(b);
        let hi = a.max(b);
        I64CO::try_new(lo, hi)
    }

    fn edge_values() -> Vec<i64> {
        let mut v = vec![i64::MIN, i64::MAX, 0, 1];

        if i64::MIN < i64::MAX {
            v.push(i64::MIN.saturating_add(1));
            v.push(i64::MAX.saturating_sub(1));
        }

        v.sort_unstable();
        v.dedup();
        v
    }

    fn edge_scalar() -> impl Strategy<Value = i64> {
        prop::sample::select(edge_values())
    }

    fn mixed_scalar() -> impl Strategy<Value = i64> {
        prop_oneof![
            3 => edge_scalar(),
            7 => any::<i64>(),
        ]
    }

    fn span_strategy() -> impl Strategy<Value = I64CO> {
        (mixed_scalar(), mixed_scalar()).prop_filter_map("non-empty span", |(a, b)| span(a, b))
    }

    proptest! {
        #![proptest_config(ProptestConfig {
            cases: 64,
            .. ProptestConfig::default()
        })]

        #[test]
        fn difference_points_are_in_x_and_not_in_y(
            x in span_strategy(),
            y in span_strategy(),
            p in mixed_scalar(),
        ) {
            let in_diff = match x.difference(y) {
                ZeroOneTwo::Zero => false,
                ZeroOneTwo::One(z) => z.contains(p),
                ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
            };

            prop_assert_eq!(in_diff, x.contains(p) && !y.contains(p));
        }

        #[test]
        fn difference_two_parts_do_not_overlap(
            x in span_strategy(),
            y in span_strategy(),
        ) {
            if let ZeroOneTwo::Two(l, r) = x.difference(y) {
                prop_assert!(!l.intersects(r));
                prop_assert!(!r.intersects(l));
            }
        }

        #[test]
        fn difference_is_subset_of_x(
            x in span_strategy(),
            y in span_strategy(),
            p in mixed_scalar(),
        ) {
            let in_diff = match x.difference(y) {
                ZeroOneTwo::Zero => false,
                ZeroOneTwo::One(z) => z.contains(p),
                ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
            };

            if in_diff {
                prop_assert!(x.contains(p));
            }
        }

        #[test]
        fn difference_excludes_y(
            x in span_strategy(),
            y in span_strategy(),
            p in mixed_scalar(),
        ) {
            let in_diff = match x.difference(y) {
                ZeroOneTwo::Zero => false,
                ZeroOneTwo::One(z) => z.contains(p),
                ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
            };

            if in_diff {
                prop_assert!(!y.contains(p));
            }
        }

        #[test]
        fn difference_shape_matches_math(
            x in span_strategy(),
            y in span_strategy(),
        ) {
            match x.difference(y) {

                ZeroOneTwo::Zero => {
                    prop_assert!(y.start() <= x.start() && y.end_excl() >= x.end_excl());
                }

                ZeroOneTwo::One(z) => {
                    prop_assert!(z.start() >= x.start());
                    prop_assert!(z.end_excl() <= x.end_excl());
                }

                ZeroOneTwo::Two(l, r) => {
                    prop_assert!(l.end_excl() <= r.start());
                    prop_assert!(l.start() >= x.start());
                    prop_assert!(r.end_excl() <= x.end_excl());
                }
            }
        }
    }
}