Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
MultiSet.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_MultiSet_h_
5#define _Stroika_Foundation_Containers_MultiSet_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <set>
10
11#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Common/Concepts.h"
14#include "Stroika/Foundation/Common/CountedValue.h"
17
18#include "Stroika/Foundation/Containers/Common.h"
19#include "Stroika/Foundation/Containers/DefaultTraits/MultiSet.h"
20
21/**
22 * \file
23 *
24 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
25 *
26 * TODO:
27 * @todo http://stroika-bugs.sophists.com/browse/STK-428 - See how to map EqualsComparer to CountedValue we use
28 * probably added template param to CountedValue - TRAITS.
29 *
30 * MAYBE add using MultisetEntryType = CountedValue<T>; to
31 * TRAITS object. Then use throughout.... Not sure this is worth parameterizing, but
32 * COULD help address unifying the EQUALS support!
33 *
34 * @todo IMPORTANT - FIX TRAITS support like I did for Mapping/Set<> - Sorted...
35 * see git commit # 3c5bf0ecd686af850ff77761cf94142a33f48588
36 *
37 * Key is adding MultiSetTraitsType to the traits and making generic base class
38 * for MultiSet<T> - its traits - same as wtih SortedTraits.
39 *
40 * Also likewise key for MultiSet_stdmap<> - cuz now you cannot assign MultiSet_stdmap<> to
41 * MultiSet<T>!!!!
42 *
43 * @todo Consider rewriting all MultiSet<> concrete types using Mapping<T,counttype> concrete impl?
44 * Might not work easily but document why... (Add () semantics - but maybe).
45 *
46 *
47 */
48
50
52 using Common::CountedValue;
53 using Common::IEqualsComparer;
54 using Traversal::IInputIterator;
55 using Traversal::IIterableOfTo;
56 using Traversal::Iterable;
57 using Traversal::Iterator;
58
59 /**
60 * A MultiSet<T, TRAITS> A collection of elements where each time you add something, the MultiSet tallies
61 * the number of times that thing has been entered. This is not a commonly used class, but handy when you want to count things.
62 *
63 * MultiSet<T, TRAITS> inherits from Iterable<CountedValue<T>> instead of Iterable<T> because if you are
64 * using a MultiSet, you probably want access to the counts as you iterate - not just the
65 * unique elements (though we make it easy to get that iterator too with Elements () or
66 * UniqueElements ()).
67 *
68 * A MultiSet<T, TRAITS> makes no promises about ordering of elements in iteration.
69 *
70 * @see http://en.wikipedia.org/wiki/Multiset_(abstract_data_type)#Multiset
71 *
72 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
73 *
74 * @aliases Tally (Stroika 1.0), Bag (from SmallTalk-80)
75 *
76 * \em Concrete Implementations:
77 * o @see Concrete::MultiSet_Array<>
78 * o @see Concrete::MultiSet_LinkedList<>
79 * o @see Concrete::SortedMultiSet_stdmap<>
80 * o @see Concrete::SortedMultiSet_SkipList<>
81 *
82 * \em Factory:
83 * @see MultiSet_Factory<> to see default implementations.
84 *
85 * \note Though the name (and function) is quite similar to the std::multiset<>, this version is generally MUCH
86 * more convenient to use, in that when you iterate over it, you get the items with their counts, not
87 * each element repeated that many times (in some arbitrary order). To get the std::multiset behavior, use
88 * MultiSet<T>::Elements ().
89 *
90 * \note <a href="ReadMe.md#Container Element comparisons">Container Element comparisons</a>:
91 * See about ElementInOrderComparerType, ElementThreeWayComparerType and GetElementThreeWayComparer etc
92 *
93 * \note See <a href="./ReadMe.md">ReadMe.md</a> for common features of all Stroika containers (especially
94 * constructors, iterators, etc)
95 *
96 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
97 * o static_assert (equality_comparable<MultiSet<T>>);
98 *
99 * o Multisets intrinsically know how to compare their elements (for equality) - even if equal_to<T> not defined
100 *
101 * Two MultiSet are considered equal if they contain the same elements (by comparing them with GetElementEqualsComparer ())
102 * with the same count. In short, they are equal if OccurrencesOf() each item in the LHS equals the OccurrencesOf()
103 * the same item in the RHS.
104 *
105 * Note - this computation MAYBE very expensive, and not optimized (maybe do better in a future release - see TODO).
106 * @todo - document computational complexity
107 *
108 * ThreeWayComparer support is NOT provided for Multisets, because there is no intrinsic ordering among the elements
109 * of the multiset (keys) - even if there was some way to compare the T elements.
110 *
111 */
112 template <typename T, typename TRAITS = DefaultTraits::MultiSet<T>>
113 class [[nodiscard]] MultiSet : public Iterable<typename TRAITS::CountedValueType> {
114 private:
116
117 public:
118 /**
119 * Use this typedef in templates to recover the basic functional container pattern of concrete types.
120 */
122
123 public:
124 /**
125 * Just a short-hand for the 'TRAITS' part of MultiSet<T,TRAITS>. This is often handy to use in
126 * building other templates.
127 */
128 using TraitsType = TRAITS;
129
130 public:
131 /**
132 * \brief MultiSetOfElementType is just a handy copy of the *T* template type which this
133 * MultiSet<T, TRAITS> parameterizes counting.
134 */
136
137 public:
138 /**
139 * \brief
140 */
141 using CounterType = typename TraitsType::CounterType;
142
143 public:
144 /**
145 * typename TRAITS::CountedValueType - not 'T' itself
146 *
147 * @see inherited::value_type
148 */
150 static_assert (same_as<typename TRAITS::CountedValueType, value_type>);
151
152 protected:
153 class _IRep;
154
155 public:
156 /**
157 * This is the type returned by GetElementEqualsComparer () and CAN be used as the argument to a MultiSet<> as EqualityComparer, but
158 * we allow any template in the Set<> CTOR for an equalityComparer that follows the IEqualsComparer concept.
159 */
161 Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eEquals, function<bool (ArgByValueType<T>, ArgByValueType<T>)>>;
162 static_assert (IEqualsComparer<ElementEqualityComparerType, T>);
163
164 public:
165 /**
166 * All constructors either copy their source comparer (copy/move CTOR), or use the provided argument comparer
167 * (which in turn defaults to equal_to<T>).
168 *
169 * \note For efficiency sake, the base constructor takes a templated EQUALS_COMPARER (avoiding translation to function<bool(T,T)>>
170 * so the REP can see the actual type, but the MultiSet API itself erases this specific type using std::function.
171 *
172 * \pre IEqualsComparer<EQUALS_COMPARER> - for constructors with that type parameter
173 *
174 * \note <a href="ReadMe.md#Container Constructors">See general information about container constructors that applies here</a>
175 *
176 * \par Example Usage
177 * \code
178 * Collection<int> c;
179 * std::vector<int> v;
180 *
181 * MultiSet<int> s1 = {1, 2, 3};
182 * MultiSet<int> s2 = s1;
183 * MultiSet<int> s3{ s1 };
184 * MultiSet<int> s4{ s1.begin (), s1.end () };
185 * MultiSet<int> s5{ c };
186 * MultiSet<int> s6{ v };
187 * MultiSet<int> s7{ v.begin (), v.end () };
188 * MultiSet<int> s8{ move (s1) };
189 * MultiSet<int> s9{ Common::DeclareEqualsComparer([](int l, int r) { return l == r; }), c};
190 * \endcode
191 */
192 MultiSet ()
193 requires (IEqualsComparer<equal_to<T>, T>);
194 template <IEqualsComparer<T> EQUALS_COMPARER>
195 explicit MultiSet (EQUALS_COMPARER&& equalsComparer);
196 MultiSet (MultiSet&&) noexcept = default;
197 MultiSet (const MultiSet&) noexcept = default;
198 MultiSet (const initializer_list<T>& src)
199 requires (IEqualsComparer<equal_to<T>, T>);
200 template <IEqualsComparer<T> EQUALS_COMPARER>
201 MultiSet (EQUALS_COMPARER&& equalsComparer, const initializer_list<T>& src);
202 MultiSet (const initializer_list<value_type>& src);
203 template <IEqualsComparer<T> EQUALS_COMPARER>
204 MultiSet (EQUALS_COMPARER&& equalsComparer, const initializer_list<value_type>& src);
205 template <IIterableOfTo<typename TRAITS::CountedValueType> ITERABLE_OF_ADDABLE>
206 explicit MultiSet (ITERABLE_OF_ADDABLE&& src)
207 requires (IEqualsComparer<equal_to<T>, T> and not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, MultiSet<T, TRAITS>>)
208#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
209 : MultiSet{}
210 {
211 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
212 _AssertRepValidType ();
213 }
214#endif
215 ;
216 template <IEqualsComparer<T> EQUALS_COMPARER, IIterableOfTo<typename TRAITS::CountedValueType> ITERABLE_OF_ADDABLE>
217 MultiSet (EQUALS_COMPARER&& equalsComparer, ITERABLE_OF_ADDABLE&& src);
218 template <IInputIterator<typename TRAITS::CountedValueType> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
219 MultiSet (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
220 requires (IEqualsComparer<equal_to<T>, T>);
221 template <IEqualsComparer<T> EQUALS_COMPARER, IInputIterator<typename TRAITS::CountedValueType> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
222 MultiSet (EQUALS_COMPARER&& equalsComparer, ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end);
223
224 protected:
225 explicit MultiSet (shared_ptr<_IRep>&& rep) noexcept;
226 explicit MultiSet (const shared_ptr<_IRep>& rep) noexcept;
227
228 public:
229 nonvirtual MultiSet& operator= (MultiSet&&) noexcept = default;
230 nonvirtual MultiSet& operator= (const MultiSet&) = default;
231
232 public:
233 /**
234 * Contains (item) is equivalent to OccurrencesOf (item) != 0, but maybe faster (since it doesn't need to compute
235 * the fully tally).
236 */
237 nonvirtual bool Contains (ArgByValueType<T> item) const;
238
239 public:
240 /**
241 * \note mutates container
242 */
243 nonvirtual void Add (ArgByValueType<T> item);
244 nonvirtual void Add (ArgByValueType<T> item, CounterType count);
245 nonvirtual void Add (const value_type& item);
246
247 public:
248 /**
249 * @aliases AddAll/2 is alias for .net AddRange ()
250 * and AddAll/2 - the iterator can be Iterator<T> or Iterator<typename TRAITS::CountedValueType>
251 *
252 * \pre IInputIterator<typename TRAITS::CountedValueType> or IIterableOfTo<typename TRAITS::CountedValueType>
253 *
254 * \note mutates container
255 */
256 template <IInputIterator<typename TRAITS::CountedValueType> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
257 nonvirtual void AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end);
258 template <IIterableOfTo<typename TRAITS::CountedValueType> ITERABLE_OF_ADDABLE>
259 nonvirtual void AddAll (ITERABLE_OF_ADDABLE&& items);
260
261 public:
262 /**
263 * \brief remove the argument data from the multiset. The data specified MUST exist (require) - else use RemoveIf ()
264 *
265 * \see RemoveIf
266 *
267 * \pre count >= 1
268 *
269 * This function requires that the iterator 'i' came from this container.
270 *
271 * The value pointed to by 'i' is removed.
272 *
273 * If using the item/count or just item overloads, then MultiSet<> requires that the removed items are present.
274 *
275 * \param nextI - if provided (not null) - will be filled in with the next value after where iterator i is pointing - since i is invalidated by changing the container)
276 *
277 * \note mutates container
278 */
279 nonvirtual void Remove (ArgByValueType<T> item, CounterType count = 1);
280 nonvirtual void Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI = nullptr);
281
282 public:
283 /**
284 * \brief remove the argument data from the multiset (can specify remove of more than are present) - returns number actually removed (multisets never have count < 0)
285 *
286 * \pre count >= 1
287 *
288 * \note mutates container
289 */
290 nonvirtual size_t RemoveIf (ArgByValueType<T> item, CounterType count = 1);
291
292 public:
293 /**
294 * \brief RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items.
295 *
296 * The no-arg overload removes all (quickly).
297 *
298 * The overloads that remove some subset of the items returns the number of items so removed.
299 *
300 * \note mutates container
301 */
302 nonvirtual void RemoveAll ();
303 nonvirtual size_t RemoveAll (ArgByValueType<T> item);
304
305 public:
306 /**
307 * if newCount == 0, equivalent to Remove(i). Require not i.Done () - so it must point to a given item.
308 *
309 * On return, nextI, if non-null, will point to the next element after the argument 'i' (useful for iteration).
310 *
311 * \note mutates container
312 */
313 nonvirtual void UpdateCount (const Iterator<value_type>& i, CounterType newCount, Iterator<value_type>* nextI = nullptr);
314
315 public:
316 /**
317 * It's perfectly legal for i to be missing before or after.
318 *
319 * \note mutates container
320 */
321 nonvirtual void SetCount (ArgByValueType<T> i, CounterType newCount);
322
323 public:
324 /**
325 * OccurrencesOf() returns the number of occurrences of 'item' in the tally. The items are compared with operator==.
326 *
327 * If there are no copies of item in the MultiSet, 0 is returned.
328 */
329 nonvirtual CounterType OccurrencesOf (ArgByValueType<T> item) const;
330
331 public:
332 /**
333 * Returns the sum of all this MultiSets contained elements. This is equivalent
334 * to Elements ().size ().
335 */
336 nonvirtual CounterType TotalOccurrences () const;
337
338 public:
339 /**
340 * @aliases Remove ().
341 */
342 nonvirtual Iterator<value_type> erase (const Iterator<value_type>& i);
343
344 public:
345 /**
346 * @aliases RemoveAll ().
347 *
348 * \note mutates container
349 */
350 nonvirtual void clear ();
351
352 public:
353 /**
354 * This is like the MultiSet was a Collection<T> (or plain Iterable<T>). If something is in there N times,
355 * it will show up in iteration N times. No guarantee is made as to order of iteration.
356 *
357 * \par Example Usage
358 * \code
359 * MultiSet<T> t;
360 * for (T i : t.Elements ()) {
361 * // Like a collection iteration - if item t.OccurrencesOf(i) == 3, then this will be encountered 3 times in the iteration
362 * }
363 * \endcode
364 *
365 * Elements () makes no guarantees about whether or not modifications to the underlying MultiSet<> will
366 * appear in the Elements() Iterable<T>.
367 *
368 * @see UniqueElements
369 */
370 nonvirtual Iterable<T> Elements () const;
371
372 public:
373 /**
374 * \par Example Usage
375 * \code
376 * MultiSet<T> t;
377 * for (T i : t.UniqueElements ()) {
378 * // Like a set iteration - if item t.OccurrencesOf(i) == 3, then this will be encountered 1 times in the iteration
379 * }
380 * \endcode
381 *
382 * UniqueElements () operates as if it copies the data from the original container, and will not reflect any subsequent changes.
383 */
384 nonvirtual Iterable<T> UniqueElements () const;
385
386 public:
387 /**
388 * \brief Find the most commonly occurring elements of the multiset (list with count ordered most to last)
389 *
390 * \par Example Usage
391 * \code
392 * MultiSet<int> test{1, 1, 5, 1, 6, 5};
393 * EXPECT_TRUE (test.Top ().SequentialEquals ({{1, 3}, {5, 2}, {6, 1}}));
394 * EXPECT_TRUE (test.Top (1).SequentialEquals ({{1, 3}}));
395 * \endcode
396 *
397 * \note n is allowed to exceed the size of the MultiSet, and the result of Top (n) maybe fewer than n values
398 *
399 * \See Iterable<T>::Top
400 * \See TopElements
401 */
402 nonvirtual Iterable<typename TRAITS::CountedValueType> Top () const;
403 nonvirtual Iterable<typename TRAITS::CountedValueType> Top (size_t n) const;
404
405 public:
406 /**
407 * \brief Find the most commonly occurring elements of the multiset (list of elements - without count - ordered most to last)
408 *
409 * \par Example Usage
410 * \code
411 * MultiSet<int> test{1, 1, 5, 1, 6, 5};
412 * EXPECT_TRUE (test.TopElements ().SequentialEquals ({1, 5, 6}));
413 * EXPECT_TRUE (test.TopElements (1).SequentialEquals ({1}));
414 * \endcode
415 *
416 * \note n is allowed to exceed the size of the MultiSet, and the result of Top (n) maybe fewer than n values
417 *
418 * \See Iterable<T>::Top
419 * \See Top
420 */
421 nonvirtual Iterable<T> TopElements () const;
422 nonvirtual Iterable<T> TopElements (size_t n) const;
423
424 public:
425 /**
426 * \brief 'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to MultiSet, and improve that case to clone properties from this rep (such is rep type, ordering, etc).
427 */
428 template <typename RESULT_CONTAINER = MultiSet<T, TRAITS>, invocable<T> ELEMENT_MAPPER>
429 nonvirtual RESULT_CONTAINER Map (ELEMENT_MAPPER&& elementMapper) const
430 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, typename TRAITS::CountedValueType>, typename RESULT_CONTAINER::value_type> or
431 convertible_to<invoke_result_t<ELEMENT_MAPPER, typename TRAITS::CountedValueType>, optional<typename RESULT_CONTAINER::value_type>>);
432
433 public:
434 /**
435 * See Iterable<T>::Where - except defaults to MultiSet, and handles cloning rep properties for that special case
436 */
437 template <derived_from<Iterable<typename TRAITS::CountedValueType>> RESULT_CONTAINER = MultiSet<T, TRAITS>, predicate<typename TRAITS::CountedValueType> INCLUDE_PREDICATE>
438 nonvirtual RESULT_CONTAINER Where (INCLUDE_PREDICATE&& includeIfTrue) const;
439
440 public:
441 /**
442 * Return the function used to compare if two elements are equal (not to be confused with MultiSet<>::EqualsComparer)
443 */
444 nonvirtual ElementEqualityComparerType GetElementEqualsComparer () const;
445
446 public:
447 /**
448 * @see comparisons section of @MultiSet documentation
449 */
450 nonvirtual bool operator== (const MultiSet& rhs) const;
451
452 public:
453 /**
454 * Synonym for Add (), or AddAll() (depending on argument);
455 *
456 * \note mutates container
457 */
458 nonvirtual MultiSet& operator+= (ArgByValueType<T> item);
459 nonvirtual MultiSet& operator+= (const MultiSet& items);
460
461 protected:
462 /**
463 * \brief Utility to get WRITABLE underlying shared_ptr (replacement for what we normally do - _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ())
464 * but where we also handle the cloning/patching of the associated iterator
465 *
466 * When you have a non-const operation (such as Remove) with an argument of an Iterator<>, then due to COW,
467 * you may end up cloning the container rep, and yet the Iterator<> contains a pointer to the earlier rep (and so maybe unusable).
468 *
469 * Prior to Stroika 2.1b14, this was handled elegantly, and automatically, by the iterator patching mechanism. But that was deprecated (due to cost, and
470 * rarity of use), in favor of this more restricted feature, where we just patch the iterators on an as-needed basis.
471 *
472 * \todo @todo - could be smarter about moves and avoid some copies here - I think, and this maybe performance sensitive enough to look into that... (esp for COMMON case where no COW needed)
473 */
474 nonvirtual tuple<_IRep*, Iterator<value_type>> _GetWritableRepAndPatchAssociatedIterator (const Iterator<value_type>& i);
475
476 protected:
477 /**
478 */
479 template <typename T2>
480 using _SafeReadRepAccessor = typename inherited::template _SafeReadRepAccessor<T2>;
481
482 protected:
483 /**
484 */
485 template <typename T2>
486 using _SafeReadWriteRepAccessor = typename inherited::template _SafeReadWriteRepAccessor<T2>;
487
488 protected:
489 nonvirtual void _AssertRepValidType () const;
490 };
491
492 /**
493 */
494 template <typename T, typename TRAITS>
495 class MultiSet<T, TRAITS>::_IRep : public Iterable<typename TRAITS::CountedValueType>::_IRep {
496 private:
498
499 public:
500 using CounterType = typename MultiSet::CounterType;
501
502 protected:
503 _IRep () = default;
504
505 public:
506 virtual ElementEqualityComparerType GetElementEqualsComparer () const = 0;
507 virtual shared_ptr<_IRep> CloneEmpty () const = 0;
508 virtual shared_ptr<_IRep> CloneAndPatchIterator (Iterator<value_type>* i) const = 0;
509 virtual bool Equals (const _IRep& rhs) const = 0;
510 virtual bool Contains (ArgByValueType<T> item) const = 0;
511 virtual void Add (ArgByValueType<T> item, CounterType count) = 0;
512 virtual size_t RemoveIf (ArgByValueType<T> item, CounterType count) = 0;
513 virtual void Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI) = 0;
514 virtual void UpdateCount (const Iterator<value_type>& i, CounterType newCount, Iterator<value_type>* nextI) = 0;
515 virtual CounterType OccurrencesOf (ArgByValueType<T> item) const = 0;
516
517 /*
518 * Reference Implementations (often not used except for ensure's, but can be used for
519 * quickie backends).
520 *
521 * Importantly, these are all non-virtual so not actually pulled in or even compiled unless
522 * the sucblass refers to the method in a subclass virtual override.
523 */
524 protected:
525 nonvirtual bool _Equals_Reference_Implementation (const _IRep& rhs) const;
526 };
527
528}
529
530/*
531 ********************************************************************************
532 ***************************** Implementation Details ***************************
533 ********************************************************************************
534 */
535#include "MultiSet.inl"
536
537#endif /*_Stroika_Foundation_Containers_MultiSet_h_ */
typename inherited::value_type value_type
Definition MultiSet.h:149
T MultiSetOfElementType
MultiSetOfElementType is just a handy copy of the T template type which this MultiSet<T,...
Definition MultiSet.h:135
Implementation detail for iterator implementors.
Definition Iterable.h:1569
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
T value_type
value_type is an alias for the type iterated over - like vector<T>::value_type
Definition Iterable.h:248
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225
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