Skip to main content

oxihuman_core/
range_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Map of non-overlapping f32 ranges to values.
6
7/// A half-open interval [lo, hi).
8#[derive(Debug, Clone, PartialEq)]
9pub struct RangeEntry<V> {
10    pub lo: f32,
11    pub hi: f32,
12    pub value: V,
13}
14
15/// Ordered map of non-overlapping ranges.
16pub struct RangeMap<V> {
17    entries: Vec<RangeEntry<V>>,
18}
19
20#[allow(dead_code)]
21impl<V: Clone> RangeMap<V> {
22    pub fn new() -> Self {
23        RangeMap {
24            entries: Vec::new(),
25        }
26    }
27
28    pub fn insert(&mut self, lo: f32, hi: f32, value: V) -> bool {
29        if lo >= hi {
30            return false;
31        }
32        if self.entries.iter().any(|e| lo < e.hi && hi > e.lo) {
33            return false;
34        }
35        let pos = self.entries.partition_point(|e| e.lo < lo);
36        self.entries.insert(pos, RangeEntry { lo, hi, value });
37        true
38    }
39
40    pub fn query(&self, point: f32) -> Option<&V> {
41        self.entries
42            .iter()
43            .find(|e| (e.lo..e.hi).contains(&point))
44            .map(|e| &e.value)
45    }
46
47    pub fn remove_containing(&mut self, point: f32) -> bool {
48        if let Some(idx) = self
49            .entries
50            .iter()
51            .position(|e| (e.lo..e.hi).contains(&point))
52        {
53            self.entries.remove(idx);
54            true
55        } else {
56            false
57        }
58    }
59
60    pub fn len(&self) -> usize {
61        self.entries.len()
62    }
63
64    pub fn is_empty(&self) -> bool {
65        self.entries.is_empty()
66    }
67
68    pub fn clear(&mut self) {
69        self.entries.clear();
70    }
71
72    pub fn overlaps(&self, lo: f32, hi: f32) -> Vec<&RangeEntry<V>> {
73        self.entries
74            .iter()
75            .filter(|e| lo < e.hi && hi > e.lo)
76            .collect()
77    }
78
79    pub fn total_coverage(&self) -> f32 {
80        self.entries.iter().map(|e| e.hi - e.lo).sum()
81    }
82
83    pub fn ranges(&self) -> Vec<(f32, f32)> {
84        self.entries.iter().map(|e| (e.lo, e.hi)).collect()
85    }
86
87    pub fn contains_point(&self, point: f32) -> bool {
88        self.query(point).is_some()
89    }
90}
91
92impl<V: Clone> Default for RangeMap<V> {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98pub fn new_range_map<V: Clone>() -> RangeMap<V> {
99    RangeMap::new()
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use std::f32::consts::PI;
106
107    #[test]
108    fn insert_and_query() {
109        let mut m: RangeMap<&str> = new_range_map();
110        assert!(m.insert(0.0, 1.0, "first"));
111        assert_eq!(m.query(0.5), Some(&"first"));
112    }
113
114    #[test]
115    fn query_boundary_exclusive_hi() {
116        let mut m: RangeMap<i32> = new_range_map();
117        m.insert(0.0, 1.0, 42);
118        assert!(m.query(1.0).is_none());
119    }
120
121    #[test]
122    fn no_overlapping_insert() {
123        let mut m: RangeMap<i32> = new_range_map();
124        m.insert(0.0, 2.0, 1);
125        assert!(!m.insert(1.0, 3.0, 2));
126        assert_eq!(m.len(), 1);
127    }
128
129    #[test]
130    fn multiple_ranges() {
131        let mut m: RangeMap<i32> = new_range_map();
132        m.insert(0.0, 1.0, 1);
133        m.insert(1.0, 2.0, 2);
134        assert_eq!(m.query(0.5), Some(&1));
135        assert_eq!(m.query(1.5), Some(&2));
136    }
137
138    #[test]
139    fn remove_containing() {
140        let mut m: RangeMap<i32> = new_range_map();
141        m.insert(0.0, PI, 99);
142        assert!(m.remove_containing(1.0));
143        assert!(m.is_empty());
144    }
145
146    #[test]
147    fn total_coverage() {
148        let mut m: RangeMap<i32> = new_range_map();
149        m.insert(0.0, 1.0, 1);
150        m.insert(2.0, 5.0, 2);
151        assert!((m.total_coverage() - 4.0).abs() < 1e-6);
152    }
153
154    #[test]
155    fn overlaps_query() {
156        let mut m: RangeMap<i32> = new_range_map();
157        m.insert(0.0, 2.0, 1);
158        m.insert(5.0, 8.0, 2);
159        let ov = m.overlaps(1.0, 3.0);
160        assert_eq!(ov.len(), 1);
161    }
162
163    #[test]
164    fn contains_point() {
165        let mut m: RangeMap<i32> = new_range_map();
166        m.insert(0.0, 1.0, 1);
167        assert!(m.contains_point(0.5));
168        assert!(!m.contains_point(1.5));
169    }
170
171    #[test]
172    fn invalid_range_rejected() {
173        let mut m: RangeMap<i32> = new_range_map();
174        assert!(!m.insert(1.0, 0.5, 1));
175        assert!(m.is_empty());
176    }
177}