]>
Commit | Line | Data |
---|---|---|
bd6521f0 A |
1 | // |
2 | // UtilTRange.hpp | |
3 | // CPPUtil | |
4 | // | |
5 | // Created by James McIlree on 4/14/13. | |
6 | // Copyright (c) 2013 Apple. All rights reserved. | |
7 | // | |
8 | ||
9 | #ifndef CPPUtil_UtilTRange_hpp | |
10 | #define CPPUtil_UtilTRange_hpp | |
11 | ||
12 | template <typename T> | |
13 | class TRange { | |
14 | protected: | |
15 | T _location; | |
16 | T _length; | |
17 | ||
18 | public: | |
19 | TRange() : _location(0), _length(0) {} | |
20 | TRange(T location, T length) : _location(location), _length(length) { | |
21 | DEBUG_ONLY(validate()); | |
22 | }; | |
23 | ||
24 | bool operator==(const TRange &rhs) const { return this->_location == rhs.location() && this->_length == rhs.length(); } | |
25 | ||
26 | bool operator!=(const TRange &rhs) const { return !(*this == rhs); } | |
27 | ||
28 | bool operator<(const TRange& rhs) const { return this->_location < rhs.location(); } | |
29 | bool operator<(const TRange* rhs) const { return this->_location < rhs->location(); } | |
30 | ||
31 | const T location() const { return _location; } | |
32 | const T length() const { return _length; } | |
33 | const T max() const { return _location + _length; } | |
34 | ||
35 | void set_location(T location) { _location = location; DEBUG_ONLY(validate()); } | |
36 | void set_length(T length) { _length = length; DEBUG_ONLY(validate()); } | |
37 | void set_max(T max) { ASSERT(max >= _location, "Sanity"); _length = max - _location; } | |
38 | ||
39 | const bool contains(const TRange& other) const { return (other.location() >= location()) && (other.max() <= max()); } | |
40 | const bool contains(const T loc) const { return loc - _location < _length; } // Assumes unsigned! | |
41 | ||
42 | const bool intersects(const TRange& o) const { return this->location() < o.max() && o.location() < this->max(); } | |
43 | ||
44 | // "union" is a keyword :-( | |
45 | TRange union_range(const TRange& other) const { | |
46 | T maxend = (this->max() > other.max()) ? this->max() : other.max(); | |
47 | T minloc = this->location() < other.location() ? this->location() : other.location(); | |
48 | return TRange(minloc, maxend - minloc); | |
49 | } | |
50 | ||
51 | TRange intersection_range(const TRange& other) const { | |
52 | if (this->intersects(other)) { | |
53 | auto intersection_start = std::max(_location, other.location()); | |
54 | auto intersection_end = std::min(max(), other.max()); | |
55 | return TRange(intersection_start, intersection_end - intersection_start); | |
56 | } | |
57 | ||
58 | return TRange(T(0),T(0)); | |
59 | } | |
60 | ||
61 | void validate() const { ASSERT((_location + _length >= _location) /*|| (_location + 1 == 0)*/, "range must not wrap"); } | |
62 | }; | |
63 | ||
64 | template <typename TRANGE> | |
65 | bool is_trange_vector_sorted_and_non_overlapping(const std::vector<TRANGE>& vec) { | |
66 | if (vec.size() > 1) { | |
67 | auto last_it = vec.begin(); | |
68 | auto it = last_it + 1; | |
69 | ||
70 | while (it < vec.end()) { | |
71 | if (it < last_it) | |
72 | return false; | |
73 | ||
74 | if (last_it->intersects(*it)) | |
75 | return false; | |
76 | ||
77 | last_it = it; | |
78 | it++; | |
79 | } | |
80 | } | |
81 | return true; | |
82 | } | |
83 | ||
84 | template <typename TRANGE> | |
85 | bool is_trange_vector_sorted(const std::vector<TRANGE>& vec) { | |
86 | if (vec.size() > 1) { | |
87 | auto last_it = vec.begin(); | |
88 | auto it = last_it + 1; | |
89 | ||
90 | while (it < vec.end()) { | |
91 | if (it < last_it) | |
92 | return false; | |
93 | ||
94 | last_it = it; | |
95 | it++; | |
96 | } | |
97 | } | |
98 | return true; | |
99 | } | |
100 | ||
101 | // NOTE! | |
102 | // | |
103 | // This produces an output vector with the | |
104 | // intervals "flattened". | |
105 | // | |
106 | // IOW, this: | |
107 | // | |
108 | // vec1: XXXXXXXX AAAAAAAAAA | |
109 | // YYYYYYYYYYY ZZZZZZZZZ | |
110 | // | |
111 | // becomes: | |
112 | // | |
113 | // res: IIIIIIIIIIII IIIIIIIIIIII | |
114 | // | |
115 | // The input vector should be sorted. | |
116 | // | |
117 | template <typename TRANGE> | |
118 | std::vector<TRANGE> trange_vector_union(std::vector<TRANGE>& input) { | |
119 | std::vector<TRANGE> union_vec; | |
120 | ||
121 | ASSERT(is_trange_vector_sorted(input), "Sanity"); | |
122 | ||
123 | if (!input.empty()) { | |
124 | auto input_it = input.begin(); | |
125 | union_vec.push_back(*input_it); | |
126 | while (++input_it < input.end()) { | |
127 | TRANGE union_range = union_vec.back(); | |
128 | ||
129 | if (union_range.intersects(*input_it)) { | |
130 | union_vec.pop_back(); | |
131 | union_vec.push_back(union_range.union_range(*input_it)); | |
132 | } else { | |
133 | ASSERT(union_range < *input_it, "Out of order merging"); | |
134 | union_vec.push_back(*input_it); | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | ASSERT(is_trange_vector_sorted_and_non_overlapping(union_vec), "union'd vector fails invariant"); | |
140 | ||
141 | return union_vec; | |
142 | } | |
143 | ||
144 | // NOTE! | |
145 | // | |
146 | // This will coalesce intervals that intersect. | |
147 | // | |
148 | // IOW, given two input vectors: | |
149 | // | |
150 | // vec1: XXXX XXXX | |
151 | // vec2: XXX | |
152 | // | |
153 | // res: XXXX XXX XXXX | |
154 | // | |
155 | // -------------------------------- | |
156 | // | |
157 | // vec1: XXXX XX | |
158 | // vec2: XXXXXXXXXXXXXXX | |
159 | // | |
160 | // res: XXXXXXXXXXXXXXXXX | |
161 | ||
162 | template <typename TRANGE> | |
163 | std::vector<TRANGE> trange_vector_union(std::vector<TRANGE>& vec1, std::vector<TRANGE>& vec2) { | |
164 | std::vector<TRANGE> union_vec; | |
165 | ||
166 | ASSERT(is_trange_vector_sorted_and_non_overlapping(vec1), "input vector violates invariants"); | |
167 | ASSERT(is_trange_vector_sorted_and_non_overlapping(vec2), "input vector violates invariants"); | |
168 | ||
169 | // while (not done) | |
170 | // select next interval (lowest location) | |
171 | // if intersects with last union_vec entry, union, pop_back, push_back | |
172 | // else push_back | |
173 | ||
174 | auto vec1_it = vec1.begin(); | |
175 | auto vec2_it = vec2.begin(); | |
176 | ||
177 | while (uint32_t chose_vector = (((vec1_it != vec1.end()) ? 1 : 0) + ((vec2_it != vec2.end()) ? 2 : 0))) { | |
178 | // | |
179 | // This is a fancy "chose" algorithm | |
180 | // | |
181 | // vec1 == bit 1 | |
182 | // vec2 == bit 2 | |
183 | // | |
184 | decltype(vec1_it) merge_it; | |
185 | switch (chose_vector) { | |
186 | case 1: | |
187 | merge_it = vec1_it++; | |
188 | break; | |
189 | ||
190 | case 2: | |
191 | merge_it = vec2_it++; | |
192 | break; | |
193 | ||
194 | case 3: | |
195 | merge_it = (*vec1_it < * vec2_it) ? vec1_it++ : vec2_it++; | |
196 | break; | |
197 | ||
198 | default: | |
199 | ASSERT(false, "ShouldNotReachHere"); | |
200 | return std::vector<TRANGE>(); | |
201 | } | |
202 | ||
203 | if (union_vec.empty()) { | |
204 | union_vec.push_back(*merge_it); | |
205 | } else { | |
206 | TRANGE last_range = union_vec.back(); | |
207 | ||
208 | if (last_range.intersects(*merge_it)) { | |
209 | union_vec.pop_back(); | |
210 | union_vec.push_back(last_range.union_range(*merge_it)); | |
211 | } else { | |
212 | ASSERT(last_range < *merge_it, "Out of order merging"); | |
213 | union_vec.push_back(*merge_it); | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | ASSERT(is_trange_vector_sorted_and_non_overlapping(union_vec), "union'd vector fails invariant"); | |
219 | ||
220 | return union_vec; | |
221 | } | |
222 | ||
223 | template <typename TRANGE> | |
224 | std::vector<TRANGE> trange_vector_intersect(std::vector<TRANGE>& vec1, std::vector<TRANGE>& vec2) { | |
225 | std::vector<TRANGE> intersect_vec; | |
226 | ||
227 | ASSERT(is_trange_vector_sorted_and_non_overlapping(vec1), "input vector violates invariants"); | |
228 | ASSERT(is_trange_vector_sorted_and_non_overlapping(vec2), "input vector violates invariants"); | |
229 | ||
230 | auto vec1_it = vec1.begin(); | |
231 | auto vec2_it = vec2.begin(); | |
232 | ||
233 | // As soon as one vector empties, there can be no more intersections | |
234 | while (vec1_it != vec1.end() && vec2_it != vec2.end()) { | |
235 | TRANGE temp = vec1_it->intersection_range(*vec2_it); | |
236 | if (temp.length() > 0) { | |
237 | intersect_vec.push_back(temp); | |
238 | } | |
239 | ||
240 | // We keep the interval that ends last | |
241 | ||
242 | if (vec1_it->max() > vec2_it->max()) { | |
243 | vec2_it++; | |
244 | } else { | |
245 | vec1_it++; | |
246 | } | |
247 | } | |
248 | ||
249 | ASSERT(is_trange_vector_sorted_and_non_overlapping(intersect_vec), "intersection vector fails invariant"); | |
250 | ||
251 | return intersect_vec; | |
252 | } | |
253 | ||
254 | #endif |