1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CONTAINERS_H
#define TRINITY_CONTAINERS_H
#include "Define.h"
#include "Random.h"
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace Trinity
{
template<class T>
constexpr inline T* AddressOrSelf(T* ptr)
{
return ptr;
}
template<class T>
constexpr inline T* AddressOrSelf(T& not_ptr)
{
return std::addressof(not_ptr);
}
template <class T>
class CheckedBufferOutputIterator
{
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using pointer = T*;
using reference = T&;
using difference_type = std::ptrdiff_t;
CheckedBufferOutputIterator(T* buf, size_t n) : _buf(buf), _end(buf+n) {}
T& operator*() const { check(); return *_buf; }
CheckedBufferOutputIterator& operator++() { check(); ++_buf; return *this; }
CheckedBufferOutputIterator operator++(int) { CheckedBufferOutputIterator v = *this; operator++(); return v; }
size_t remaining() const { return (_end - _buf); }
private:
T* _buf;
T* _end;
void check() const
{
if (!(_buf < _end))
throw std::out_of_range("index");
}
};
namespace Containers
{
// resizes <container> to have at most <requestedSize> elements
// if it has more than <requestedSize> elements, the elements to keep are selected randomly
template<class C>
void RandomResize(C& container, std::size_t requestedSize)
{
static_assert(std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<typename C::iterator>::iterator_category>::value, "Invalid container passed to Trinity::Containers::RandomResize");
if (std::size(container) <= requestedSize)
return;
auto keepIt = std::begin(container), curIt = std::begin(container);
uint32 elementsToKeep = requestedSize, elementsToProcess = std::size(container);
while (elementsToProcess)
{
// this element has chance (elementsToKeep / elementsToProcess) of being kept
if (urand(1, elementsToProcess) <= elementsToKeep)
{
if (keepIt != curIt)
*keepIt = std::move(*curIt);
++keepIt;
--elementsToKeep;
}
++curIt;
--elementsToProcess;
}
container.erase(keepIt, std::end(container));
}
template<class C, class Predicate>
void RandomResize(C& container, Predicate&& predicate, std::size_t requestedSize)
{
//! First use predicate filter
C containerCopy;
std::copy_if(std::begin(container), std::end(container), std::inserter(containerCopy, std::end(containerCopy)), predicate);
if (requestedSize)
RandomResize(containerCopy, requestedSize);
container = std::move(containerCopy);
}
/*
* Select a random element from a container.
*
* Note: container cannot be empty
*/
template<class C>
inline auto SelectRandomContainerElement(C const& container) -> typename std::add_const<decltype(*std::begin(container))>::type&
{
auto it = std::begin(container);
std::advance(it, urand(0, uint32(std::size(container)) - 1));
return *it;
}
/*
* Select a random element from a container where each element has a different chance to be selected.
*
* @param container Container to select an element from
* @param weights Chances of each element to be selected, must be in the same order as elements in container.
* Caller is responsible for checking that sum of all weights is greater than 0.
*
* Note: container cannot be empty
*/
template<class C>
inline auto SelectRandomWeightedContainerElement(C const& container, std::vector<double> weights) -> decltype(std::begin(container))
{
auto it = std::begin(container);
std::advance(it, urandweighted(weights.size(), weights.data()));
return it;
}
/*
* Select a random element from a container where each element has a different chance to be selected.
*
* @param container Container to select an element from
* @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double
*
* Note: container cannot be empty
*/
template<class C, class Fn>
auto SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor) -> decltype(std::begin(container))
{
std::vector<double> weights;
weights.reserve(std::size(container));
double weightSum = 0.0;
for (auto& val : container)
{
double weight = weightExtractor(val);
weights.push_back(weight);
weightSum += weight;
}
if (weightSum <= 0.0)
weights.assign(std::size(container), 1.0);
return SelectRandomWeightedContainerElement(container, weights);
}
/**
* @fn void Trinity::Containers::RandomShuffle(C& container)
*
* @brief Reorder the elements of the container randomly.
*
* @param container Container to reorder
*/
template<class C>
inline void RandomShuffle(C& container)
{
std::shuffle(std::begin(container), std::end(container), RandomEngine::Instance());
}
/**
* @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2)
*
* @brief Checks if two SORTED containers have a common element
*
* @param first1 Iterator pointing to start of the first container
* @param last1 Iterator pointing to end of the first container
* @param first2 Iterator pointing to start of the second container
* @param last2 Iterator pointing to end of the second container
*
* @return true if containers have a common element, false otherwise.
*/
template<class Iterator1, class Iterator2>
bool Intersects(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
{
while (first1 != last1 && first2 != last2)
{
if (*first1 < *first2)
++first1;
else if (*first2 < *first1)
++first2;
else
return true;
}
return false;
}
/**
* Returns a pointer to mapped value (or the value itself if map stores pointers)
*/
template<class M>
inline auto MapGetValuePtr(M& map, typename M::key_type const& key) -> decltype(AddressOrSelf(map.find(key)->second))
{
auto itr = map.find(key);
return itr != map.end() ? AddressOrSelf(itr->second) : nullptr;
}
template<class K, class V, template<class, class, class...> class M, class... Rest>
void MultimapErasePair(M<K, V, Rest...>& multimap, K const& key, V const& value)
{
auto range = multimap.equal_range(key);
for (auto itr = range.first; itr != range.second;)
{
if (itr->second == value)
itr = multimap.erase(itr);
else
++itr;
}
}
template <typename Container, typename Predicate>
std::enable_if_t<std::is_move_assignable_v<decltype(*std::declval<Container>().begin())>, void> EraseIf(Container& c, Predicate p)
{
auto wpos = c.begin();
for (auto rpos = c.begin(), end = c.end(); rpos != end; ++rpos)
{
if (!p(*rpos))
{
if (rpos != wpos)
std::swap(*rpos, *wpos);
++wpos;
}
}
c.erase(wpos, c.end());
}
template <typename Container, typename Predicate>
std::enable_if_t<!std::is_move_assignable_v<decltype(*std::declval<Container>().begin())>, void> EraseIf(Container& c, Predicate p)
{
for (auto it = c.begin(); it != c.end();)
{
if (p(*it))
it = c.erase(it);
else
++it;
}
}
}
//! namespace Containers
}
//! namespace Trinity
#endif //! #ifdef TRINITY_CONTAINERS_H
|