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