oxihuman_core/
range_map.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq)]
9pub struct RangeEntry<V> {
10 pub lo: f32,
11 pub hi: f32,
12 pub value: V,
13}
14
15pub 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}