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