int-interval-set 0.1.0

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

use super::*;

mod unit_tests {
    use super::*;

    #[inline]
    fn iv(start: usize, end_excl: usize) -> UsizeCO {
        UsizeCO::try_new(start, end_excl).unwrap()
    }

    mod interval_count_tests {
        use super::*;

        #[test]
        fn empty_set() {
            let set = UsizeCOBatchSet::default();
            assert_eq!(set.interval_count(), 0);
        }

        #[test]
        fn single_interval() {
            let set = UsizeCOBatchSet::from([iv(3, 7)]);
            assert_eq!(set.interval_count(), 1);
        }

        #[test]
        fn multi_interval() {
            let set = UsizeCOBatchSet::from([iv(1, 3), iv(5, 8), iv(10, 11)]);
            assert_eq!(set.interval_count(), 3);
        }

        #[test]
        fn normalized_count_after_merge() {
            let set = UsizeCOBatchSet::from([iv(5, 7), iv(1, 3), iv(3, 5), iv(10, 12)]);
            assert_eq!(set.interval_count(), 2);
        }
    }

    mod point_count_tests {
        use super::*;

        #[test]
        fn empty_set() {
            let set = UsizeCOBatchSet::default();
            assert_eq!(set.point_count(), 0);
        }

        #[test]
        fn single_interval() {
            let set = UsizeCOBatchSet::from([iv(3, 7)]);
            assert_eq!(set.point_count(), 4);
        }

        #[test]
        fn multi_interval() {
            let set = UsizeCOBatchSet::from([iv(1, 3), iv(5, 8), iv(10, 11)]);
            assert_eq!(set.point_count(), 2 + 3 + 1);
        }

        #[test]
        fn normalized_point_count_after_merge() {
            let set = UsizeCOBatchSet::from([iv(5, 7), iv(1, 3), iv(3, 5), iv(10, 12)]);
            assert_eq!(set.point_count(), 6 + 2);
        }
    }

    mod coverage_len_of_tests {
        use super::*;

        #[test]
        fn empty_set() {
            let set = UsizeCOBatchSet::default();

            assert_eq!(set.coverage_len_of(iv(0, 1)), 0);
            assert_eq!(set.coverage_len_of(iv(10, 20)), 0);
        }

        #[test]
        fn query_before_all_intervals() {
            let set = UsizeCOBatchSet::from([iv(5, 8), iv(10, 12)]);
            assert_eq!(set.coverage_len_of(iv(0, 4)), 0);
        }

        #[test]
        fn query_after_all_intervals() {
            let set = UsizeCOBatchSet::from([iv(1, 3), iv(5, 8)]);
            assert_eq!(set.coverage_len_of(iv(10, 20)), 0);
        }

        #[test]
        fn single_interval_basic() {
            let set = UsizeCOBatchSet::from([iv(5, 10)]);

            assert_eq!(set.coverage_len_of(iv(0, 5)), 0);
            assert_eq!(set.coverage_len_of(iv(0, 6)), 1);
            assert_eq!(set.coverage_len_of(iv(5, 6)), 1);
            assert_eq!(set.coverage_len_of(iv(6, 9)), 3);
            assert_eq!(set.coverage_len_of(iv(9, 12)), 1);
            assert_eq!(set.coverage_len_of(iv(10, 12)), 0);
            assert_eq!(set.coverage_len_of(iv(0, 20)), 5);
        }

        #[test]
        fn multi_interval_with_gaps() {
            let set = UsizeCOBatchSet::from([iv(1, 3), iv(5, 7), iv(9, 11)]);

            assert_eq!(set.coverage_len_of(iv(0, 12)), 2 + 2 + 2);
            assert_eq!(set.coverage_len_of(iv(2, 10)), 1 + 2 + 1);
            assert_eq!(set.coverage_len_of(iv(3, 5)), 0);
            assert_eq!(set.coverage_len_of(iv(3, 6)), 1);
            assert_eq!(set.coverage_len_of(iv(7, 9)), 0);
            assert_eq!(set.coverage_len_of(iv(8, 10)), 1);
        }

        #[test]
        fn query_spans_multiple_segments() {
            let set = UsizeCOBatchSet::from([iv(2, 4), iv(7, 9), iv(12, 15)]);

            assert_eq!(set.coverage_len_of(iv(3, 13)), 1 + 2 + 1);
            assert_eq!(set.coverage_len_of(iv(0, 20)), 2 + 2 + 3);
        }

        #[test]
        fn normalized_set_coverage() {
            let set = UsizeCOBatchSet::from([iv(5, 7), iv(1, 3), iv(3, 5), iv(10, 12)]);

            // normalize 后: [1,7), [10,12)
            assert_eq!(set.coverage_len_of(iv(0, 20)), 6 + 2);
            assert_eq!(set.coverage_len_of(iv(2, 6)), 4);
            assert_eq!(set.coverage_len_of(iv(6, 10)), 1);
            assert_eq!(set.coverage_len_of(iv(7, 10)), 0);
            assert_eq!(set.coverage_len_of(iv(6, 11)), 1 + 1);
        }

        #[test]
        fn matches_pointwise_semantics() {
            let set = UsizeCOBatchSet::from([iv(1, 4), iv(6, 9), iv(20, 30)]);
            let queries = [
                iv(0, 1),
                iv(0, 2),
                iv(3, 6),
                iv(4, 6),
                iv(8, 10),
                iv(10, 20),
                iv(19, 31),
            ];

            for q in queries {
                let expect = q.iter().filter(|&x| set.contains_point(x)).count() as usize;
                assert_eq!(set.coverage_len_of(q), expect, "q={q:?}");
            }
        }
    }

    mod coverage_ratio_of_tests {
        use super::*;

        #[inline]
        fn assert_f32_eq(a: f32, b: f32) {
            assert!((a - b).abs() <= 1e-6, "left={a}, right={b}");
        }

        #[test]
        fn empty_set() {
            let set = UsizeCOBatchSet::default();

            assert_f32_eq(set.coverage_ratio_of(iv(0, 1)), 0.0);
            assert_f32_eq(set.coverage_ratio_of(iv(10, 20)), 0.0);
        }

        #[test]
        fn fully_covered_query() {
            let set = UsizeCOBatchSet::from([iv(3, 9)]);

            assert_f32_eq(set.coverage_ratio_of(iv(3, 9)), 1.0);
            assert_f32_eq(set.coverage_ratio_of(iv(4, 8)), 1.0);
        }

        #[test]
        fn partially_covered_query() {
            let set = UsizeCOBatchSet::from([iv(5, 10)]);

            assert_f32_eq(set.coverage_ratio_of(iv(0, 6)), 1.0 / 6.0);
            assert_f32_eq(set.coverage_ratio_of(iv(6, 9)), 1.0);
            assert_f32_eq(set.coverage_ratio_of(iv(9, 12)), 1.0 / 3.0);
            assert_f32_eq(set.coverage_ratio_of(iv(0, 20)), 5.0 / 20.0);
        }

        #[test]
        fn multi_interval_with_gaps() {
            let set = UsizeCOBatchSet::from([iv(1, 3), iv(5, 7), iv(9, 11)]);

            assert_f32_eq(set.coverage_ratio_of(iv(0, 12)), 6.0 / 12.0);
            assert_f32_eq(set.coverage_ratio_of(iv(2, 10)), 4.0 / 8.0);
            assert_f32_eq(set.coverage_ratio_of(iv(3, 5)), 0.0);
            assert_f32_eq(set.coverage_ratio_of(iv(3, 6)), 1.0 / 3.0);
        }

        #[test]
        fn normalized_set_ratio() {
            let set = UsizeCOBatchSet::from([iv(5, 7), iv(1, 3), iv(3, 5), iv(10, 12)]);

            assert_f32_eq(set.coverage_ratio_of(iv(0, 20)), 8.0 / 20.0);
            assert_f32_eq(set.coverage_ratio_of(iv(2, 6)), 1.0);
            assert_f32_eq(set.coverage_ratio_of(iv(7, 10)), 0.0);
            assert_f32_eq(set.coverage_ratio_of(iv(6, 11)), 2.0 / 5.0);
        }

        #[test]
        fn ratio_matches_len_div_query_len() {
            let set = UsizeCOBatchSet::from([iv(1, 4), iv(6, 9), iv(20, 30)]);
            let queries = [iv(1, 2), iv(0, 10), iv(3, 8), iv(10, 20), iv(25, 31)];

            for q in queries {
                let expect = set.coverage_len_of(q) as f32 / q.len() as f32;
                assert_f32_eq(set.coverage_ratio_of(q), expect);
            }
        }
    }

    mod normalized_statistics_tests {
        use super::*;

        #[test]
        fn normalized_set_statistics_are_consistent() {
            let set = UsizeCOBatchSet::from([iv(8, 10), iv(1, 3), iv(3, 5), iv(5, 8), iv(20, 21)]);

            // normalize 后: [1,10), [20,21)
            assert_eq!(set.interval_count(), 2);
            assert_eq!(set.point_count(), 9 + 1);

            assert_eq!(set.coverage_len_of(iv(0, 30)), 10);
            assert_eq!(set.coverage_len_of(iv(2, 9)), 7);
            assert_eq!(set.coverage_len_of(iv(10, 20)), 0);

            let ratio = set.coverage_ratio_of(iv(0, 30));
            assert!((ratio - 10.0 / 30.0).abs() <= 1e-6);
        }
    }
}