AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
collections.h
1#ifndef UTILS_COLLECTIONS_H
2#define UTILS_COLLECTIONS_H
3
4#include <algorithm>
5#include <cassert>
6#include <concepts>
7#include <cstddef>
8#include <functional>
9#include <iterator>
10#include <map>
11#include <ranges>
12#include <set>
13#include <unordered_map>
14#include <unordered_set>
15#include <vector>
16
17namespace utils {
18
19template <class T>
20extern void sort_unique(std::vector<T>& vec)
21{
22 std::sort(vec.begin(), vec.end());
23 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
24}
25
26template <class T>
27extern bool is_sorted_unique(const std::vector<T>& values)
28{
29 for (size_t i = 1; i < values.size(); ++i) {
30 if (values[i - 1] >= values[i]) return false;
31 }
32 return true;
33}
34
35template <class T>
36extern bool all_values_unique(const std::vector<T>& v)
37{
38 std::unordered_set<T> s(v.begin(), v.end());
39 return s.size() == v.size();
40}
41
42template <class T>
43bool in_bounds(std::signed_integral auto index, const T& container)
44{
45 return index >= 0 && static_cast<size_t>(index) < container.size();
46}
47
48template <class T>
49bool in_bounds(std::unsigned_integral auto index, const T& container)
50{
51 return static_cast<size_t>(index) < container.size();
52}
53
54template <typename T>
55T swap_and_pop_from_vector(std::vector<T>& vec, size_t pos)
56{
57 assert(in_bounds(pos, vec));
58 T element = vec[pos];
59 std::swap(vec[pos], vec.back());
60 vec.pop_back();
61 return element;
62}
63
64template <class T>
65void release_vector_memory(std::vector<T>& vec)
66{
67 std::vector<T>().swap(vec);
68}
69
70template <class KeyType, class ValueType>
71ValueType get_value_or_default(
72 const std::unordered_map<KeyType, ValueType>& dict,
73 const KeyType& key,
74 const ValueType& default_value)
75{
76 auto it = dict.find(key);
77 if (it != dict.end()) {
78 return it->second;
79 }
80 return default_value;
81}
82
83template <typename ElemTo, typename Collection, typename MapFunc>
84std::vector<ElemTo> map_vector(const Collection& collection, MapFunc map_func)
85{
86 std::vector<ElemTo> transformed;
87 transformed.reserve(collection.size());
88 std::transform(
89 std::begin(collection),
90 std::end(collection),
91 std::back_inserter(transformed),
92 map_func);
93 return transformed;
94}
95
96template <typename T, typename Collection>
97std::vector<T> sorted(Collection&& collection)
98{
99 std::vector<T> vec(std::forward<Collection>(collection));
100 std::sort(vec.begin(), vec.end());
101 return vec;
102}
103
104template <typename T>
105int estimate_vector_bytes(int num_elements)
106{
107 /*
108 This estimate is based on a study of the C++ standard library
109 that shipped with gcc around the year 2017. It does not claim to
110 be accurate and may certainly be inaccurate for other compilers
111 or compiler versions.
112 */
113 int size = 0;
114 size += 2 * sizeof(void*); // overhead for dynamic memory management
115 size += sizeof(std::vector<T>); // size of empty vector
116 size += num_elements * sizeof(T); // size of actual entries
117 return size;
118}
119
120template <typename T>
121int _estimate_hash_table_bytes(int num_entries)
122{
123 /*
124 The same comments as for estimate_vector_bytes apply.
125 Additionally, there may be alignment issues, especially on
126 64-bit systems, that make this estimate too optimistic for
127 certain cases.
128 */
129
130 assert(num_entries < (1 << 28));
131 /*
132 Having num_entries < 2^28 is necessary but not sufficient for
133 the result value to not overflow. If we ever change this
134 function to support larger data structures (using a size_t
135 return value), we must update the list of bounds below (taken
136 from the gcc library source).
137 */
138 int num_buckets = 0;
139 const auto bounds = {
140 2, 5, 11, 23, 47, 97, 199,
141 409, 823, 1741, 3469, 6949, 14033, 28411,
142 57557, 116731, 236897, 480881, 976369, 1982627, 4026031,
143 8175383, 16601593, 33712729, 68460391, 139022417, 282312799};
144
145 for (int bound : bounds) {
146 if (num_entries < bound) {
147 num_buckets = bound;
148 break;
149 }
150 }
151
152 int size = 0;
153 size += 2 * sizeof(void*); // overhead for dynamic memory management
154 size += sizeof(T); // empty container
155 using Entry = typename T::value_type;
156 size += num_entries * sizeof(Entry); // actual entries
157 size += num_entries * sizeof(Entry*); // pointer to values
158 size += num_entries * sizeof(void*); // pointer to next node
159 size += num_buckets * sizeof(void*); // pointer to next bucket
160 return size;
161}
162
163template <typename T>
164int estimate_unordered_set_bytes(int num_entries)
165{
166 // See comments for _estimate_hash_table_bytes.
167 return _estimate_hash_table_bytes<std::unordered_set<T>>(num_entries);
168}
169
170template <typename Key, typename Value>
171int estimate_unordered_map_bytes(int num_entries)
172{
173 // See comments for _estimate_hash_table_bytes.
174 return _estimate_hash_table_bytes<std::unordered_map<Key, Value>>(
175 num_entries);
176}
177
178template <class I1, class I2>
179bool have_common_element(I1 first1, I1 last1, I2 first2, I2 last2)
180{
181 while (first1 != last1 && first2 != last2) {
182 if (*first1 < *first2) {
183 ++first1;
184 } else if (*first2 < *first1) {
185 ++first2;
186 } else {
187 return true;
188 }
189 }
190
191 return false;
192}
193
194template <class Range1, class Range2>
195bool have_common_element(const Range1& range1, const Range2& range2)
196{
197 return have_common_element(
198 std::begin(range1),
199 std::end(range1),
200 std::begin(range2),
201 std::end(range2));
202}
203
204// The following are used by probfd
205template <typename T, typename A>
206std::vector<T, A>
207merge_sorted(const std::vector<T, A>& left, const std::vector<T, A>& right)
208{
209 std::vector<T, A> merged;
210 merged.reserve(left.size() + right.size());
211 std::ranges::merge(left, right, std::back_inserter(merged));
212 return merged;
213}
214
215template <typename T, typename A>
216void insert_set(std::vector<T, A>& lhs, T element)
217{
218 assert(std::is_sorted(lhs.begin(), lhs.end()));
219
220 auto it = std::lower_bound(lhs.begin(), lhs.end(), element);
221 if (it == lhs.end() || *it != element) {
222 lhs.insert(it, std::move(element));
223 }
224}
225
226template <typename T, typename A>
227void insert_set(std::vector<T, A>& lhs, const std::vector<T, A>& rhs)
228{
229 for (const auto& element : rhs) {
230 insert_set(lhs, element);
231 }
232}
233
234template <std::ranges::input_range T>
235bool contains(T&& range, const std::ranges::range_value_t<T>& val)
236{
237 return std::ranges::find(range, val) != std::ranges::end(range);
238}
239
240template <class T>
241extern bool is_unique(const std::vector<T>& values)
242{
243 std::vector<T> temp(values.begin(), values.end());
244 std::ranges::sort(temp);
245 return is_sorted_unique(temp);
246}
247
248template <typename Iterator, typename Sentinel, typename T>
249auto find_sorted(Iterator begin, Sentinel end, const T& elem)
250{
251 auto it = std::lower_bound(begin, end, elem);
252 return it != end && *it == elem ? it : end;
253}
254
255template <class T>
256void release_container_memory(T& container)
257{
258 T().swap(container);
259}
260
261} // namespace utils
262
263#endif