int-interval-set 0.3.4

Integer half-open interval set structures built on top of int-interval.
Documentation
use proptest::prelude::*;

use crate::{
    I8COSet,
    int_co_set::test_support::{arb_iv, build, iv},
};

#[test]
fn empty_set_has_empty_shape() {
    let set = build([]);

    assert!(set.is_empty());
    assert_eq!(set.interval_count(), 0);
    assert_eq!(set.as_slice(), &[]);
    assert_eq!(set.iter_intervals().collect::<Vec<_>>(), Vec::new());
}

#[test]
fn interval_count_counts_canonical_intervals() {
    let set = build([(-10, -5), (-5, 0), (10, 20), (30, 40)]);

    assert_eq!(set.interval_count(), 3);
    assert_eq!(set.as_slice(), &[iv(-10, 0), iv(10, 20), iv(30, 40)]);
}

#[test]
fn as_slice_returns_sorted_canonical_slice() {
    let set = build([(30, 40), (-20, -10), (-12, 0), (50, 60)]);

    assert_eq!(set.as_slice(), &[iv(-20, 0), iv(30, 40), iv(50, 60)]);
}

#[test]
fn iter_intervals_yields_canonical_intervals_in_order() {
    let set = build([(10, 20), (-10, -5), (-5, -2), (30, 40)]);

    assert_eq!(
        set.iter_intervals().collect::<Vec<_>>(),
        vec![iv(-10, -2), iv(10, 20), iv(30, 40)]
    );
}

#[test]
fn clone_preserves_logical_content() {
    let set = build([(-10, 0), (20, 30)]);
    let cloned = set.clone();

    assert_eq!(cloned.as_slice(), set.as_slice());
    assert_eq!(cloned.interval_count(), set.interval_count());
    assert_eq!(cloned.is_empty(), set.is_empty());
}

#[test]
fn sparse_intervals_remain_sorted_and_canonical_at_domain_edges() {
    let set = build([
        (i8::MIN, i8::MIN + 1),
        (i8::MIN + 2, i8::MIN + 3),
        (-2, -1),
        (-1, 1),
        (10, 20),
        (i8::MAX - 1, i8::MAX),
    ]);

    assert_eq!(set.interval_count(), 5);
    assert_eq!(
        set.as_slice(),
        &[
            iv(i8::MIN, i8::MIN + 1),
            iv(i8::MIN + 2, i8::MIN + 3),
            iv(-2, 1),
            iv(10, 20),
            iv(i8::MAX - 1, i8::MAX),
        ]
    );
}

#[test]
fn is_empty_is_false_after_any_interval() {
    let set = build([(-1, 1)]);

    assert!(!set.is_empty());
    assert_eq!(set.interval_count(), 1);
    assert_eq!(set.as_slice(), &[iv(-1, 1)]);
}

proptest! {
    #[test]
    fn prop_accessors_are_consistent(xs in prop::collection::vec(arb_iv(), 0..64)) {
        let set: I8COSet = xs.into_iter().collect();
        let iterated = set.iter_intervals().collect::<Vec<_>>();

        prop_assert_eq!(iterated.as_slice(), set.as_slice());
        prop_assert_eq!(set.interval_count(), set.as_slice().len());
        prop_assert_eq!(set.is_empty(), set.as_slice().is_empty());
    }

    #[test]
    fn prop_clone_preserves_accessors(xs in prop::collection::vec(arb_iv(), 0..64)) {
        let set: I8COSet = xs.into_iter().collect();
        let cloned = set.clone();

        prop_assert_eq!(cloned.as_slice(), set.as_slice());
        prop_assert_eq!(
            cloned.iter_intervals().collect::<Vec<_>>(),
            set.iter_intervals().collect::<Vec<_>>()
        );
        prop_assert_eq!(cloned.interval_count(), set.interval_count());
        prop_assert_eq!(cloned.is_empty(), set.is_empty());
    }
}