int-interval-set 0.1.0

Integer half-open interval set structures built on top of int-interval.
Documentation
// -----------------------------------------------------------------------------
// @generated by xtask/codegen (--batch --signed)
// DO NOT EDIT MANUALLY.
// Changes will be overwritten.
// -----------------------------------------------------------------------------

use super::*;

mod prop_tests {
    use super::*;
    use proptest::prelude::*;

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

        // ------------------------------------------------------------
        // strategies
        // ------------------------------------------------------------

        prop_compose! {
            fn point_strategy()(
                x in prop_oneof![
                    Just(i128::MIN),
                    Just(i128::MIN.saturating_add(1)),
                    Just(0i128),
                    Just(1i128),
                    Just(2i128),
                    Just(i128::MAX.saturating_sub(2)),
                    Just(i128::MAX.saturating_sub(1)),
                    Just(i128::MAX),
                    any::<i128>(),
                ]
            ) -> i128 {
                x
            }
        }

        prop_compose! {
            fn interval_strategy()(
                a in point_strategy(),
                b in point_strategy(),
            ) -> I128CO {
                let start = a.min(b);
                let end_excl = a.max(b).saturating_add(1);

                // end_excl 至少比 start 大 1;若 a.max(b) == MAX,则 saturating_add(1) == MAX,
                // 这时可能 start == end_excl,需要手动修正成一个合法非空区间。
                if let Some(iv) = I128CO::try_new(start, end_excl) {
                    iv
                } else {
                    // 唯一会落到这里的典型情形是 start == end_excl == MAX。
                    // 构造一个贴近上界的最小非空区间。
                    I128CO::try_new(i128::MAX - 1, i128::MAX).unwrap()
                }
            }
        }

        fn intervals_strategy() -> impl Strategy<Value = Vec<I128CO>> {
            prop::collection::vec(interval_strategy(), 0..48)
        }

        fn probe_points_strategy() -> impl Strategy<Value = Vec<i128>> {
            prop::collection::vec(point_strategy(), 0..96)
        }

        prop_compose! {
            fn interval_array_8_strategy()(
                xs in prop::array::uniform8(interval_strategy())
            ) -> [I128CO; 8] {
                xs
            }
        }

        // ------------------------------------------------------------
        // helpers
        // ------------------------------------------------------------

        #[inline]
        fn raw_contains_point(raw: &[I128CO], x: i128) -> bool {
            raw.iter().any(|iv| iv.contains(x))
        }

        #[inline]
        fn is_strictly_normalized(xs: &[I128CO]) -> bool {
            xs.windows(2).all(|w| {
                let a = w[0];
                let b = w[1];

                a.start() < b.start() && !a.is_contiguous_with(b)
            })
        }

        #[inline]
        fn sum_len(xs: &[I128CO]) -> u128 {
            xs.iter().fold(0u128, |acc, iv| acc.saturating_add(iv.len()))
        }

        fn enrich_probes(raw: &[I128CO], extra: &[i128]) -> Vec<i128> {
            #[inline]
            fn midpoint_i128(a: i128, b: i128) -> i128 {
                (a & b) + ((a ^ b) >> 1)
            }

            let mut out = Vec::with_capacity(extra.len() + raw.len() * 6 + 6);

            out.push(i128::MIN);
            out.push(i128::MIN.saturating_add(1));
            out.push(0);
            out.push(1);
            out.push(i128::MAX.saturating_sub(1));
            out.push(i128::MAX);

            out.extend_from_slice(extra);

            for &iv in raw {
                let s = iv.start();
                let e = iv.end_excl();
                let ei = iv.end_incl();

                out.push(s);
                out.push(ei);
                out.push(e);

                out.push(s.saturating_sub(1));
                out.push(ei.saturating_add(1));

                out.push(midpoint_i128(s, ei));
            }

            out.sort_unstable();
            out.dedup();
            out
        }

        // ------------------------------------------------------------
        // deterministic smoke tests
        // ------------------------------------------------------------

        #[test]
        fn empty_input_builds_empty_set() {
            let set = I128COBatchSet::default();
            assert!(set.as_slice().is_empty());
            assert_eq!(set.interval_count(), 0);
            assert_eq!(set.point_count(), 0);

            let set = I128COBatchSet::from(Vec::<I128CO>::new());
            assert!(set.as_slice().is_empty());
            assert_eq!(set.interval_count(), 0);
            assert_eq!(set.point_count(), 0);

            let set: I128COBatchSet = core::iter::empty::<I128CO>().collect();
            assert!(set.as_slice().is_empty());
            assert_eq!(set.interval_count(), 0);
            assert_eq!(set.point_count(), 0);
        }

        // ------------------------------------------------------------
        // main properties
        // ------------------------------------------------------------

        proptest! {
            #[test]
            fn prop_vec_constructor_normalizes_structure_and_preserves_point_membership(
                raw in intervals_strategy(),
                extra_probes in probe_points_strategy(),
            ) {
                let set = I128COBatchSet::from(raw.clone());

                prop_assert!(is_strictly_normalized(set.as_slice()));

                let probes = enrich_probes(&raw, &extra_probes);
                for x in probes {
                    prop_assert_eq!(
                        set.contains_point(x),
                        raw_contains_point(&raw, x),
                        "membership mismatch at point {:?}, raw={:?}, set={:?}",
                        x,
                        raw,
                        set,
                    );
                }
            }

            #[test]
            fn prop_vec_constructor_is_order_invariant(
                raw in intervals_strategy(),
            ) {
                let mut sorted = raw.clone();
                sorted.sort_unstable_by(|a, b| {
                    a.start()
                        .cmp(&b.start())
                        .then(a.end_excl().cmp(&b.end_excl()))
                });

                let a = I128COBatchSet::from(raw);
                let b = I128COBatchSet::from(sorted);

                prop_assert_eq!(a, b);
            }

            #[test]
            fn prop_interval_count_matches_internal_slice_len(
                raw in intervals_strategy(),
            ) {
                let set = I128COBatchSet::from(raw);

                prop_assert_eq!(set.interval_count(), set.as_slice().len() as i128);
            }

            #[test]
            fn prop_point_count_matches_sum_of_normalized_interval_lengths(
                raw in intervals_strategy(),
            ) {
                let set = I128COBatchSet::from(raw);

                prop_assert_eq!(set.point_count(), sum_len(set.as_slice()));
            }

            #[test]
            fn prop_from_iter_matches_vec_constructor(
                raw in intervals_strategy(),
            ) {
                let a = I128COBatchSet::from(raw.clone());
                let b: I128COBatchSet = raw.into_iter().collect();

                prop_assert_eq!(a, b);
            }

            #[test]
            fn prop_array_constructor_matches_vec_constructor(
                raw in interval_array_8_strategy(),
            ) {
                let a = I128COBatchSet::from(raw);
                let b = I128COBatchSet::from(Vec::from(raw));

                prop_assert_eq!(a, b);
            }
        }
    }
}