Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Iterable.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <execution>
5#include <set>
6
7#include "Stroika/Foundation/Containers/Adapters/Adder.h"
12
13namespace Stroika::Foundation::Traversal {
14
15 constexpr bool kIterableUsesStroikaSharedPtr [[deprecated ("Since Stroika v3.0d1 - not used")]] = false;
16
17 struct [[deprecated ("Since Stroika v3.0d1")]] IterableBase {
18 template <typename SHARED_T>
19 using PtrImplementationTemplate [[deprecated ("Since Stroika v3.0d1 - use shared_ptr directly")]] = shared_ptr<SHARED_T>;
20 template <typename SHARED_T, typename... ARGS_TYPE>
21 [[deprecated ("Since Stroika v3.0d1 - use Memory::MakeSharedPtr directly")]] static shared_ptr<SHARED_T> MakeSmartPtr (ARGS_TYPE&&... args)
22 {
23 return Memory::MakeSharedPtr<SHARED_T> (forward<ARGS_TYPE> (args)...);
24 }
25 template <typename SHARED_T>
26 using enable_shared_from_this_PtrImplementationTemplate [[deprecated ("Since Stroika v3.0d1")]] = std::enable_shared_from_this<SHARED_T>;
27 };
28
29 /*
30 ********************************************************************************
31 ***************************** Iterable<T>::_IRep *******************************
32 ********************************************************************************
33 */
34 template <typename T>
35 inline size_t Iterable<T>::_IRep::size () const
36 {
37 /*
38 * Default slow/weak implementation.
39 */
40 size_t sz{};
41 if constexpr (true) {
42 this->Apply ([&sz] (const T&) { ++sz; }, Execution::SequencePolicy::eDEFAULT);
43 }
44 else {
45 for (Iterator<T> i = MakeIterator (); i != Iterator<T>::GetEmptyIterator (); ++i, ++sz)
46 ;
47 }
48 return sz;
49 }
50 template <typename T>
51 inline bool Iterable<T>::_IRep::empty () const
52 {
53 if (auto i = MakeIterator ()) {
54 return false;
55 }
56 return true;
57 }
58 template <typename T>
59 inline void Iterable<T>::_IRep::Apply (const function<void (ArgByValueType<T> item)>& doToElement, [[maybe_unused]] Execution::SequencePolicy seq) const
60 {
61 RequireNotNull (doToElement);
62 for (Iterator<T> i = MakeIterator (); i != Iterator<T>::GetEmptyIterator (); ++i) {
63 doToElement (*i);
64 }
65 }
66 template <typename T>
67 inline auto Iterable<T>::_IRep::Find (const function<bool (ArgByValueType<T> item)>& that, [[maybe_unused]] Execution::SequencePolicy seq) const
69 {
70 RequireNotNull (that);
71 for (Iterator<T> i = MakeIterator (); i != Iterator<T>::GetEmptyIterator (); ++i) {
72 if (that (*i)) {
73 return i;
74 }
75 }
77 }
78 template <typename T>
79 inline auto Iterable<T>::_IRep::Find_equal_to (const ArgByValueType<T>& v, [[maybe_unused]] Execution::SequencePolicy seq) const
81 {
82 if constexpr (Common::IEqualToOptimizable<T>) {
83 /*
84 * This is the default implementation. It is only ever if there is a valid equal_to<> around, and
85 * that valid equal_to<> is stateless (verified by Common::IEqualToOptimizable).
86 */
87 if constexpr (true) {
88 // simpler but not sure if faster; better though cuz by default leverages seq, which might
89 // help. In 'size' testing, on windows, this was slightly larger, so
90 // not 100% sure this is the best default -- LGP 2023-02-06
91 return Find ([&v] (const T& rhs) { return equal_to<T>{}(v, rhs); }, seq);
92 }
93 else {
94 for (Iterator<T> i = MakeIterator (); i != Iterator<T>::GetEmptyIterator (); ++i) {
95 if (equal_to<T>{}(v, *i)) {
96 return i;
97 }
98 }
100 }
101 }
102 else {
103 RequireNotReached (); // cannot call if not IEqualToOptimizable
105 }
106 }
107
108 /*
109 ********************************************************************************
110 ******************* Iterable<T>::_SafeReadRepAccessor **************************
111 ********************************************************************************
112 */
113 template <typename T>
114 template <typename REP_SUB_TYPE>
116 : fConstRef_{Debug::UncheckedDynamicCast<const REP_SUB_TYPE*> (it->_fRep.cget ())}
117 , fIterableEnvelope_{it}
118#if qStroika_Foundation_Debug_AssertionsChecked
119 , fAssertReadLock_{it->_fThisAssertExternallySynchronized}
120#endif
121 {
122 RequireNotNull (it);
123 EnsureMember (fConstRef_, REP_SUB_TYPE);
124 }
125 template <typename T>
126 template <typename REP_SUB_TYPE>
127 inline Iterable<T>::_SafeReadRepAccessor<REP_SUB_TYPE>::_SafeReadRepAccessor (_SafeReadRepAccessor&& src) noexcept
128 : fConstRef_{src.fConstRef_}
129 , fIterableEnvelope_{src.fIterableEnvelope_}
130#if qStroika_Foundation_Debug_AssertionsChecked
131 , fAssertReadLock_{move (src.fAssertReadLock_)}
132#endif
133 {
134 RequireNotNull (fConstRef_);
135 EnsureMember (fConstRef_, REP_SUB_TYPE);
136 src.fConstRef_ = nullptr;
137 }
138 template <typename T>
139 template <typename REP_SUB_TYPE>
140 inline auto Iterable<T>::_SafeReadRepAccessor<REP_SUB_TYPE>::operator= (_SafeReadRepAccessor&& rhs) noexcept -> _SafeReadRepAccessor&
141 {
142 fConstRef_ = rhs.fConstRef_;
143 this->fIterableEnvelope_ = rhs.fIterableEnvelope_;
144#if qStroika_Foundation_Debug_AssertionsChecked
145 this->fAssertReadLock_ = move (rhs.fAssertReadLock_);
146#endif
147 return *this;
148 }
149 template <typename T>
150 template <typename REP_SUB_TYPE>
151 inline const REP_SUB_TYPE& Iterable<T>::_SafeReadRepAccessor<REP_SUB_TYPE>::_ConstGetRep () const noexcept
152 {
153 EnsureMember (fConstRef_, REP_SUB_TYPE);
154 return *fConstRef_;
155 }
156 template <typename T>
157 template <typename REP_SUB_TYPE>
158 inline auto Iterable<T>::_SafeReadRepAccessor<REP_SUB_TYPE>::_ConstGetRepSharedPtr () const noexcept -> shared_ptr<REP_SUB_TYPE>
159 {
160 return Debug::UncheckedDynamicPointerCast<REP_SUB_TYPE> (this->fIterableEnvelope_->_fRep.cget_ptr ());
161 }
162
163 /*
164 ********************************************************************************
165 ************* Iterable<CONTAINER_OF_T, T>::_SafeReadWriteRepAccessor ***********
166 ********************************************************************************
167 */
168 template <typename T>
169 template <typename REP_SUB_TYPE>
170 inline Iterable<T>::_SafeReadWriteRepAccessor<REP_SUB_TYPE>::_SafeReadWriteRepAccessor (Iterable<T>* iterableEnvelope)
171 : fRepReference_{Debug::UncheckedDynamicCast<REP_SUB_TYPE*> (iterableEnvelope->_fRep.rwget ())}
173 , fAssertWriteLock_{iterableEnvelope->_fThisAssertExternallySynchronized}
174 , fIterableEnvelope_{iterableEnvelope}
175#endif
176 {
177 RequireNotNull (iterableEnvelope);
178 }
179 template <typename T>
180 template <typename REP_SUB_TYPE>
181 inline Iterable<T>::_SafeReadWriteRepAccessor<REP_SUB_TYPE>::_SafeReadWriteRepAccessor (_SafeReadWriteRepAccessor&& from)
182 : fRepReference_{from.fRepReference_}
184 , fAssertWriteLock_{move (from.fAssertWriteLock_)}
185 , fIterableEnvelope_{from.fIterableEnvelope_}
186#endif
187 {
188 RequireNotNull (fRepReference_);
189 EnsureMember (fRepReference_, REP_SUB_TYPE);
190#if qStroika_Foundation_Debug_AssertionsChecked
191 from.fIterableEnvelope_ = nullptr;
192#endif
193 from.fRepReference_ = nullptr;
194 }
195 template <typename T>
196 template <typename REP_SUB_TYPE>
197 inline auto Iterable<T>::_SafeReadWriteRepAccessor<REP_SUB_TYPE>::operator= (_SafeReadWriteRepAccessor&& rhs) noexcept -> _SafeReadWriteRepAccessor&
198 {
199 fRepReference_ = rhs.fRepReference_;
200 EnsureMember (fRepReference_, REP_SUB_TYPE);
201#if qStroika_Foundation_Debug_AssertionsChecked
202 this->fAssertWriteLock_ = move (rhs.fAssertWriteLock_);
203 this->fIterableEnvelope_ = rhs.fIterableEnvelope_;
204 rhs.fIterableEnvelope_ = nullptr;
205#endif
206 return *this;
207 }
208 template <typename T>
209 template <typename REP_SUB_TYPE>
210 inline const REP_SUB_TYPE& Iterable<T>::_SafeReadWriteRepAccessor<REP_SUB_TYPE>::_ConstGetRep () const
211 {
212 EnsureNotNull (fRepReference_);
213 return *fRepReference_;
214 }
215 template <typename T>
216 template <typename REP_SUB_TYPE>
217 inline REP_SUB_TYPE& Iterable<T>::_SafeReadWriteRepAccessor<REP_SUB_TYPE>::_GetWriteableRep ()
218 {
219 EnsureNotNull (fRepReference_);
220#if qStroika_Foundation_Debug_AssertionsChecked
221 EnsureNotNull (fIterableEnvelope_);
222 EnsureNotNull (fIterableEnvelope_->_fRep);
223 Ensure (fIterableEnvelope_->_fRep.use_count () == 1);
224#endif
225 return *fRepReference_;
226 }
227
228 /*
229 ********************************************************************************
230 ********************************** Iterable<T> *********************************
231 ********************************************************************************
232 */
233 template <typename T>
234 inline Iterable<T>::Iterable (const shared_ptr<typename Iterable<T>::_IRep>& rep) noexcept
235 : _fRep{(RequireExpression (rep != nullptr), rep)}
236 {
237 Require (_fRep.GetSharingState () != Memory::SharedByValue_State::eNull);
238 }
239 template <typename T>
240 inline Iterable<T>::Iterable (shared_ptr<typename Iterable<T>::_IRep>&& rep) noexcept
241 : _fRep{(RequireExpression (rep != nullptr), move (rep))}
242 {
243 Require (_fRep.GetSharingState () != Memory::SharedByValue_State::eNull);
244 Require (rep == nullptr); // after move (see https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr "After the construction, ... r is empty and its stored pointer is null"
245 }
246#if !qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
247 template <typename T>
248 template <IIterableOfTo<T> CONTAINER_OF_T>
249 Iterable<T>::Iterable (CONTAINER_OF_T&& from)
250 requires (not derived_from<remove_cvref_t<CONTAINER_OF_T>, Iterable<T>> and
251 (copyable<remove_cvref_t<CONTAINER_OF_T>> or same_as<remove_cvref_t<CONTAINER_OF_T>, initializer_list<T>>))
252 : _fRep{mk_ (forward<CONTAINER_OF_T> (from))._fRep}
253 {
254 }
255#endif
256 template <typename T>
257 inline Iterable<T>::Iterable (const initializer_list<T>& from)
258 : _fRep{mk_ (from)._fRep}
259 {
260 }
261 template <typename T>
262 inline Iterable<T>::operator bool () const
263 {
264 return not empty ();
265 }
266 template <typename T>
267 inline shared_ptr<typename Iterable<T>::_IRep> Iterable<T>::Clone_ (const _IRep& rep)
268 {
269 return rep.Clone ();
270 }
271 template <typename T>
272 template <typename CONTAINER_OF_T>
273 Iterable<T> Iterable<T>::mk_ (CONTAINER_OF_T&& from)
274 requires (copyable<remove_cvref_t<CONTAINER_OF_T>> or same_as<remove_cvref_t<CONTAINER_OF_T>, initializer_list<T>>)
275 {
276 using DECAYED_CONTAINER = remove_cvref_t<CONTAINER_OF_T>;
277 // Most containers are safe to use copy-by-value, except not initializer_list<> - not sure how to check for that generically...
278 using USE_CONTAINER_TYPE =
279 conditional_t<copy_constructible<DECAYED_CONTAINER> and not same_as<DECAYED_CONTAINER, initializer_list<T>>, DECAYED_CONTAINER, vector<T>>;
280 auto sharedCopyOfContainer = make_shared<USE_CONTAINER_TYPE> (forward<CONTAINER_OF_T> (from));
281 // shared copy so if/when getNext copied, the container itself isn't (so not invalidating any iterators)
282 function<optional<T> ()> getNext = [sharedCopyOfContainer, i = sharedCopyOfContainer->begin ()] () mutable -> optional<T> {
283 if (i != sharedCopyOfContainer->end ()) {
284 return *i++; // intentionally increment AFTER returning value
285 }
286 return nullopt;
287 };
288 return CreateGenerator (getNext);
289 }
290 template <typename T>
293 return _fRep.GetSharingState ();
294 }
295 template <typename T>
297 {
298 _SafeReadRepAccessor<> accessor{this};
299 return accessor._ConstGetRep ().MakeIterator ();
300 }
301 template <typename T>
302 inline size_t Iterable<T>::size () const
303 {
304 _SafeReadRepAccessor<> accessor{this};
305 return accessor._ConstGetRep ().size ();
306 }
307 template <typename T>
308 inline bool Iterable<T>::empty () const
309 {
310 _SafeReadRepAccessor<> accessor{this};
311 return accessor._ConstGetRep ().empty ();
312 }
313 template <typename T>
314 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER>
315 bool Iterable<T>::Contains (ArgByValueType<T> element, EQUALS_COMPARER&& equalsComparer) const
316 {
317 // grab iterator to first matching item, and contains if not at end; this is faster than using iterators
318 return static_cast<bool> (this->Find ([&element, &equalsComparer] (T i) -> bool { return equalsComparer (i, element); }));
319 }
320 template <typename T>
321 template <ranges::range LHS_CONTAINER_TYPE, ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
322 bool Iterable<T>::SetEquals (const LHS_CONTAINER_TYPE& lhs, const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer)
323 {
324 // @todo OPTIMIZATION - check if constexpr EQUALS_COMPARE == equal_to and if less<> defined - and if so - construct a std::set<> to lookup/compare (do on shorter side)
325 /*
326 * An extremely inefficient but space-constant implementation. N^2 and check
327 * a contains b and b contains a
328 */
329 for (const auto& ti : lhs) {
330 bool contained = false;
331 for (const auto& ri : rhs) {
332 if (equalsComparer (ti, ri)) {
333 contained = true;
334 break;
335 }
336 }
337 if (not contained) {
338 return false;
339 }
340 }
341 for (const auto& ri : rhs) {
342 bool contained = false;
343 for (const auto& ti : lhs) {
344 if (equalsComparer (ti, ri)) {
345 contained = true;
346 break;
347 }
348 }
349 if (not contained) {
350 return false;
351 }
352 }
353 return true;
354 }
355 template <typename T>
356 template <ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
357 inline bool Iterable<T>::SetEquals (const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer) const
358 {
359 return SetEquals (*this, rhs, forward<EQUALS_COMPARER> (equalsComparer));
360 }
361 template <typename T>
362 template <ranges::range LHS_CONTAINER_TYPE, ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
363 bool Iterable<T>::MultiSetEquals (const LHS_CONTAINER_TYPE& lhs, const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer)
364 {
365 auto tallyOf = [&equalsComparer] (const auto& c, Common::ArgByValueType<T> item) -> size_t {
366 size_t total = 0;
367 for (const auto& ti : c) {
368 if (equalsComparer (ti, item)) {
369 ++total;
370 }
371 }
372 return total;
373 };
374 /*
375 * An extremely in-efficient but space-constant implementation. N^3 and check
376 * a contains b and b contains a
377 */
378 for (const auto& ti : lhs) {
379 if (tallyOf (lhs, ti) != tallyOf (rhs, ti)) {
380 return false;
381 }
382 }
383 for (const auto& ti : rhs) {
384 if (tallyOf (lhs, ti) != tallyOf (rhs, ti)) {
385 return false;
386 }
387 }
388 return true;
389 }
390 template <typename T>
391 template <ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
392 inline bool Iterable<T>::MultiSetEquals (const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer) const
393 {
394 return MultiSetEquals (*this, rhs, equalsComparer);
395 }
396 template <typename T>
397 template <ranges::range LHS_CONTAINER_TYPE, ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
398 bool Iterable<T>::SequentialEquals (const LHS_CONTAINER_TYPE& lhs, const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer, bool useIterableSize)
399 {
400 if (useIterableSize) {
401 if (lhs.size () != rhs.size ()) {
402 return false;
403 }
404 }
405 auto li{lhs.begin ()};
406 auto ri{rhs.begin ()};
407 auto le{lhs.end ()};
408 if (useIterableSize) {
409#if qStroika_Foundation_Debug_AssertionsChecked
410 auto re{rhs.end ()};
411 Assert ((li != le) == (ri != re)); // cuz same length, and this requires size cannot change during call
412#endif
413 while (li != le) {
414 if (not equalsComparer (*li, *ri)) {
415 return false;
416 }
417 ++li;
418 ++ri;
419#if qStroika_Foundation_Debug_AssertionsChecked
420 Assert ((li != le) == (ri != re)); // cuz same length, and this requires size cannot change during call
421#endif
422 }
423#if qStroika_Foundation_Debug_AssertionsChecked
424 Assert (li == le and ri == re);
425#endif
426 return true;
427 }
428 else {
429 auto re{rhs.end ()};
430 for (; li != le and ri != re; ++ri, ++li) {
431 if (not equalsComparer (*li, *ri)) {
432 return false;
433 }
434 }
435 // one caused us to end (or more likely both)
436 Assert (li == le or ri == re);
437 // only true if we get to end at the same time
438 return li == le and ri == re;
439 }
440 }
441 template <typename T>
442 template <ranges::range RHS_CONTAINER_TYPE, Common::IEqualsComparer<T> EQUALS_COMPARER>
443 inline bool Iterable<T>::SequentialEquals (const RHS_CONTAINER_TYPE& rhs, EQUALS_COMPARER&& equalsComparer, bool useIterableSize) const
444 {
445 return SequentialEquals (*this, rhs, forward<EQUALS_COMPARER> (equalsComparer), useIterableSize);
446 }
447 template <typename T>
448#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
449 template <typename RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
450#else
451 template <derived_from<Iterable<T>> RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
452#endif
453 inline RESULT_CONTAINER Iterable<T>::Where (INCLUDE_PREDICATE&& includeIfTrue) const
454 {
455 //
456 // LAZY evaluate Iterable<> and for concrete container types, explicitly create the object.
457 //
458 if constexpr (same_as<RESULT_CONTAINER, Iterable<T>>) {
459 // If we have many iterator copies, we need ONE copy of this sharedContext (they all share a reference to the same Iterable)
460 auto sharedContext = make_shared<Iterable<T>> (*this);
461 // If we have many iterator copies, each needs to copy their 'base iterator' (this is their 'index' into the container)
462 // Both the 'sharedContext' and the i' get stored into the lambda closure so they get appropriately copied as you copy iterators
463 function<optional<T> ()> getNext = [sharedContext, i = sharedContext->MakeIterator (), includeIfTrue] () mutable -> optional<T> {
464 while (i and not includeIfTrue (*i)) {
465 ++i;
466 }
467 if (i) {
468 auto tmp = *i;
469 ++i;
470 return move (tmp);
471 }
472 return nullopt;
473 };
474 return CreateGenerator (getNext);
475 }
476 else {
477 return Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue), RESULT_CONTAINER{});
478 }
479 }
480 template <typename T>
481#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
482 template <typename RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
483#else
484 template <derived_from<Iterable<T>> RESULT_CONTAINER, predicate<T> INCLUDE_PREDICATE>
485#endif
486 RESULT_CONTAINER Iterable<T>::Where (INCLUDE_PREDICATE&& includeIfTrue, [[maybe_unused]] RESULT_CONTAINER&& emptyResult) const
487 {
488 if constexpr (same_as<RESULT_CONTAINER, Iterable<T>>) {
489 // no point in emptyResult overload; vector to one spot for Iterable<> lazy implementation
490 return Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue));
491 }
492 else {
493 Require (emptyResult.empty ());
494 RESULT_CONTAINER result = forward<RESULT_CONTAINER> (emptyResult);
495 this->Apply ([&result, &includeIfTrue] (Common::ArgByValueType<T> arg) {
496 if (includeIfTrue (arg)) {
497 Containers::Adapters::Adder<RESULT_CONTAINER>::Add (&result, arg);
498 }
499 });
500 return result;
501 }
503 template <typename T>
504 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER>
505 Iterable<T> Iterable<T>::Distinct (EQUALS_COMPARER&& equalsComparer) const
506 {
507 vector<T> tmp; // Simplistic/stupid/weak implementation
508 if constexpr (same_as<equal_to<T>, EQUALS_COMPARER> and is_invocable_v<less<T>>) {
509 set<T> t1{begin (), end ()};
510 tmp = vector<T>{t1.begin (), t1.end ()};
512 else {
513 for (const auto& i : *this) {
514 if (find_if (tmp.begin (), tmp.end (), [&] (ArgByValueType<T> n) { return equalsComparer (n, i); }) == tmp.end ()) {
515 tmp.push_back (i);
516 }
517 }
518 }
519 function<optional<T> ()> getNext = [container = move (tmp), idx = size_t{0}] () mutable -> optional<T> {
520 if (idx < container.size ()) {
521 return container[idx++];
522 }
523 else {
524 return nullopt;
525 }
526 };
527 return CreateGenerator (getNext);
528 }
529 template <typename T>
530 template <typename RESULT, Common::IPotentiallyComparer<T> EQUALS_COMPARER>
531 Iterable<RESULT> Iterable<T>::Distinct (const function<RESULT (ArgByValueType<T>)>& extractElt, EQUALS_COMPARER&& equalsComparer) const
532 {
533 RequireNotNull (extractElt);
534 vector<RESULT> tmp; // Simplistic/stupid/weak implementation
535 if constexpr (same_as<equal_to<T>, EQUALS_COMPARER> and is_invocable_v<less<T>>) {
536 set<RESULT> t1;
537 for (const T& i : *this) {
538 t1.add (extractElt (i));
539 }
540 tmp = vector<RESULT>{t1.begin (), t1.end ()};
541 }
542 else {
543 for (const T& i : *this) {
544 RESULT item2Test = extractElt (i);
545 if (find_if (tmp.begin (), tmp.end (), [&] (ArgByValueType<T> n) { return equalsComparer (n, item2Test); }) == tmp.end ()) {
546 tmp.push_back (item2Test);
547 }
548 }
549 }
550 function<optional<RESULT> ()> getNext = [container = move (tmp), idx = size_t{0}] () mutable -> optional<RESULT> {
551 if (idx < container.size ()) {
552 return container[idx++];
553 }
554 else {
555 return nullopt;
557 };
558 return CreateGenerator (getNext);
559 }
560 template <typename T>
561 template <ranges::range RESULT_CONTAINER, invocable<T> ELEMENT_MAPPER>
562 RESULT_CONTAINER Iterable<T>::Map (ELEMENT_MAPPER&& elementMapper) const
563 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
564 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>)
565 {
566 using RESULT_ELEMENT = typename RESULT_CONTAINER::value_type;
567 constexpr bool kLazyEvaluateIteration_ = same_as<RESULT_CONTAINER, Iterable<RESULT_ELEMENT>>; // For now use vector and lazy not truly implemented
568 [[maybe_unused]] constexpr bool kOptionalExtractor_ =
569 not convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> and
570 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>;
571 if constexpr (kLazyEvaluateIteration_) {
572 // If we have many iterator copies, we need ONE copy of this sharedContext (they all share a reference to the same Iterable)
573 auto sharedContext = make_shared<Iterable<T>> (*this);
574 // Both the 'sharedContext' and the 'i' get stored into the lambda closure so they get appropriately copied as you copy iterators
575 function<optional<RESULT_ELEMENT> ()> getNext = [sharedContext, i = sharedContext->MakeIterator (),
576 elementMapper] () mutable -> optional<RESULT_ELEMENT> {
577 // tricky. The function we are defining returns nullopt as a sentinel to signal end of iteration. The function we are GIVEN returns nullopt
578 // to signal skip this item. So adjust accordingly
579 if constexpr (kOptionalExtractor_) {
580 while (i) {
581 optional<RESULT_ELEMENT> t = elementMapper (*i);
582 ++i;
583 if (t) {
584 return *t;
585 }
586 }
587 return nullopt;
588 }
589 else {
590 if (i) {
591 RESULT_ELEMENT result = elementMapper (*i);
592 ++i;
593 return move (result);
594 }
595 return nullopt;
596 }
597 };
598 return CreateGenerator (getNext);
599 }
600 else {
601 // subclasseers can replace this 'else' branch if RESULT_CONTAINER cannot or should not be default constructed
602 return this->Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper), RESULT_CONTAINER{});
603 }
604 }
605 template <typename T>
606 template <ranges::range RESULT_CONTAINER, invocable<T> ELEMENT_MAPPER>
607 RESULT_CONTAINER Iterable<T>::Map (ELEMENT_MAPPER&& elementMapper, RESULT_CONTAINER&& emptyResult) const
608 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
609 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>)
610 {
611 using RESULT_ELEMENT = typename RESULT_CONTAINER::value_type;
612 constexpr bool kLazyEvaluateIteration_ = same_as<RESULT_CONTAINER, Iterable<RESULT_ELEMENT>>; // For now use vector and lazy not truly implemented
613 constexpr bool kOptionalExtractor_ = not convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> and
614 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>;
615 if constexpr (kLazyEvaluateIteration_) {
616 return this->Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper)); // ignore useless emptyResult if provided, and invoke other overload to share code
617 }
618 else {
619 RESULT_CONTAINER c = forward<RESULT_CONTAINER> (emptyResult);
620 // reserve iff we know the right size and container supports reserve
621 if constexpr (not kOptionalExtractor_ and requires (RESULT_CONTAINER p) { p.reserve (3u); }) {
622 c.reserve (this->size ());
623 }
624 this->Apply ([&c, &elementMapper] (Common::ArgByValueType<T> arg) {
625 if constexpr (kOptionalExtractor_) {
626 if (auto oarg = elementMapper (arg)) {
627 Containers::Adapters::Adder<RESULT_CONTAINER>::Add (&c, *oarg);
628 }
629 }
630 else {
631 Containers::Adapters::Adder<RESULT_CONTAINER>::Add (&c, elementMapper (arg));
632 }
633 });
634 return c;
635 }
636 }
637 template <typename T>
638 template <typename RESULT_T, invocable<T> CONVERT_TO_RESULT, invocable<RESULT_T, RESULT_T, bool> COMBINER>
639 RESULT_T Iterable<T>::Join (const CONVERT_TO_RESULT& convertToResult, const COMBINER& combiner) const
640 requires (convertible_to<invoke_result_t<CONVERT_TO_RESULT, T>, RESULT_T> and
641 convertible_to<invoke_result_t<COMBINER, RESULT_T, RESULT_T, bool>, RESULT_T>)
642 {
643 RESULT_T result{};
644 size_t idx{0};
645 size_t cnt = this->size ();
646 for (auto i : *this) {
647 if (idx == 0) {
648 result = convertToResult (i);
649 }
650 else {
651 result = combiner (result, convertToResult (i), idx + 1 == cnt);
652 }
653 ++idx;
654 }
655 return result;
656 }
657 template <typename T>
658 Iterable<T> Iterable<T>::Skip (size_t nItems) const
659 {
660 // If we have many iterator copies, we need ONE copy of this sharedContext (they all share a reference to the same Iterable)
661 auto sharedContext = make_shared<Iterable<T>> (*this);
662 // If we have many iterator copies, each needs to copy their 'base iterator' (this is their 'index' into the container)
663 // Both the 'sharedContext' and the i' get stored into the lambda closure so they get appropriately copied as you copy iterators
664 // perIteratorContextNItemsToSkip also must be cloned per iterator instance
665 function<optional<T> ()> getNext = [sharedContext, i = sharedContext->MakeIterator (),
666 perIteratorContextNItemsToSkip = nItems] () mutable -> optional<T> {
667 while (i and perIteratorContextNItemsToSkip > 0) {
668 --perIteratorContextNItemsToSkip;
669 ++i;
670 }
671 if (i) {
672 auto result = *i;
673 ++i;
674 return move (result);
675 }
676 return nullopt;
677 };
678 return CreateGenerator (getNext);
679 }
680 template <typename T>
681 Iterable<T> Iterable<T>::Take (size_t nItems) const
682 {
683 // If we have many iterator copies, we need ONE copy of this sharedContext (they all share a reference to the same Iterable)
684 auto sharedContext = make_shared<Iterable<T>> (*this);
685 // If we have many iterator copies, each needs to copy their 'base iterator' (this is their 'index' into the container)
686 // Both the 'sharedContext' and the i' get stored into the lambda closure so they get appropriately copied as you copy iterators
687 // perIteratorContextNItemsToTake also must be cloned per iterator instance
688 function<optional<T> ()> getNext = [sharedContext, i = sharedContext->MakeIterator (),
689 perIteratorContextNItemsToTake = nItems] () mutable -> optional<T> {
690 if (perIteratorContextNItemsToTake == 0) {
691 return nullopt;
692 }
693 perIteratorContextNItemsToTake--;
694 if (i) {
695 auto result = *i;
696 ++i;
697 return move (result);
698 }
699 return nullopt;
700 };
701 return CreateGenerator (getNext);
702 }
703 template <typename T>
704 Iterable<T> Iterable<T>::Slice (size_t from, size_t to) const
705 {
706 // If we have many iterator copies, we need ONE copy of this sharedContext (they all share a reference to the same Iterable)
707 auto sharedContext = make_shared<Iterable<T>> (*this);
708 // If we have many iterator copies, each needs to copy their 'base iterator' (this is their 'index' into the container)
709 // Both the 'sharedContext' and the i' get stored into the lambda closure so they get appropriately copied as you copy iterators
710 // perIteratorContextNItemsToSkip also must be cloned per iterator instance
711 // perIteratorContextNItemsToTake also must be cloned per iterator instance
712 function<optional<T> ()> getNext = [sharedContext, i = sharedContext->MakeIterator (), perIteratorContextNItemsToSkip = from,
713 perIteratorContextNItemsToTake = to - from] () mutable -> optional<T> {
714 while (i and perIteratorContextNItemsToSkip > 0) {
715 --perIteratorContextNItemsToSkip;
716 ++i;
717 }
718 if (perIteratorContextNItemsToTake == 0) {
719 return nullopt;
720 }
721 perIteratorContextNItemsToTake--;
722 if (i) {
723 auto result = *i;
724 ++i;
725 return move (result);
726 }
727 return nullopt;
728 };
729 return CreateGenerator (getNext);
730 }
731 template <typename T>
732 template <Common::IPotentiallyComparer<T> COMPARER>
733 Iterable<T> Iterable<T>::Top (COMPARER&& cmp) const
734 {
735 // @todo http://stroika-bugs.sophists.com/browse/STK-972 - optimize case where 'iterable' is already sortable
736 vector<T> tmp{this->begin (), Iterator<T>{this->end ()}};
737#if __cpp_lib_execution >= 201603L
738 sort (std::execution::par, tmp.begin (), tmp.end (), forward<COMPARER> (cmp));
739#else
740 sort (tmp.begin (), tmp.end (), forward<COMPARER> (cmp));
741#endif
742 size_t idx{0};
743 function<optional<T> ()> getNext = [tmp, idx] () mutable -> optional<T> {
744 if (idx < tmp.size ()) {
745 return tmp[idx++];
746 }
747 else {
748 return nullopt;
749 }
750 };
751 return CreateGenerator (getNext);
752 }
753 template <typename T>
754 template <Common::IPotentiallyComparer<T> COMPARER>
755 Iterable<T> Iterable<T>::Top (size_t n, COMPARER&& cmp) const
756 {
757 if (n >= size ()) {
758 return Top (forward<COMPARER> (cmp));
759 }
760 // @todo http://stroika-bugs.sophists.com/browse/STK-972 - optimize case where 'iterable' is already sortable
761 vector<T> tmp{this->begin (), Iterator<T>{this->end ()}};
762#if __cpp_lib_execution >= 201603L
763 partial_sort (std::execution::par, tmp.begin (), tmp.begin () + n, tmp.end (), forward<COMPARER> (cmp));
764#else
765 partial_sort (tmp.begin (), tmp.begin () + n, tmp.end (), forward<COMPARER> (cmp));
766#endif
767 size_t idx{0};
768 tmp.erase (tmp.begin () + n, tmp.end ());
769 function<optional<T> ()> getNext = [tmp, idx] () mutable -> optional<T> {
770 if (idx < tmp.size ()) {
771 return tmp[idx++];
772 }
773 else {
774 return nullopt;
775 }
776 };
777 return CreateGenerator (getNext);
778 }
779 template <typename T>
781 {
782 return Top (std::greater<T>{});
783 }
784 template <typename T>
785 inline Iterable<T> Iterable<T>::Top (size_t n) const
786 {
787 return Top (n, std::greater<int>{});
788 }
789 template <typename T>
790 template <Common::IPotentiallyComparer<T> INORDER_COMPARER_TYPE>
791 Iterable<T> Iterable<T>::OrderBy (INORDER_COMPARER_TYPE&& inorderComparer, [[maybe_unused]] Execution::SequencePolicy seq) const
792 {
793 // @todo http://stroika-bugs.sophists.com/browse/STK-972 - optimize case where 'iterable' is already sortable
794 vector<T> tmp{begin (), Iterator<T>{end ()}}; // Somewhat simplistic implementation (always over copy and index so no need to worry about iterator refereincing inside container)
795 // STILL MISSING ON CLANG++-20 Ubuntu 25.04
796#if __cpp_lib_execution >= 201603L
798 stable_sort (tmp.begin (), tmp.end (), inorderComparer);
799 }
800 else {
801 stable_sort (std::execution::par, tmp.begin (), tmp.end (), inorderComparer);
802 }
803#else
804 stable_sort (tmp.begin (), tmp.end (), inorderComparer);
805#endif
806 function<optional<T> ()> getNext = [tmp, idx = size_t{0}] () mutable -> optional<T> {
807 if (idx < tmp.size ()) {
808 return tmp[idx++];
809 }
810 else {
811 return nullopt;
812 }
813 };
814 return CreateGenerator (getNext);
815 }
816 template <typename T>
817 template <Common::IPotentiallyComparer<T> INORDER_COMPARER_TYPE>
818 bool Iterable<T>::IsOrderedBy (INORDER_COMPARER_TYPE&& inorderComparer) const
819 {
820 optional<T> last;
821 for (const T& i : *this) {
822 if (last.has_value ()) [[likely]] {
823 // inorderComparer is 'strict inorder' - so case of equals we keep going...
824 if (inorderComparer (i, *last)) [[unlikely]] {
825 return false;
826 }
827 }
828 last = i;
829 }
830 return true;
831 }
832 template <typename T>
833 inline optional<T> Iterable<T>::First () const
834 {
835 auto i = begin ();
836 return i ? *i : optional<T>{};
837 }
838 template <typename T>
839 template <invocable<T> F>
840 inline optional<T> Iterable<T>::First (F&& that) const
841 requires (convertible_to<invoke_result_t<F, T>, bool>)
842 {
843 constexpr bool kUseIterableRepIteration_ = true; // same semantics, but maybe faster cuz avoids Stroika iterator extra virtual calls overhead
844 if (kUseIterableRepIteration_) {
845 Iterator<T> t = this->_fRep->Find (forward<F> (that), Execution::SequencePolicy::eSeq);
846 return t ? optional<T>{*t} : optional<T>{};
847 }
848 else {
849 for (const auto& i : *this) {
850 if (that (i)) {
851 return i;
852 }
853 }
854 return nullopt;
855 }
856 }
857 template <typename T>
858 template <typename RESULT_T>
859 inline optional<RESULT_T> Iterable<T>::First (const function<optional<RESULT_T> (ArgByValueType<T>)>& that) const
860 {
861 RequireNotNull (that);
862 constexpr bool kUseIterableRepIteration_ = true; // same semantics, but maybe faster cuz avoids Stroika iterator extra virtual calls overhead
863 if (kUseIterableRepIteration_) {
864 optional<RESULT_T> result; // actual result captured in side-effect of lambda
865 auto f = [&that, &result] (ArgByValueType<T> i) { return (result = that (i)).has_value (); };
866 _SafeReadRepAccessor<_IRep> accessor{this};
867 Iterator<T> t = accessor._ConstGetRep ().Find (f, Execution::SequencePolicy::eSeq);
868 return t ? result : optional<RESULT_T>{};
869 }
870 else {
871 for (const auto& i : *this) {
872 if (auto r = that (i)) {
873 return r;
874 }
875 }
876 return nullopt;
877 }
878 }
879 template <typename T>
880 inline T Iterable<T>::FirstValue (ArgByValueType<T> defaultValue) const
881 {
882 return this->First ().value_or (defaultValue);
883 }
884 template <typename T>
885 template <invocable<T> F>
886 inline T Iterable<T>::FirstValue (F&& that, ArgByValueType<T> defaultValue) const
887 requires (convertible_to<invoke_result_t<F, T>, bool>)
888 {
889 return this->First (forward<F> (that)).value_or (defaultValue);
890 }
891 template <typename T>
892 optional<T> Iterable<T>::Last () const
893 {
894 auto i = begin ();
895 if (i) {
896 auto prev = i;
897 while (i) {
898 prev = i;
899 ++i;
900 }
901 return *prev;
902 }
903 return nullopt;
904 }
905 template <typename T>
906 template <invocable<T> F>
907 inline optional<T> Iterable<T>::Last (F&& that) const
908 requires (convertible_to<invoke_result_t<F, T>, bool>)
909 {
910 optional<T> result;
911 for (const auto& i : *this) {
912 if (that (i)) {
913 result = i;
914 }
915 }
916 return result;
917 }
918 template <typename T>
919 template <typename RESULT_T>
920 inline optional<RESULT_T> Iterable<T>::Last (const function<optional<RESULT_T> (ArgByValueType<T>)>& that) const
921 {
922 RequireNotNull (that);
923 optional<T> result;
924 for (const auto& i : *this) {
925 if (auto o = that (i)) {
926 result = *o;
927 }
928 }
929 return result;
930 }
931 template <typename T>
932 inline T Iterable<T>::LastValue (ArgByValueType<T> defaultValue) const
933 {
934 return this->Last ().value_or (defaultValue);
935 }
936 template <typename T>
937 template <invocable<T> F>
938 inline T Iterable<T>::LastValue (F&& that, ArgByValueType<T> defaultValue) const
939 requires (convertible_to<invoke_result_t<F, T>, bool>)
940 {
941 return this->Last (forward<F> (that)).value_or (defaultValue);
942 }
943 template <typename T>
944 bool Iterable<T>::All (const function<bool (ArgByValueType<T>)>& testEachElt) const
945 {
946 RequireNotNull (testEachElt);
947 for (const auto& i : *this) {
948 if (not testEachElt (i)) {
949 return false;
950 }
951 }
952 return true;
953 }
954 template <typename T>
955 template <typename REDUCED_TYPE>
956 optional<REDUCED_TYPE> Iterable<T>::Reduce (const function<REDUCED_TYPE (ArgByValueType<T>, ArgByValueType<T>)>& op) const
957 {
958 optional<REDUCED_TYPE> result;
959 for (const auto& i : *this) {
960 if (result) {
961 result = op (i, *result);
962 }
963 else {
964 result = i;
965 }
966 }
967 return result;
968 }
969 template <typename T>
970 template <typename REDUCED_TYPE>
971 inline REDUCED_TYPE Iterable<T>::ReduceValue (const function<REDUCED_TYPE (ArgByValueType<T>, ArgByValueType<T>)>& op,
972 ArgByValueType<REDUCED_TYPE> defaultValue) const
973 {
974 return Reduce<REDUCED_TYPE> (op).value_or (defaultValue);
975 }
976 template <typename T>
977 inline optional<T> Iterable<T>::Min () const
978 {
979 return Reduce<T> ([] (T lhs, T rhs) -> T { return min (lhs, rhs); });
980 }
981 template <typename T>
982 template <typename RESULT_TYPE>
983 inline RESULT_TYPE Iterable<T>::MinValue (ArgByValueType<RESULT_TYPE> defaultValue) const
984 {
985 return Min ().value_or (defaultValue);
987 template <typename T>
988 inline optional<T> Iterable<T>::Max () const
989 {
990 return Reduce<T> ([] (T lhs, T rhs) -> T { return max (lhs, rhs); });
991 }
992 template <typename T>
993 template <typename RESULT_TYPE>
994 inline RESULT_TYPE Iterable<T>::MaxValue (ArgByValueType<RESULT_TYPE> defaultValue) const
995 {
996 return Max ().value_or (defaultValue);
997 }
998 template <typename T>
999 template <typename RESULT_TYPE>
1000 inline optional<RESULT_TYPE> Iterable<T>::Mean () const
1001 {
1002 Iterator<T> i = begin ();
1003 if (i == end ()) [[unlikely]] {
1004 return nullopt;
1005 }
1006 return Math::Mean (i, end ());
1007 }
1008 template <typename T>
1009 template <typename RESULT_TYPE>
1010 inline RESULT_TYPE Iterable<T>::MeanValue (ArgByValueType<RESULT_TYPE> defaultValue) const
1011 {
1012 return Mean ().value_or (defaultValue);
1013 }
1014 template <typename T>
1015 template <typename RESULT_TYPE>
1016 inline optional<RESULT_TYPE> Iterable<T>::Sum () const
1017 {
1018 return Reduce<RESULT_TYPE> ([] (T lhs, T rhs) { return lhs + rhs; });
1019 }
1020 template <typename T>
1021 template <typename RESULT_TYPE>
1022 inline RESULT_TYPE Iterable<T>::SumValue (ArgByValueType<RESULT_TYPE> defaultValue) const
1023 {
1024 return Sum ().value_or (defaultValue);
1025 }
1026 template <typename T>
1027 template <constructible_from<T> RESULT_TYPE, Common::IPotentiallyComparer<RESULT_TYPE> INORDER_COMPARE_FUNCTION>
1028 inline optional<RESULT_TYPE> Iterable<T>::Median (const INORDER_COMPARE_FUNCTION& compare) const
1029 {
1030 Iterator<T> i = begin ();
1031 if (i == end ()) [[unlikely]] {
1032 return nullopt;
1033 }
1034 return Math::Median<RESULT_TYPE> (i, end (), compare);
1035 }
1036 template <typename T>
1037 template <constructible_from<T> RESULT_TYPE>
1038 inline RESULT_TYPE Iterable<T>::MedianValue (ArgByValueType<RESULT_TYPE> defaultValue) const
1039 {
1040 return Median ().value_or (defaultValue);
1041 }
1042 template <typename T>
1044 {
1045 switch (count) {
1046 case 0:
1047 return Iterable<T>{};
1048 case 1:
1049 return *this;
1050 default: {
1051 // Somewhat simplistic / inefficient implementation
1052 vector<T> origList{begin (), Iterator<T>{end ()}};
1053 size_t repeatCountIndex{1}; // start at one, cuz we don't copy the zeroth time
1054 size_t innerIndex{0};
1055 function<optional<T> ()> getNext = [origList, repeatCountIndex, innerIndex, count] () mutable -> optional<T> {
1056 Again:
1057 if (innerIndex < origList.size ()) [[likely]] {
1058 return origList[innerIndex++];
1059 }
1060 if (repeatCountIndex < count) [[likely]] {
1061 ++repeatCountIndex;
1062 innerIndex = 0;
1063 goto Again;
1064 }
1065 return nullopt;
1066 };
1067 return CreateGenerator (getNext);
1068 }
1069 }
1070 }
1071 template <typename T>
1072 inline bool Iterable<T>::Any () const
1073 {
1074 return not empty ();
1075 }
1076 template <typename T>
1077 inline bool Iterable<T>::Any (const function<bool (ArgByValueType<T>)>& includeIfTrue) const
1078 {
1079 return static_cast<bool> (Find (includeIfTrue));
1080 }
1081 template <typename T>
1082 inline size_t Iterable<T>::Count () const
1083 {
1084 return size ();
1085 }
1086 template <typename T>
1087 inline size_t Iterable<T>::Count (const function<bool (ArgByValueType<T>)>& includeIfTrue) const
1088 {
1089 size_t cnt{};
1090 Apply ([&] (ArgByValueType<T> a) {
1091 if (includeIfTrue (a))
1092 ++cnt;
1093 });
1094 Ensure (cnt == Where (includeIfTrue).size ());
1095 return cnt;
1096 }
1097 template <typename T>
1098 inline size_t Iterable<T>::length () const
1099 {
1100 return size ();
1101 }
1102 template <typename T>
1104 {
1105 return MakeIterator ();
1106 }
1107 template <typename T>
1108 constexpr default_sentinel_t Iterable<T>::end () noexcept
1109 {
1111 }
1112 template <typename T>
1113 inline void Iterable<T>::Apply (const function<void (ArgByValueType<T> item)>& doToElement, Execution::SequencePolicy seq) const
1114 {
1115 RequireNotNull (doToElement);
1116 _SafeReadRepAccessor<> accessor{this};
1117 accessor._ConstGetRep ().Apply (doToElement, seq);
1118 }
1119 template <typename T>
1120 template <predicate<T> THAT_FUNCTION>
1121 inline Iterator<T> Iterable<T>::Find (THAT_FUNCTION&& that, Execution::SequencePolicy seq) const
1122 {
1123 // NB: This transforms perfectly forwarded 'THAT_FUNCTION' and converts it to std::function<> - preventing further inlining at this point -
1124 // just so it can be done
1125 _SafeReadRepAccessor<> accessor{this};
1126 return accessor._ConstGetRep ().Find (forward<THAT_FUNCTION> (that), seq);
1127 }
1128 template <typename T>
1129 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER>
1130 inline Iterator<T> Iterable<T>::Find (Common::ArgByValueType<T> v, EQUALS_COMPARER&& equalsComparer, Execution::SequencePolicy seq) const
1131 {
1132 if constexpr (same_as<remove_cvref_t<EQUALS_COMPARER>, equal_to<T>> and Common::IEqualToOptimizable<T>) {
1133 // This CAN be much faster than the default implementation for this special (but common) case (often a tree structure will have been maintained making this find faster)
1134 _SafeReadRepAccessor<> accessor{this};
1135 return accessor._ConstGetRep ().Find_equal_to (v, seq);
1136 }
1137 else {
1138 return Find ([v, equalsComparer] (Common::ArgByValueType<T> arg) { return equalsComparer (v, arg); }, seq);
1139 }
1140 }
1141 template <typename T>
1142 template <predicate<T> THAT_FUNCTION>
1143 inline Iterator<T> Iterable<T>::Find (const Iterator<T>& startAt, THAT_FUNCTION&& that, [[maybe_unused]] Execution::SequencePolicy seq) const
1144 {
1145 for (Iterator<T> i = startAt; i != end (); ++i) {
1146 if (forward<THAT_FUNCTION> (that) (*i)) [[unlikely]] {
1147 return i;
1148 }
1149 }
1150 return end ();
1151 }
1152 template <typename T>
1153 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER>
1154 Iterator<T> Iterable<T>::Find (const Iterator<T>& startAt, Common::ArgByValueType<T> v, EQUALS_COMPARER&& equalsComparer,
1155 [[maybe_unused]] Execution::SequencePolicy seq) const
1156 {
1157 for (Iterator<T> i = startAt; i != end (); ++i) {
1158 if (forward<EQUALS_COMPARER> (equalsComparer) (v, *i)) [[unlikely]] {
1159 return i;
1160 }
1161 }
1162 return end ();
1163 }
1164 template <typename T>
1165 template <IIterableOfFrom<T> CONTAINER_OF_T, typename... CONTAINER_OF_T_CONSTRUCTOR_ARGS>
1166 inline CONTAINER_OF_T Iterable<T>::As (CONTAINER_OF_T_CONSTRUCTOR_ARGS... args) const
1167 {
1168 // some containers require two iterators as arguments, but Stroika ones work with default_sentinel_t or iterator
1169 // use CONTAINER_OF_T () instead of CONTAINER_OF_T{} because we do want to allow coercion here - since use explicitly called As<>
1170 if constexpr (derived_from<CONTAINER_OF_T, Iterable<T>>) {
1171 return CONTAINER_OF_T (forward<CONTAINER_OF_T_CONSTRUCTOR_ARGS> (args)..., begin (), end ());
1172 }
1173 else {
1174 return CONTAINER_OF_T (forward<CONTAINER_OF_T_CONSTRUCTOR_ARGS> (args)..., begin (), Iterator<T>{end ()});
1175 }
1176 }
1177 template <typename T>
1178 inline T Iterable<T>::Nth (ptrdiff_t n) const
1179 {
1180 Require (n < static_cast<ptrdiff_t> (size ()));
1181 Require (n > -static_cast<ptrdiff_t> (size ()));
1182 size_t useIndex = n >= 0 ? static_cast<size_t> (n) : static_cast<size_t> (n + static_cast<ptrdiff_t> (size ()));
1183 size_t idx = useIndex; // countdown
1184 for (const T& i : *this) {
1185 if (idx == 0) {
1186 return i;
1187 }
1188 --idx;
1189 }
1191 return *begin ();
1192 }
1193 template <typename T>
1194 inline T Iterable<T>::NthValue (ptrdiff_t n, ArgByValueType<T> defaultValue) const
1195 {
1196 size_t useIndex = n >= 0 ? static_cast<size_t> (n) : static_cast<size_t> (n + static_cast<ptrdiff_t> (size ()));
1197 size_t idx = useIndex; // countdown
1198 for (const T& i : *this) {
1199 if (idx == 0) {
1200 return i;
1201 }
1202 --idx;
1203 }
1204 return defaultValue;
1205 }
1206
1207 /*
1208 ********************************************************************************
1209 ******************** Iterable<T>::SequentialEqualsComparer *********************
1210 ********************************************************************************
1212 template <typename T>
1213 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (IEqualsComparer<T>) T_EQUALS_COMPARER>
1214 constexpr Iterable<T>::SequentialEqualsComparer<T_EQUALS_COMPARER>::SequentialEqualsComparer (const T_EQUALS_COMPARER& elementEqualsComparer,
1215 bool useIterableSize)
1216 : fElementComparer{elementEqualsComparer}
1217 , fUseIterableSize{useIterableSize}
1218 {
1219 }
1220 template <typename T>
1221 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (IEqualsComparer<T>) T_EQUALS_COMPARER>
1223 {
1224 return SequentialEquals (lhs, rhs, fElementComparer, fUseIterableSize);
1225 }
1226
1227 /*
1228 ********************************************************************************
1229 ******************** Iterable<T>::SequentialThreeWayComparer *******************
1230 ********************************************************************************
1232 template <typename T>
1233 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (IThreeWayComparer<T>) T_THREEWAY_COMPARER>
1234 constexpr Iterable<T>::SequentialThreeWayComparer<T_THREEWAY_COMPARER>::SequentialThreeWayComparer (const T_THREEWAY_COMPARER& elementComparer)
1235 : fElementComparer{elementComparer}
1236 {
1237 }
1238 DISABLE_COMPILER_MSC_WARNING_START (4701)
1239 template <typename T>
1240 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (IThreeWayComparer<T>) T_THREEWAY_COMPARER>
1241 inline auto Iterable<T>::SequentialThreeWayComparer<T_THREEWAY_COMPARER>::operator() (const Iterable& lhs, const Iterable& rhs) const
1242 {
1243 auto li = lhs.begin ();
1244 auto le = lhs.end ();
1245 auto ri = rhs.begin ();
1246 auto re = rhs.end ();
1247 DISABLE_COMPILER_MSC_WARNING_START (6001)
1248 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
1249 // no need for c' initialization cuz only used in else return at end, but never get there
1250 // unless set at least once
1251 optional<strong_ordering> c;
1252 while ((li != le) and (ri != re) and (c = fElementComparer (*li, *ri)) == strong_ordering::equal) {
1253 ++li;
1254 ++ri;
1255 }
1256 if (li == le) {
1257 if (ri == re) {
1258 return strong_ordering::equal; // all items same and loop ended with both things at end
1259 }
1260 else {
1261 return strong_ordering::less; // lhs shorter but an initial sequence of rhs
1262 }
1263 }
1264 else if (ri == re) {
1265 return strong_ordering::greater; // rhs shorter but an initial sequence of lhs
1266 }
1267 else {
1268 Assert (li != le and ri != re);
1269 Assert (c == fElementComparer (*li, *ri));
1270 return c.value ();
1271 }
1272 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
1273 DISABLE_COMPILER_MSC_WARNING_END (6001)
1274 }
1275 DISABLE_COMPILER_MSC_WARNING_END (4701)
1276}
#define EnsureNotNull(p)
Definition Assertions.h:340
#define RequireNotReached()
Definition Assertions.h:385
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
#define RequireExpression(c)
Definition Assertions.h:267
#define AssertNotReached()
Definition Assertions.h:355
#define EnsureMember(p, c)
Definition Assertions.h:319
T UncheckedDynamicCast(T1 &&arg) noexcept
return the same value as dynamic_cast<T> would have, except instead of checking nullptr,...
Definition Cast.inl:13
Iterable< T > CreateGenerator(const function< optional< T >()> &getNext)
Create an Iterable<T> from a function that returns optional<T> - treating nullopt as meaning the END ...
Definition Generator.inl:55
nonvirtual SharedByValue_State GetSharingState() const
virtual Iterator< value_type > MakeIterator() const =0
virtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq) const
Definition Iterable.inl:59
virtual Iterator< value_type > Find_equal_to(const ArgByValueType< T > &v, Execution::SequencePolicy seq) const
Definition Iterable.inl:79
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
nonvirtual RESULT_T Join(const CONVERT_TO_RESULT &convertToResult=kDefaultToStringConverter<>, const COMBINER &combiner=Characters::kDefaultStringCombiner) const
ape the JavaScript/python 'join' function - take the parts of 'this' iterable and combine them into a...
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual RESULT_TYPE MaxValue(ArgByValueType< RESULT_TYPE > defaultValue={}) const
nonvirtual Iterable< T > Slice(size_t from, size_t to) const
Definition Iterable.inl:704
nonvirtual Iterator< T > Find(THAT_FUNCTION &&that, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument bool-returning function (or lambda) on each element of the container,...
static bool SetEquals(const LHS_CONTAINER_TYPE &lhs, const RHS_CONTAINER_TYPE &rhs, EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{})
Definition Iterable.inl:322
nonvirtual bool Any() const
Any() same as not empty (); Any (includeIfTrue) returns true iff includeIfTrue returns true on any va...
nonvirtual optional< T > Max() const
Definition Iterable.inl:988
nonvirtual RESULT_TYPE MedianValue(ArgByValueType< RESULT_TYPE > defaultValue={}) const
nonvirtual optional< RESULT_TYPE > Mean() const
nonvirtual size_t length() const
STL-ish alias for size() - really in STL only used in string, I think, but still makes sense as an al...
nonvirtual Iterable< T > Top() const
return the top/largest (possibly just top N) values from this Iterable<T>
Definition Iterable.inl:780
nonvirtual CONTAINER_OF_T As(CONTAINER_OF_T_CONSTRUCTOR_ARGS... args) const
nonvirtual Iterable< T > Distinct(EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{}) const
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
functional API which iterates over all members of an Iterable, applies a map function to each element...
nonvirtual size_t Count() const
with no args, same as size, with function filter arg, returns number of items that pass.
nonvirtual bool IsOrderedBy(INORDER_COMPARER_TYPE &&inorderComparer=INORDER_COMPARER_TYPE{}) const
nonvirtual Iterable< T > Repeat(size_t count) const
nonvirtual Memory::SharedByValue_State _GetSharingState() const
Definition Iterable.inl:291
nonvirtual optional< T > First() const
return first element in iterable, or if 'that' specified, first where 'that' is true,...
Definition Iterable.inl:833
nonvirtual RESULT_TYPE MinValue(ArgByValueType< RESULT_TYPE > defaultValue={}) const
nonvirtual bool All(const function< bool(ArgByValueType< T >)> &testEachElt) const
return true iff argument predicate returns true for each element of the iterable
Definition Iterable.inl:944
nonvirtual bool Contains(ArgByValueType< T > element, EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{}) const
nonvirtual optional< T > Min() const
Definition Iterable.inl:977
nonvirtual T NthValue(ptrdiff_t n, ArgByValueType< T > defaultValue={}) const
Find the Nth element of the Iterable<>, but allow for n to be out of range, and just return argument ...
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:302
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
produce a subset of this iterable where argument function returns true
nonvirtual T Nth(ptrdiff_t n) const
Find the Nth element of the Iterable<>
nonvirtual Iterable< T > Take(size_t nItems) const
Definition Iterable.inl:681
nonvirtual RESULT_TYPE MeanValue(ArgByValueType< RESULT_TYPE > defaultValue={}) const
nonvirtual optional< RESULT_TYPE > Median(const INORDER_COMPARE_FUNCTION &compare={}) const
nonvirtual Iterable< T > OrderBy(INORDER_COMPARER_TYPE &&inorderComparer=INORDER_COMPARER_TYPE{}, Execution::SequencePolicy seq=Execution::SequencePolicy::ePar) const
nonvirtual Iterable< T > Skip(size_t nItems) const
Definition Iterable.inl:658
nonvirtual RESULT_TYPE SumValue(ArgByValueType< RESULT_TYPE > defaultValue={}) const
nonvirtual optional< REDUCED_TYPE > Reduce(const function< REDUCED_TYPE(ArgByValueType< T >, ArgByValueType< T >)> &op) const
Walk the entire list of items, and use the argument 'op' to combine (reduce) items to a resulting sin...
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
Iterable(const Iterable &) noexcept=default
Iterable are safely copyable (by value). Since Iterable uses COW, this just copies the underlying poi...
nonvirtual optional< RESULT_TYPE > Sum() const
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:308
nonvirtual T LastValue(ArgByValueType< T > defaultValue={}) const
Definition Iterable.inl:932
static bool SequentialEquals(const LHS_CONTAINER_TYPE &lhs, const RHS_CONTAINER_TYPE &rhs, EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{}, bool useIterableSize=false)
Definition Iterable.inl:398
static bool MultiSetEquals(const LHS_CONTAINER_TYPE &lhs, const RHS_CONTAINER_TYPE &rhs, EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{})
Definition Iterable.inl:363
nonvirtual optional< T > Last() const
return last element in iterable, or if 'that' specified, last where 'that' is true,...
Definition Iterable.inl:892
nonvirtual T FirstValue(ArgByValueType< T > defaultValue={}) const
return first element in iterable provided default
Definition Iterable.inl:880
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
nonvirtual REDUCED_TYPE ReduceValue(const function< REDUCED_TYPE(ArgByValueType< T >, ArgByValueType< T >)> &op, ArgByValueType< REDUCED_TYPE > defaultValue={}) const
nonvirtual Iterator< T > MakeIterator() const
Create an iterator object which can be used to traverse the 'Iterable'.
Definition Iterable.inl:296
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225
static constexpr default_sentinel_t GetEmptyIterator() noexcept
Used by someContainer::end ()
Definition Iterator.inl:247
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
SequencePolicy
equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy,...
@ eSeq
default case - not parallelized