Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DisjointRange.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5namespace Stroika::Foundation::Traversal {
6
7 /*
8 ********************************************************************************
9 ********************** DisjointRange<T, RANGE_TYPE> ****************************
10 ********************************************************************************
11 */
12 template <typename T, typename RANGE_TYPE>
14 {
15 MergeIn_ (from);
16 }
17 template <typename T, typename RANGE_TYPE>
18 inline DisjointRange<T, RANGE_TYPE>::DisjointRange (const initializer_list<RangeType>& from)
19 : DisjointRange{begin (from), end (from)}
20 {
21 }
22 template <typename T, typename RANGE_TYPE>
23 template <typename CONTAINER_OF_RANGE_OF_T>
24 inline DisjointRange<T, RANGE_TYPE>::DisjointRange (const CONTAINER_OF_RANGE_OF_T& from)
25 : DisjointRange{begin (from), end (from)}
26 {
27 }
28 template <typename T, typename RANGE_TYPE>
29 template <typename COPY_FROM_ITERATOR_OF_RANGE_OF_T, sentinel_for<remove_cvref_t<COPY_FROM_ITERATOR_OF_RANGE_OF_T>> COPY_FROM_ITERATOR_OF_RANGE_OF_T2>
30 DisjointRange<T, RANGE_TYPE>::DisjointRange (COPY_FROM_ITERATOR_OF_RANGE_OF_T&& start, COPY_FROM_ITERATOR_OF_RANGE_OF_T2&& end)
31 {
32 for (auto i = forward<COPY_FROM_ITERATOR_OF_RANGE_OF_T> (start); i != forward<COPY_FROM_ITERATOR_OF_RANGE_OF_T2> (end); ++i) {
33 MergeIn_ (*i);
34 }
36 for (auto i = fSubRanges_.begin (); i != fSubRanges_.end (); ++i) {
37 if (i + 1 != fSubRanges_.end ()) {
38 Ensure (i->GetUpperBound () <= (i + 1)->GetLowerBound ());
39 }
40 }
41 }
42 }
43 template <typename T, typename RANGE_TYPE>
44 inline auto DisjointRange<T, RANGE_TYPE>::SubRanges () const -> Containers::Sequence<RangeType>
45 {
46 return fSubRanges_;
47 }
48 template <typename T, typename RANGE_TYPE>
50 {
51 return DisjointRange<RangeType>{RangeType::FullRange ()};
52 }
53 template <typename T, typename RANGE_TYPE>
55 {
56 return fSubRanges_.empty ();
57 }
58 template <typename T, typename RANGE_TYPE>
60 {
61 fSubRanges_.clear ();
62 }
63 template <typename T, typename RANGE_TYPE>
65 {
66 return static_cast<bool> (fSubRanges_.Find ([&elt] (const RangeType& r) { return r.Contains (elt); }));
67 }
68 template <typename T, typename RANGE_TYPE>
69 bool DisjointRange<T, RANGE_TYPE>::Contains (const RangeType& rhs) const
70 {
71 // @todo could be more efficient
72 DisjointRange<T, RANGE_TYPE> intersection = Intersection (rhs);
74 return sr.size () == 1 and sr[0] == rhs;
75 }
76 template <typename T, typename RANGE_TYPE>
78 {
79 // NOTE: this code counts on the fact that the subranges are ORDERED
80 size_t n = fSubRanges_.size ();
81 switch (n) {
82 case 0:
83 return RangeType{};
84 case 1:
85 return fSubRanges_[0];
86 default:
87 // @todo need constexpr check if RangeType gas this CTOR (careful of case when used with DicreteRange)
88 //return RangeType{fSubRanges_[0].GetLowerBound (), fSubRanges_.Last ()->GetUpperBound (), fSubRanges_[0].GetLowerBoundOpenness (), fSubRanges_.Last ()->GetUpperBoundOpenness ()};
89 return RangeType{fSubRanges_[0].GetLowerBound (), fSubRanges_.Last ()->GetUpperBound ()};
90 }
91 }
92 template <typename T, typename RANGE_TYPE>
94 {
95 // NOTE: this code counts on the fact that the subranges are ORDERED
96 size_t n = fSubRanges_.size ();
97 switch (n) {
98 case 0:
99 return RangeType{};
100 case 1:
101 return fSubRanges_[0];
102 default:
103 // @todo need constexpr check if RangeType gas this CTOR (careful of case when used with DicreteRange)
104 //return RangeType{fSubRanges_[0].GetLowerBound (), fSubRanges_.Last ()->GetUpperBound (), Openness::eClosed, Openness::eClosed};
105 return RangeType{fSubRanges_[0].GetLowerBound (), fSubRanges_.Last ()->GetUpperBound ()};
106 }
107 }
108 template <typename T, typename RANGE_TYPE>
110 {
111 // @todo could do more efficiently
112 return not Intersection (rhs).empty ();
113 }
114 template <typename T, typename RANGE_TYPE>
115 inline bool DisjointRange<T, RANGE_TYPE>::Intersects (const DisjointRange& rhs) const
116 {
117 // @todo could do more efficiently
118 return not Intersection (rhs).empty ();
119 }
120 template <typename T, typename RANGE_TYPE>
122 {
123 // @todo could do more efficiently
124 return Intersection (DisjointRange{rhs});
125 }
126 template <typename T, typename RANGE_TYPE>
129 // @todo could do more efficiently
130 Containers::Sequence<RangeType> disjointRanges{};
131 for (const RangeType& rri : rhs.SubRanges ()) {
132 for (const RangeType& mySR : this->SubRanges ()) {
133 RangeType intersectedSubPart = rri ^ mySR;
134 if (not intersectedSubPart.empty ()) {
135 disjointRanges.Append (intersectedSubPart);
136 }
137 }
138 }
139 return DisjointRange{disjointRanges};
140 }
141 template <typename T, typename RANGE_TYPE>
142 auto DisjointRange<T, RANGE_TYPE>::Union (const DisjointRange& rhs) const -> DisjointRange
143 {
144 // @todo could do more efficiently
145 Containers::Sequence<RangeType> disjointRanges{};
146 for (const RangeType& rri : rhs.SubRanges ()) {
147 for (const RangeType& mySR : this->SubRanges ()) {
148 RangeType sp = rri + mySR;
149 disjointRanges.Append (sp);
150 }
151 }
152 return DisjointRange{disjointRanges};
153 }
154 template <typename T, typename RANGE_TYPE>
155 auto DisjointRange<T, RANGE_TYPE>::UnionBounds (const DisjointRange& rhs) const -> RangeType
156 {
157 // @todo could do more efficiently (since 1-dimensional and subranges ordered, just need to min (lhs of each)
158 // and max (rhs of each (and grab closedness of edge captured)
159 return Union (rhs).GetBounds ();
160 }
161 template <typename T, typename RANGE_TYPE>
162 Characters::String DisjointRange<T, RANGE_TYPE>::ToString (const function<Characters::String (T)>& elt2String) const
163 {
164 Characters::StringBuilder out;
165 out << "["sv;
166 for (const RangeType& rri : SubRanges ()) {
167 out << rri.ToString (elt2String);
168 }
169 out << "]"sv;
170 return out.str ();
171 }
172 template <typename T, typename RANGE_TYPE>
173 void DisjointRange<T, RANGE_TYPE>::MergeIn_ (const RangeType& r)
174 {
175 using namespace Characters::Literals;
176#if 0
177 if (sNoisyDebugTrace_) {
178 DbgTrace (L"MergeIn (r = %s)", r.Format ().c_str ());
179 }
180#endif
181 AssertInternalRepValid_ ();
182 if (not r.empty ()) {
183 value_type rStart{r.GetLowerBound ()};
184 value_type rEnd{r.GetUpperBound ()};
185 if (fSubRanges_.size () == 0) {
186 fSubRanges_.Append (r);
187 }
188 else {
189 /*
190 * [XXXXXX] [XXX] [XXXXXXXXXXXX]
191 * EX 1 ^ ^
192 * BECOMES:
193 * [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
194 *
195 * [XXXXXX] [XXX] [XXXXXXXXXXXX]
196 * EX 2 ^ ^
197 * BECOMES:
198 * [XXXXXX] [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
199 *
200 * Find first range to the right of LEFT-ANCHOR, and adjusting it to the LEFT based on rStart,
201 * and stretch it to the left (if needed).
202 *
203 * Then grab the last block on the right (starting before rEnd) of the defined range,
204 * and stetch its right side to the right. Then delete all those in between.
205 */
206 Assert (fSubRanges_.size () >= 1);
207
208 // tmphack - need random-access iterators !!! for sequence at least!
209 auto prevOfIterator = [this] (const Iterator<RangeType>& pOfI) -> Iterator<RangeType> {
210 Iterator<RangeType> result = Iterator<RangeType>::GetEmptyIterator ();
211 for (Iterator<RangeType> i = fSubRanges_.begin (); i != fSubRanges_.end (); ++i) {
212 if (i == pOfI) {
213 return result;
214 }
215 result = i;
216 }
217 return result;
218 };
219
220 auto findStartI = [this, &rStart] () -> Iterator<RangeType> {
221 return fSubRanges_.Find (
222 [&rStart] (const RangeType& r) -> bool { return r.GetLowerBound () >= rStart or r.Contains (rStart); });
223 };
224
225 Iterator<RangeType> startI = findStartI ();
226 bool extendedRange{false};
227 if (startI == fSubRanges_.end ()) {
228 if (sNoisyDebugTrace_) {
229 DbgTrace ("Appending subrange cuz this is past the rest: {}/{}"_f, static_cast<double> (r.GetLowerBound ()),
230 static_cast<double> (r.GetUpperBound ()));
231 }
232 // cuz this means no ranges to the right containing rStart
233 //
234 // when appending, we can sometimes extend the last item
235 value_type prevVal = RangeType::TraitsType::GetPrevious (rStart);
236 Iterator<RangeType> i =
237 fSubRanges_.Find ([prevVal] (const RangeType& r) -> bool { return r.GetUpperBound () == prevVal; });
238 if (i) {
239 Assert (i->GetUpperBound () == prevVal);
240 RangeType newValue{i->GetLowerBound (), rStart};
241 fSubRanges_.Update (i, newValue);
242 extendedRange = true;
243 }
244 else {
245 fSubRanges_.Append (r);
246 }
247 }
248 else if (r.Intersects (*startI)) {
249 RangeType newValue{min (rStart, startI->GetLowerBound ()), startI->GetUpperBound ()};
250 if (sNoisyDebugTrace_) {
251 DbgTrace ("Updating subrange element {} from {}/{} to {}/{}"_f, fSubRanges_.IndexOf (startI),
252 static_cast<double> (startI->GetLowerBound ()), static_cast<double> (startI->GetUpperBound ()),
253 static_cast<double> (newValue.GetLowerBound ()), static_cast<double> (newValue.GetUpperBound ()));
254 }
255 if (*startI != newValue) {
256 fSubRanges_.Update (startI, newValue);
257 extendedRange = true;
258 }
259 }
260 else {
261 if (sNoisyDebugTrace_) {
262 DbgTrace ("Inserting subrange element {} before {}/{} of {}/{}"_f, fSubRanges_.IndexOf (startI),
263 static_cast<double> (startI->GetLowerBound ()), static_cast<double> (startI->GetUpperBound ()),
264 static_cast<double> (r.GetLowerBound ()), static_cast<double> (r.GetUpperBound ()));
265 }
266 fSubRanges_.Insert (startI, r);
267 }
268
269 startI = findStartI (); // refresh iterator cuz container changed
270
271 /*
272 * Next adjust RHS of rhs-most element.
273 */
274 Iterator<RangeType> endI =
275 prevOfIterator (fSubRanges_.Find (startI, [rEnd] (const RangeType& r) -> bool { return rEnd < r.GetLowerBound (); }));
276 if (endI == fSubRanges_.end ()) {
277 endI = prevOfIterator (fSubRanges_.end ());
278 }
279 Assert (endI != fSubRanges_.end ());
280 if (endI->GetLowerBound () <= rEnd) {
281 RangeType newValue{endI->GetLowerBound (), max (rEnd, endI->GetUpperBound ())};
282 if (newValue != *endI) {
283 if (sNoisyDebugTrace_) {
284 DbgTrace ("Updating RHS of subrange element {} from {}/{} to {}/%{}"_f, fSubRanges_.IndexOf (endI),
285 static_cast<double> (endI->GetLowerBound ()), static_cast<double> (endI->GetUpperBound ()),
286 static_cast<double> (newValue.GetLowerBound ()), static_cast<double> (newValue.GetUpperBound ()));
287 }
288 fSubRanges_.Update (endI, newValue);
289 extendedRange = true;
290 }
291 }
292 else {
293 if (sNoisyDebugTrace_) {
294 DbgTrace ("Appending RHS subrange element {}/{}"_f, static_cast<double> (r.GetLowerBound ()),
295 static_cast<double> (r.GetUpperBound ()));
296 }
297 fSubRanges_.Append (r);
298 }
299
300 startI = findStartI (); // refresh iterator cuz container changed
301
302 // hack cuz endI invalidated - REALLY must rewrite this algorithm given new rules for iterator patching / invalidation!!!
303 {
304 endI =
305 prevOfIterator (fSubRanges_.Find (startI, [rEnd] (const RangeType& r) -> bool { return rEnd < r.GetLowerBound (); }));
306 if (endI == fSubRanges_.end ()) {
307 endI = prevOfIterator (fSubRanges_.end ());
308 }
309 Assert (endI != fSubRanges_.end ());
310 }
311
312 // then merge out unneeded items in between
313 // @todo/CLEANUP/REVIEW - not sure we always have startI <= endI...
314 if (extendedRange and startI != fSubRanges_.end ()) {
315 for (auto i = startI; i != endI and i != fSubRanges_.end ();) {
316 if (i != startI and i != endI) {
317 if (sNoisyDebugTrace_) {
318 DbgTrace ("Removing redundant subrange element {} from {} to {}"_f, fSubRanges_.IndexOf (i),
319 static_cast<double> (i->GetLowerBound ()), static_cast<double> (i->GetUpperBound ()));
320 }
321 i = fSubRanges_.erase (i);
322 }
323 else {
324 ++i;
325 }
326 }
327 }
328 }
329 }
330 AssertInternalRepValid_ ();
331 //Ensure (Contains (r); NYI so do below tmphack alternative
332 Ensure (r.GetLowerBoundOpenness () == Openness::eOpen or Contains (r.GetLowerBound ()));
333 Ensure (r.GetUpperBoundOpenness () == Openness::eOpen or Contains (r.GetUpperBound ()));
334 Ensure (GetBounds ().Contains (r));
335//Ensure (Contains (r)); DISABLE TEMPORARILY CUZ CONTAINS CONSTRUCTS (ANOTHER) NEW RANGE, CAUSING INFINITE RECURSE - ...
336#if 0
337 if (sNoisyDebugTrace_) {
338 DbgTrace (L"MergeIn (r = %s)", r.Format ().c_str ());
339 }
340#endif
341 }
342 template <typename T, typename RANGE_TYPE>
343 inline void DisjointRange<T, RANGE_TYPE>::AssertInternalRepValid_ () const
344 {
346 optional<RangeType> lastRangeSeenSoFar;
347 for (const RangeType& r : fSubRanges_) {
348 Assert (not r.empty ());
349 if (lastRangeSeenSoFar) {
350 Assert (lastRangeSeenSoFar->GetUpperBound () <= r.GetLowerBound ()); // equal maybe bad but check that case with intersects which pays attention to openness
351 Assert (not lastRangeSeenSoFar->Intersects (r));
352 // and make sure we merge together adjacent points
353 value_type nextVal = RangeType::TraitsType::GetNext (lastRangeSeenSoFar->GetUpperBound ());
354 Assert (nextVal < r.GetLowerBound ()); // if nextval of previous item == lowerBound of successive one, we could have merged them into a contiguous run
355 }
356 lastRangeSeenSoFar = r;
357 }
358 }
359 }
360 template <typename T, typename RANGE_TYPE>
362 {
363 return SubRanges () == rhs.SubRanges ();
364 }
365
366 /*
367 ********************************************************************************
368 ***************** DisjointRange<T, RANGE_TYPE> Operators ***********************
369 ********************************************************************************
370 */
371 template <typename T, typename RANGE_TYPE>
373 {
374 return lhs.Union (rhs);
375 }
376 template <typename T, typename RANGE_TYPE>
377 inline DisjointRange<T, RANGE_TYPE> operator^ (const DisjointRange<T, RANGE_TYPE>& lhs, const DisjointRange<T, RANGE_TYPE>& rhs)
378 {
379 return lhs.Intersection (rhs);
380 }
381
382}
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define DbgTrace
Definition Trace.h:309
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
A DiscreteRange is a Range where the underlying endpoints are integral (discrete, not continuous); th...
A DisjointRange is NOT a range, but a collection of non-overlapping (except at the edges) Ranges....
typename RangeType::value_type value_type
Range::value_type is the type of the contained elements of the range (say range of integers,...
nonvirtual Containers::Sequence< RangeType > SubRanges() const
RANGE_TYPE RangeType
a Disjoint range collection of (disjoint) ranges of this type.
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:300
constexpr bool empty() const
Definition Range.inl:143
constexpr T GetLowerBound() const
Definition Range.inl:394