TrinityCore
Containers.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_CONTAINERS_H
19#define TRINITY_CONTAINERS_H
20
21#include "Concepts.h"
22#include "Define.h"
23#include "Random.h"
24#include <algorithm>
25#include <iterator>
26#include <span>
27#include <stdexcept>
28#include <type_traits>
29#include <utility>
30#include <vector>
31
32namespace Trinity
33{
34 template <class T>
36 {
37 public:
38 using iterator_category = std::output_iterator_tag;
39 using value_type = void;
40 using pointer = T*;
41 using reference = T&;
42 using difference_type = std::ptrdiff_t;
43
44 CheckedBufferOutputIterator(T* buf, size_t n) : _buf(buf), _end(buf+n) {}
45
46 T& operator*() const { check(); return *_buf; }
49
50 size_t remaining() const { return (_end - _buf); }
51
52 private:
53 T* _buf;
54 T* _end;
55 void check() const
56 {
57 if (!(_buf < _end))
58 throw std::out_of_range("index");
59 }
60 };
61
62 namespace Containers
63 {
64 // resizes <container> to have at most <requestedSize> elements
65 // if it has more than <requestedSize> elements, the elements to keep are selected randomly
66 template <std::ranges::forward_range C>
67 void RandomResize(C& container, std::size_t requestedSize)
68 {
69 uint32 elementsToProcess = uint32(std::ranges::size(container));
70 if (elementsToProcess <= requestedSize)
71 return;
72
73 auto keepIt = std::ranges::begin(container), curIt = std::ranges::begin(container);
74 uint32 elementsToKeep = uint32(requestedSize);
75 while (elementsToProcess)
76 {
77 // this element has chance (elementsToKeep / elementsToProcess) of being kept
78 if (urand(1, elementsToProcess) <= elementsToKeep)
79 {
80 if (keepIt != curIt)
81 *keepIt = std::move(*curIt);
82 ++keepIt;
83 --elementsToKeep;
84 }
85 ++curIt;
86 --elementsToProcess;
87 }
88 container.erase(keepIt, std::ranges::end(container));
89 }
90
91 template <std::ranges::forward_range C, invocable_r<bool, std::ranges::range_reference_t<C>> Predicate>
92 void RandomResize(C& container, Predicate&& predicate, std::size_t requestedSize)
93 {
95 C containerCopy;
96 std::ranges::copy_if(container, std::inserter(containerCopy, std::ranges::end(containerCopy)), std::forward<Predicate>(predicate));
97
98 if (requestedSize)
99 RandomResize(containerCopy, requestedSize);
100
101 container = std::move(containerCopy);
102 }
103
104 /*
105 * Select a random element from a container.
106 *
107 * Note: container cannot be empty
108 */
109 template <std::ranges::input_range C>
110 inline auto SelectRandomContainerElement(C const& container) -> std::add_const_t<decltype(*std::ranges::begin(container))>&
111 {
112 auto it = std::ranges::begin(container);
113 std::ranges::advance(it, urand(0, uint32(std::ranges::distance(container)) - 1));
114 return *it;
115 }
116
117 /*
118 * Select a random element from a container where each element has a different chance to be selected.
119 *
120 * @param container Container to select an element from
121 * @param weights Chances of each element to be selected, must be in the same order as elements in container.
122 * Caller is responsible for checking that sum of all weights is greater than 0.
123 *
124 * Note: container cannot be empty
125 */
126 template <std::ranges::input_range C>
127 inline auto SelectRandomWeightedContainerElement(C const& container, std::span<double> const& weights) -> decltype(std::ranges::begin(container))
128 {
129 auto it = std::ranges::begin(container);
130 std::ranges::advance(it, urandweighted(weights.size(), weights.data()));
131 return it;
132 }
133
134 /*
135 * Select a random element from a container where each element has a different chance to be selected.
136 *
137 * @param container Container to select an element from
138 * @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double
139 *
140 * Note: container cannot be empty
141 */
142 template <std::ranges::input_range C, invocable_r<double, std::ranges::range_reference_t<C>> Fn>
143 inline auto SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor) -> decltype(std::ranges::begin(container))
144 {
145 std::size_t size = std::ranges::distance(container);
146 std::size_t i = 0;
147 double* weights = new double[size];
148 double weightSum = 0.0;
149 for (auto const& val : container)
150 {
151 double weight = weightExtractor(val);
152 weights[i++] = weight;
153 weightSum += weight;
154 }
155
156 auto it = std::ranges::begin(container);
157 std::ranges::advance(it, weightSum > 0.0 ? urandweighted(size, weights) : urand(0, uint32(size) - 1));
158 delete[] weights;
159 return it;
160 }
161
170 template <std::random_access_iterator Iterator>
171 inline void RandomShuffle(Iterator begin, Iterator end)
172 {
173 std::ranges::shuffle(begin, end, RandomEngine::Instance());
174 }
175
183 template <std::ranges::random_access_range C>
184 inline void RandomShuffle(C& container)
185 {
186 RandomShuffle(std::ranges::begin(container), std::ranges::end(container));
187 }
188
201 template <std::input_iterator Iterator1, std::sentinel_for<Iterator1> Sentinel1,
202 std::input_iterator Iterator2, std::sentinel_for<Iterator2> Sentinel2>
203 inline constexpr bool Intersects(Iterator1 first1, Sentinel1 last1, Iterator2 first2, Sentinel2 last2)
204 {
205 while (first1 != last1 && first2 != last2)
206 {
207 if (*first1 < *first2)
208 ++first1;
209 else if (*first2 < *first1)
210 ++first2;
211 else
212 return true;
213 }
214
215 return false;
216 }
217
231 template <std::input_iterator Iterator1, std::sentinel_for<Iterator1> Sentinel1,
232 std::input_iterator Iterator2, std::sentinel_for<Iterator2> Sentinel2,
233 invocable_r<bool, std::iter_reference_t<Iterator1>, std::iter_reference_t<Iterator2>> Predicate>
234 inline constexpr bool Intersects(Iterator1 first1, Sentinel1 last1, Iterator2 first2, Sentinel2 last2, Predicate&& equalPred)
235 {
236 while (first1 != last1 && first2 != last2)
237 {
238 if (*first1 < *first2)
239 ++first1;
240 else if (*first2 < *first1)
241 ++first2;
242 else if (!std::invoke(std::forward<Predicate>(equalPred), *first1, *first2))
243 ++first1, ++first2;
244 else
245 return true;
246 }
247
248 return false;
249 }
250
251 namespace Impl
252 {
253 template <typename Container, typename Predicate>
254 inline constexpr void EraseIfMoveAssignable(Container& c, Predicate& p)
255 {
256 auto wpos = c.begin();
257 for (auto rpos = c.begin(), end = c.end(); rpos != end; ++rpos)
258 {
259 if (!p(*rpos))
260 {
261 if (rpos != wpos)
262 std::swap(*rpos, *wpos);
263 ++wpos;
264 }
265 }
266 c.erase(wpos, c.end());
267 }
268
269 template <typename Container, typename Predicate>
270 inline constexpr void EraseIfNotMoveAssignable(Container& c, Predicate& p)
271 {
272 for (auto it = c.begin(); it != c.end();)
273 {
274 if (p(*it))
275 it = c.erase(it);
276 else
277 ++it;
278 }
279 }
280 }
281
282 template <std::ranges::forward_range Container, invocable_r<bool, std::ranges::range_reference_t<Container>> Predicate>
283 inline constexpr void EraseIf(Container& c, Predicate p) requires requires { c.erase(c.begin(), c.end()); }
284 {
285 if constexpr (std::is_move_assignable_v<decltype(*c.begin())>)
287 else
289 }
290
298 template <typename T>
299 inline constexpr decltype(auto) EnsureWritableVectorIndex(std::vector<T>& vec, typename std::vector<T>::size_type i)
300 {
301 if (i >= vec.size())
302 vec.resize(i + 1);
303
304 return vec[i];
305 }
306
313 template <typename T>
314 inline constexpr decltype(auto) EnsureWritableVectorIndex(std::vector<T>& vec, typename std::vector<T>::size_type i, T const& resizeDefault)
315 {
316 if (i >= vec.size())
317 vec.resize(i + 1, resizeDefault);
318
319 return vec[i];
320 }
321 }
323}
325
326#endif
uint32_t uint32
Definition: Define.h:142
uint32 urandweighted(size_t count, double const *chances)
Definition: Random.cpp:87
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
static RandomEngine & Instance()
Definition: Random.cpp:93
CheckedBufferOutputIterator operator++(int)
Definition: Containers.h:48
CheckedBufferOutputIterator & operator++()
Definition: Containers.h:47
std::output_iterator_tag iterator_category
Definition: Containers.h:38
CheckedBufferOutputIterator(T *buf, size_t n)
Definition: Containers.h:44
constexpr void EraseIfMoveAssignable(Container &c, Predicate &p)
Definition: Containers.h:254
constexpr void EraseIfNotMoveAssignable(Container &c, Predicate &p)
Definition: Containers.h:270
auto SelectRandomWeightedContainerElement(C const &container, std::span< double > const &weights) -> decltype(std::ranges::begin(container))
Definition: Containers.h:127
constexpr void EraseIf(Container &c, Predicate p)
Definition: Containers.h:283
void RandomShuffle(Iterator begin, Iterator end)
Reorder the elements of the iterator range randomly.
Definition: Containers.h:171
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition: Containers.h:110
constexpr decltype(auto) EnsureWritableVectorIndex(std::vector< T > &vec, typename std::vector< T >::size_type i)
Definition: Containers.h:299
void RandomResize(C &container, std::size_t requestedSize)
Definition: Containers.h:67
constexpr bool Intersects(Iterator1 first1, Sentinel1 last1, Iterator2 first2, Sentinel2 last2)
Definition: Containers.h:203
constexpr std::size_t size()
Definition: UpdateField.h:769