Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Range.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
8
9namespace Stroika::Foundation::Traversal {
10
11 /*
12 ********************************************************************************
13 **** RangeTraits::ExplicitOpennessAndDifferenceType<T, OPENNESS, DIFF_TYPE> ****
14 ********************************************************************************
15 */
16 template <typename T, typename OPENNESS, typename DIFF_TYPE>
19 -> SignedDifferenceType
20 {
21 if constexpr (is_enum_v<T> or is_convertible_v<T, SignedDifferenceType>) {
22 return static_cast<SignedDifferenceType> (rhs) - static_cast<SignedDifferenceType> (lhs);
23 }
24 else {
25 return static_cast<SignedDifferenceType> (rhs - lhs);
26 }
27 }
28
29 /*
30 ********************************************************************************
31 **********& RangeTraits::Explicit<T, OPENNESS, BOUNDS, DIFF_TYPE> **************
32 ********************************************************************************
33 */
34 template <typename T, typename OPENNESS, typename BOUNDS, typename DIFF_TYPE>
36 requires (is_integral_v<T> or is_enum_v<T>)
37 {
38 Require (i != kUpperBound);
39 return static_cast<value_type> (static_cast<UnsignedDifferenceType> (i) + 1);
40 }
41 template <typename T, typename OPENNESS, typename BOUNDS, typename DIFF_TYPE>
43 requires (is_floating_point_v<T>)
44 {
45 Require (i != kUpperBound);
46 return nextafter (i, kUpperBound);
47 }
48 template <typename T, typename OPENNESS, typename BOUNDS, typename DIFF_TYPE>
50 requires (is_integral_v<T> or is_enum_v<T>)
51 {
52 Require (i != kLowerBound);
53 return static_cast<value_type> (static_cast<SignedDifferenceType> (i) - 1);
54 }
55 template <typename T, typename OPENNESS, typename BOUNDS, typename DIFF_TYPE>
57 requires (is_floating_point_v<T>)
58 {
59 Require (i != kLowerBound);
60 return nextafter (i, kLowerBound);
61 }
62
63 /*
64 ********************************************************************************
65 ***************************** Range<T, TRAITS> *********************************
66 ********************************************************************************
67 */
68 template <typename T, typename TRAITS>
70 : fBegin_{TRAITS::kUpperBound}
71 , fEnd_{TRAITS::kLowerBound}
72 , fBeginOpenness_{TRAITS::kLowerBoundOpenness}
73 , fEndOpenness_{TRAITS::kUpperBoundOpenness}
74 {
75 Ensure (empty ());
76 }
77 template <typename T, typename TRAITS>
78 template <typename T2, typename TRAITS2>
79 constexpr Range<T, TRAITS>::Range (const Range<T2, TRAITS>& src)
80 : Range{src.GetLowerBound (), src.GetUpperBound (), src.GetLowerBoundOpenness (), src.GetUpperBoundOpenness ()}
81 {
82 }
83 template <typename T, typename TRAITS>
85 : Range{begin, end, TRAITS::kLowerBoundOpenness, TRAITS::kUpperBoundOpenness}
86 {
87 }
88 template <typename T, typename TRAITS>
89 constexpr Range<T, TRAITS>::Range (const optional<T>& begin, const optional<T>& end)
90 : Range{begin.has_value () ? *begin : TRAITS::kLowerBound, end.has_value () ? *end : TRAITS::kUpperBound,
91 TRAITS::kLowerBoundOpenness, TRAITS::kUpperBoundOpenness}
92 {
93 }
94 template <typename T, typename TRAITS>
95 constexpr Range<T, TRAITS>::Range (Common::ArgByValueType<T> begin, Common::ArgByValueType<T> end, Openness lhsOpen, Openness rhsOpen)
96 : fBegin_{begin}
97 , fEnd_{end}
98 , fBeginOpenness_{lhsOpen}
99 , fEndOpenness_{rhsOpen}
100 {
101 Require (TRAITS::kLowerBound <= TRAITS::kUpperBound); // always required for class
102 Require (TRAITS::kLowerBound <= begin);
103 Require (begin <= end);
104 Require (end <= TRAITS::kUpperBound);
105 Require (begin < end or (lhsOpen == Openness::eClosed and rhsOpen == Openness::eClosed));
106 Ensure (not empty ());
107 }
108 template <typename T, typename TRAITS>
109 constexpr Range<T, TRAITS>::Range (const optional<T>& begin, const optional<T>& end, Openness lhsOpen, Openness rhsOpen)
110 : Range{begin.has_value () ? *begin : TRAITS::kLowerBound, end.has_value () ? *end : TRAITS::kUpperBound, lhsOpen, rhsOpen}
111 {
112 }
113 template <typename T, typename TRAITS>
115 {
116 Require (start <= GetUpperBound ());
117 return Range{start, GetUpperBound (), GetLowerBoundOpenness (), GetUpperBoundOpenness ()};
118 }
119 template <typename T, typename TRAITS>
121 {
122 Require (GetLowerBound () <= end);
123 return Range{GetLowerBound (), end, GetLowerBoundOpenness (), GetUpperBoundOpenness ()};
124 }
125 template <typename T, typename TRAITS>
127 Openness lhsOpen, Openness rhsOpen)
129 return Range{center - radius, center + radius, lhsOpen, rhsOpen};
130 }
131 template <typename T, typename TRAITS>
133 {
134 // note the case of begin==end is depends on openness, and already handled in normal CTOR - just avoid assert for having begin/end reversed
135 return begin > end ? Range{} : Range{begin, end};
136 }
137 template <typename T, typename TRAITS>
139 {
140 return Range{TraitsType::kLowerBound, TraitsType::kUpperBound, TraitsType::kLowerBoundOpenness, TraitsType::kUpperBoundOpenness};
141 }
142 template <typename T, typename TRAITS>
143 constexpr bool Range<T, TRAITS>::empty () const
144 {
145 if (fBegin_ > fEnd_) {
146 // internal hack done in Range::Range() - empty range - otherwise not possible to create this situation
147 return true;
148 }
149 else if (fBegin_ == fEnd_) {
150 return fBeginOpenness_ == Openness::eOpen and fEndOpenness_ == Openness::eOpen;
151 }
152 return false;
153 }
154 template <typename T, typename TRAITS>
155 constexpr Range<T, TRAITS>::operator bool () const
156 {
157 return not empty ();
158 }
159 template <typename T, typename TRAITS>
160 constexpr typename Range<T, TRAITS>::UnsignedDifferenceType Range<T, TRAITS>::GetDistanceSpanned () const
161 {
162 if (empty ()) [[unlikely]] {
163 return static_cast<UnsignedDifferenceType> (0);
164 }
165 return static_cast<UnsignedDifferenceType> (TraitsType::Difference (fBegin_, fEnd_));
167 template <typename T, typename TRAITS>
168 constexpr T Range<T, TRAITS>::GetMidpoint () const
169 {
170 Require (not empty ());
171 return GetLowerBound () + GetDistanceSpanned () / 2;
172 }
173 template <typename T, typename TRAITS>
174 constexpr T Range<T, TRAITS>::Pin (T v) const
175 {
176 Require (not empty ());
177 Assert (fBegin_ != fEnd_ or (fBeginOpenness_ == Openness::eClosed and fEndOpenness_ == Openness::eClosed));
178 if (v < fBegin_ or (v == fBegin_ and fBeginOpenness_ == Openness::eOpen)) {
179 // must advance
180 T tmp{fBeginOpenness_ == Openness::eClosed ? fBegin_ : TraitsType::GetNext (fBegin_)};
181 Require (Contains (tmp));
182 return tmp;
183 }
184 else if (v > fEnd_ or (v == fEnd_ and fEndOpenness_ == Openness::eOpen)) {
185 // must retreat
186 T tmp{fEndOpenness_ == Openness::eClosed ? fEnd_ : TraitsType::GetPrevious (fEnd_)};
187 Require (Contains (tmp));
188 return tmp;
189 }
190 return v;
191 }
192 template <typename T, typename TRAITS>
194 {
195 if (empty ()) {
196 return false;
197 }
198 if (fBegin_ < r and r < fEnd_) {
199 return true;
200 }
201 if (fBeginOpenness_ == Openness::eClosed and r == fBegin_) {
202 return true;
203 }
204 if (fEndOpenness_ == Openness::eClosed and r == fEnd_) {
205 return true;
206 }
207 return false;
208 }
209 template <typename T, typename TRAITS>
210 constexpr bool Range<T, TRAITS>::Contains (const Range& containee) const
211 {
212 /*
213 * First, ANY empty set is contained in any other set: \forall A: \emptyset \subseteq A
214 */
215 if (containee.empty ()) {
216 return true; // \forall A: \emptyset \subseteq A
217 }
218 /*
219 * Roughly, a non-empty range is contained in a range iff both ends are contained - but we need to take care of openness
220 * edge conditions.
221 *
222 * if BOTH container and containee are open, the container wont CONTAIN the lower bound of the containee if the lower bounds are equal
223 *
224 * Check lower bound and upper bound separately.
225 */
226 switch (containee.GetLowerBoundOpenness ()) {
227 case Openness::eClosed:
228 if (not Contains (containee.GetLowerBound ())) {
229 return false;
230 }
231 [[fallthrough]];
232 case Openness::eOpen:
233 if (containee.GetLowerBound () < GetLowerBound ()) {
234 return false;
235 }
236 break;
237 }
238 switch (containee.GetUpperBoundOpenness ()) {
239 case Openness::eClosed:
240 if (not Contains (containee.GetUpperBound ())) {
241 return false;
242 }
243 [[fallthrough]];
244 case Openness::eOpen:
245 if (containee.GetUpperBound () > GetUpperBound ()) {
246 return false;
247 }
248 }
249 return true;
250 }
251 template <typename T, typename TRAITS>
253 {
254 Require (not empty ());
255 return Range{GetLowerBound (), GetUpperBound (), Openness::eClosed, Openness::eClosed};
256 }
257 template <typename T, typename TRAITS>
258 template <typename T2, typename TRAITS2>
259 constexpr bool Range<T, TRAITS>::Intersects (const Range<T2, TRAITS2>& rhs) const
260 {
261#if 0
262 // For SOME cases - if we can check if constexpr/if consteval - we maybe able to use the simpler
263 T mn = min (fEnd, rhs.fEnd);
264 T mx = max (fBegin, rhs.fBegin);
265 int cmp = mx - min;
266 if (cmp == 0) {
267 return rhs.GetUpperBoundOpenness () == eOpen or GetLowerBoundOpenness () == Openness::eClosed;
268 }
269 return cmp > 0;
270 if consteval (I know this is closed) {
271 .... do optimized case;
272 }
273 ...
274 bools as numbers
276#endif
277 [[maybe_unused]] auto oldCode = [this] (const Range<T2, TRAITS2>& rhs) {
278 if (empty () or rhs.empty ()) {
279 return false;
280 }
281 T l = max (fBegin_, rhs.GetLowerBound ());
282 T r = min (fEnd_, rhs.GetUpperBound ());
283 if (l < r) {
284 return true;
285 }
286 else if (l == r) {
287 // must check if the end that has 'l' for each Range that that end is closed. Contains()
288 // is a shortcut for that
289 return Contains (l) and rhs.Contains (l);
290 }
291 else {
292 return false;
293 }
294 };
295 /*
296 * | this |
297 * | A | | B | | C | | D | | E |
298 * | G | | H |
299 * | F |
300 */
301 // note: GetLowerBound () and GetUpperBound () do assert !empty (), so must access fBegin/fEnd directly
302 // since probably more efficient to check past end cases before checking empty cases
303 if (rhs.fEnd_ < fBegin_) {
304 Assert (oldCode (rhs) == false);
305 return false; // the entirety of rhs is strictly BEFORE lhs (see case A)
306 }
307 if (rhs.fBegin_ > fEnd_) {
308 Assert (oldCode (rhs) == false);
309 return false; // the entirety of rhs is strictly AFTER lhs (see case E)
310 }
311 if (empty () or rhs.empty ()) {
312 Assert (oldCode (rhs) == false);
313 return false; // if either side is empty, clearly they cannot share any points
314 }
315 if (rhs.fEnd_ == fBegin_) {
316 Assert (oldCode (rhs) == (rhs.GetUpperBoundOpenness () == Openness::eClosed and GetLowerBoundOpenness () == Openness::eClosed));
317 // if they intersect at a point, both side must be closed (contain that point) - (see case G)
318 return rhs.GetUpperBoundOpenness () == Openness::eClosed and GetLowerBoundOpenness () == Openness::eClosed;
319 }
320 if (rhs.fBegin_ == fEnd_) {
321 Assert (oldCode (rhs) == (rhs.GetLowerBoundOpenness () == Openness::eClosed and GetUpperBoundOpenness () == Openness::eClosed));
322 // if they intersect at a point, both side must be closed (contain that point) - (see case H)
323 return rhs.GetLowerBoundOpenness () == Openness::eClosed and GetUpperBoundOpenness () == Openness::eClosed;
324 }
325 Assert (oldCode (rhs) == true);
326 return true; // see cases B, C, D, and F - they all intersect
327 }
328 template <typename T, typename TRAITS>
330 {
331 if (empty () or rhs.empty ()) {
332 return Range{};
333 }
334 T l = max (fBegin_, rhs.fBegin_);
335 T r = min (fEnd_, rhs.fEnd_);
336 if (l <= r) {
337 // lhs/rhs ends are closed iff BOTH lhs/rhs contains that point
338 Openness lhsO = Contains (l) and rhs.Contains (l) ? Openness::eClosed : Openness::eOpen;
339 Openness rhsO = Contains (r) and rhs.Contains (r) ? Openness::eClosed : Openness::eOpen;
340 if (l != r or (lhsO == Openness::eClosed and rhsO == Openness::eClosed)) {
341 return Range{l, r, lhsO, rhsO};
343 }
344 return Range{};
345 }
346 template <typename T, typename TRAITS>
348 {
349 return DisjointRange<T, Range>{{*this, rhs}};
350 }
351 template <typename T, typename TRAITS>
353 {
354 if (empty ()) {
355 return rhs;
356 }
357 if (rhs.empty ()) {
358 return *this;
359 }
360 T l = min (GetLowerBound (), rhs.GetLowerBound ());
361 T r = max (GetUpperBound (), rhs.GetUpperBound ());
362 Range result;
363 if (l <= r) {
364 // lhs/rhs ends are closed iff BOTH lhs/rhs contains that point
365 Openness lhsO = Contains (l) and rhs.Contains (l) ? Openness::eClosed : Openness::eOpen;
366 Openness rhsO = Contains (r) and rhs.Contains (r) ? Openness::eClosed : Openness::eOpen;
367 result = Range{l, r, lhsO, rhsO};
368 }
369 Ensure (result.GetLowerBound () <= GetLowerBound ());
370 Ensure (result.GetLowerBound () <= GetUpperBound ());
371 Ensure (result.GetLowerBound () <= rhs.GetLowerBound ());
372 Ensure (result.GetLowerBound () <= rhs.GetUpperBound ());
373 Ensure (result.GetUpperBound () >= GetLowerBound ());
374 Ensure (result.GetUpperBound () >= GetUpperBound ());
375 Ensure (result.GetUpperBound () >= rhs.GetLowerBound ());
376 Ensure (result.GetUpperBound () >= rhs.GetUpperBound ());
377 return result;
378 }
379 template <typename T, typename TRAITS>
382 if (empty ()) {
383 return Range{value, value, Openness::eClosed, Openness::eClosed};
384 }
385 if (value < GetLowerBound ()) {
386 return Range{value, GetUpperBound (), Openness::eClosed, GetUpperBoundOpenness ()};
387 }
388 if (GetUpperBound () < value) {
389 return Range{GetLowerBound (), value, GetLowerBoundOpenness (), Openness::eClosed};
390 }
391 return *this;
392 }
393 template <typename T, typename TRAITS>
395 {
396 Require (not empty ());
397 return fBegin_;
398 }
399 template <typename T, typename TRAITS>
401 {
402 return fBeginOpenness_;
403 }
404 template <typename T, typename TRAITS>
406 {
407 Require (not empty ());
408 return fEnd_;
409 }
410 template <typename T, typename TRAITS>
412 {
413 return fEndOpenness_;
414 }
415 template <typename T, typename TRAITS>
416 constexpr auto Range<T, TRAITS>::Offset (SignedDifferenceType o) const -> Range
417 {
418 Require (not empty ());
419 return Range{static_cast<T> (GetLowerBound () + o), static_cast<T> (GetUpperBound () + o), GetLowerBoundOpenness (), GetUpperBoundOpenness ()};
421 template <typename T, typename TRAITS>
422 constexpr auto Range<T, TRAITS>::Times (T o) const -> Range
423 {
424 Require (not empty ());
425 return Range{static_cast<T> (GetLowerBound () * o), static_cast<T> (GetUpperBound () * o), GetLowerBoundOpenness (), GetUpperBoundOpenness ()};
426 }
427 template <typename T, typename TRAITS>
428 Characters::String Range<T, TRAITS>::ToString (const function<Characters::String (const T&)>& eltToString) const
431 if (empty ()) {
432 out << "{}"sv;
433 }
434 else if (GetLowerBound () == GetUpperBound ()) {
435 // if single point, open and closed must be same (or always must be closed?)
436 out << "["sv << eltToString (GetLowerBound ()) << "]"sv;
437 }
438 else {
439 out << ((GetLowerBoundOpenness () == Openness::eClosed) ? "["sv : "("sv);
440 if (GetLowerBound () != TRAITS::kLowerBound) {
441 out << eltToString (GetLowerBound ());
442 }
443 out << " ... "sv;
444 if (GetUpperBound () != TRAITS::kUpperBound) {
445 out << eltToString (GetUpperBound ());
446 }
447 out << ((GetUpperBoundOpenness () == Openness::eClosed) ? "]"sv : ")"sv);
448 }
449 return out.str ();
450 }
451 template <typename T, typename TRAITS>
452 constexpr bool Range<T, TRAITS>::operator== (const Range& rhs) const
453 {
454 if (empty ()) {
455 return rhs.empty ();
456 }
457 return GetLowerBound () == rhs.GetLowerBound () and GetUpperBound () == rhs.GetUpperBound () and
458 GetLowerBoundOpenness () == rhs.GetLowerBoundOpenness () and GetUpperBoundOpenness () == rhs.GetUpperBoundOpenness ();
459 }
460 template <typename T, typename TRAITS>
461 constexpr partial_ordering Range<T, TRAITS>::operator<=> (const Range& rhs) const
462 {
463 /*
464 * | this |
465 * | rhs |
466 */
467 if (GetUpperBoundOpenness () == eClosed and rhs.GetLowerBoundOpenness () == eClosed) {
468 if (GetUpperBound () < rhs.GetLowerBound ()) {
469 return partial_ordering::less;
470 }
471 }
472 else {
473 if (GetUpperBound () <= rhs.GetLowerBound ()) {
474 return partial_ordering::less;
475 }
477 /*
478 * | this |
479 * | rhs |
480 */
481 if (GetLowerBoundOpenness () == eClosed and rhs.GetLowerBoundOpenness () == eClosed) {
482 if (GetLowerBound () > rhs.GetUpperBound ()) {
483 return partial_ordering::greater;
485 }
486 else {
487 if (GetLowerBound () >= rhs.GetUpperBound ()) {
488 return partial_ordering::greater;
489 }
491 if (*this == rhs) {
492 return partial_ordering::equivalent;
493 }
494 /*
495 * | this |
496 * | rhs |
497 * or
498 * | this |
499 * | rhs |
500 * etc...
501 */
502 return partial_ordering::unordered;
503 }
504
505 /*
506 ********************************************************************************
507 *************************** Range<T,TRAITS> Operators **************************
508 ********************************************************************************
509 */
510 template <typename T, typename TRAITS>
511 constexpr Range<T, TRAITS> operator+ (const T& lhs, const Range<T, TRAITS>& rhs)
512 {
513 return rhs.Offset (lhs);
515 template <typename T, typename TRAITS>
516 constexpr Range<T, TRAITS> operator+ (const Range<T, TRAITS>& lhs, const T& rhs)
517 {
518 return lhs.Offset (rhs);
519 }
520 template <typename T, typename TRAITS>
521 DisjointRange<T, Range<T, TRAITS>> operator+ (const Range<T, TRAITS>& lhs, const Range<T, TRAITS>& rhs)
522 {
523 return lhs.Union (rhs);
524 }
526 template <typename T, typename TRAITS>
527 constexpr Range<T, TRAITS> operator* (const T& lhs, const Range<T, TRAITS>& rhs)
528 {
529 return rhs.Times (lhs);
530 }
531 template <typename T, typename TRAITS>
532 constexpr Range<T, TRAITS> operator* (const Range<T, TRAITS>& lhs, const T& rhs)
533 {
534 return lhs.Times (rhs);
535 }
536
537 template <typename T, typename TRAITS>
538 constexpr Range<T, TRAITS> operator^ (const Range<T, TRAITS>& lhs, const Range<T, TRAITS>& rhs)
539 {
540 return lhs.Intersection (rhs);
541 }
542
543}
544
546 template <>
547 constexpr EnumNames<Traversal::Openness> DefaultNames<Traversal::Openness>::k{{{
548 {Traversal::Openness::eOpen, L"Open"},
549 {Traversal::Openness::eClosed, L"Closed"},
550 }}};
551}
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A DisjointRange is NOT a range, but a collection of non-overlapping (except at the edges) Ranges....
constexpr bool Contains(Common::ArgByValueType< T > r) const
Definition Range.inl:193
static constexpr Range FullRange()
Definition Range.inl:138
static constexpr Range Ball(Common::ArgByValueType< T > center, Common::ArgByValueType< UnsignedDifferenceType > radius, Openness lhsOpen=TRAITS::kLowerBoundOpenness, Openness rhsOpen=TRAITS::kUpperBoundOpenness)
returns a range centered around center, with the given radius (and optionally argument openness).
Definition Range.inl:126
constexpr Range Extend(Common::ArgByValueType< T > value) const
Definition Range.inl:380
constexpr bool empty() const
Definition Range.inl:143
constexpr Range Offset(SignedDifferenceType o) const
Definition Range.inl:416
constexpr T GetUpperBound() const
Definition Range.inl:405
constexpr Range Intersection(const Range &rhs) const
Definition Range.inl:329
nonvirtual Characters::String ToString(const function< Characters::String(const T &)> &elt2String=[](const T &x) -> Characters::String { return Characters::ToString(x);}) const
Definition Range.inl:428
constexpr bool Intersects(const Range< T2, TRAITS2 > &rhs) const
Definition Range.inl:259
constexpr bool operator==(const Range &rhs) const
Definition Range.inl:452
constexpr Range Times(T o) const
Definition Range.inl:422
constexpr T GetLowerBound() const
Definition Range.inl:394
constexpr Range ReplaceStart(Common::ArgByValueType< T > start) const
Construct a new Range from this, but with the given start.
Definition Range.inl:114
constexpr Range UnionBounds(const Range &rhs) const
Definition Range.inl:352
constexpr Range ReplaceEnd(Common::ArgByValueType< T > end) const
Construct a new Range from this, but with the given end.
Definition Range.inl:120
nonvirtual DisjointRange< T, Range > Union(const Range &rhs) const
Definition Range.inl:347
constexpr T GetMidpoint() const
Definition Range.inl:168
constexpr T Pin(T v) const
Definition Range.inl:174
nonvirtual constexpr Range Closure() const
Definition Range.inl:252
static constexpr Range ContainedRange(Common::ArgByValueType< T > begin, Common::ArgByValueType< T > end)
Definition Range.inl:132
constexpr UnsignedDifferenceType GetDistanceSpanned() const
Definition Range.inl:160
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:32
static constexpr value_type GetPrevious(value_type i)
Definition Range.inl:49
static constexpr value_type GetNext(value_type i)
Definition Range.inl:35
static constexpr SignedDifferenceType Difference(Common::ArgByValueType< value_type > lhs, Common::ArgByValueType< value_type > rhs)
Definition Range.inl:17