Line data Source code
1 : /* ✔ */
2 :
3 : #pragma once
4 :
5 : #include <AH/Settings/Warnings.hpp>
6 :
7 : AH_DIAGNOSTIC_WERROR() // Enable errors on warnings
8 :
9 : #include <AH/Error/Error.hpp>
10 : #include <AH/STL/iterator>
11 : #include <AH/STL/type_traits> // conditional
12 : #include <stddef.h> // size_t
13 :
14 : BEGIN_AH_NAMESPACE
15 :
16 : template <class T>
17 : constexpr T abs_diff(const T &a, const T &b) {
18 : return a < b ? b - a : a - b;
19 : }
20 :
21 : /// @addtogroup AH_Containers
22 : /// @{
23 :
24 : template <class T, size_t N, bool Reverse, bool Const>
25 : class ArraySlice;
26 :
27 : /**
28 : * @brief An array wrapper for easy copying, comparing, and iterating.
29 : *
30 : * @tparam T
31 : * The type of the elements in the array.
32 : * @tparam N
33 : * The number of elements in the array.
34 : */
35 : template <class T, size_t N>
36 : struct Array {
37 : T data[N];
38 : using type = T;
39 : constexpr static size_t length = N;
40 :
41 : /**
42 : * @brief Get the element at the given index.
43 : *
44 : * @note Bounds checking is performed. If fatal errors are disabled, the
45 : * last element is returned if the index is out of bounds.
46 : *
47 : * @param index
48 : * The (zero-based) index of the element to return.
49 : */
50 1119 : T &operator[](size_t index) {
51 1119 : if (index >= N) { // TODO
52 1 : ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED);
53 : index = N - 1; // LCOV_EXCL_LINE
54 : } // LCOV_EXCL_LINE
55 1118 : return data[index];
56 : }
57 :
58 : /**
59 : * @brief Get the element at the given index.
60 : *
61 : * @note Bounds checking is performed. If fatal errors are disabled, the
62 : * last element is returned if the index is out of bounds.
63 : *
64 : * @param index
65 : * The (zero-based) index of the element to return.
66 : */
67 1607 : const T &operator[](size_t index) const {
68 1607 : if (index >= N) { // TODO
69 1 : ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDED);
70 : index = N - 1; // LCOV_EXCL_LINE
71 : } // LCOV_EXCL_LINE
72 1606 : return data[index];
73 : }
74 :
75 : /**
76 : * @brief Get a pointer to the first element.
77 : */
78 54 : T *begin() { return &data[0]; }
79 :
80 : /**
81 : * @brief Get a pointer to the first element.
82 : */
83 42 : const T *begin() const { return &data[0]; }
84 :
85 : /**
86 : * @brief Get a pointer to the memory beyond the array.
87 : */
88 48 : T *end() { return &data[N]; }
89 :
90 : /**
91 : * @brief Get a pointer to the memory beyond the array.
92 : */
93 42 : const T *end() const { return &data[N]; }
94 :
95 : /**
96 : * @brief Check the equality of all elements in two arrays.
97 : *
98 : * @param rhs
99 : * The array to compare this array to.
100 : */
101 44 : bool operator==(const Array<T, N> &rhs) const {
102 44 : if (this == &rhs)
103 3 : return true;
104 223 : for (size_t i = 0; i < N; i++)
105 185 : if ((*this)[i] != rhs[i])
106 3 : return false;
107 38 : return true;
108 : }
109 :
110 : /**
111 : * @brief Check the inequality of all elements in two arrays.
112 : *
113 : * @param rhs
114 : * The array to compare this array to.
115 : */
116 4 : bool operator!=(const Array<T, N> &rhs) const { return !(*this == rhs); }
117 :
118 : public:
119 : /**
120 : * @brief Get a view on a slice of the Array.
121 : *
122 : * Doesn't copy the contents of the array, it's just a reference to the
123 : * original array.
124 : *
125 : * @tparam Start
126 : * The start index of the slice.
127 : * @tparam End
128 : * The end index of the slice.
129 : */
130 : template <size_t Start = 0, size_t End = N - 1>
131 : ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false> slice();
132 :
133 : /**
134 : * @brief Get a read-only view on a slice of the Array.
135 : * @copydetails slice()
136 : */
137 : template <size_t Start = 0, size_t End = N - 1>
138 : ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true> slice() const;
139 :
140 : /**
141 : * @brief Get a read-only view on a slice of the Array.
142 : * @copydetails slice()
143 : */
144 : template <size_t Start = 0, size_t End = N - 1>
145 : ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true>
146 4 : cslice() const {
147 4 : const Array<T, N> *This = this;
148 4 : return This->template slice<Start, End>();
149 : }
150 : };
151 :
152 : /**
153 : * @brief Class for a view on a slice of an array.
154 : *
155 : * Doesn't copy the contents of the array, it's just a reference to the original
156 : * array.
157 : *
158 : * @tparam T
159 : * The type of elements of the Array.
160 : * @tparam N
161 : * The size of the slice.
162 : * @tparam Reverse
163 : * Whether the slice is reversed or not.
164 : * @tparam Const
165 : * Whether to save a read-only or mutable reference to the Array.
166 : */
167 : template <class T, size_t N, bool Reverse = false, bool Const = true>
168 : class ArraySlice {
169 : using ElementRefType =
170 : typename std::conditional<Const, const T &, T &>::type;
171 : using ElementPtrType =
172 : typename std::conditional<Const, const T *, T *>::type;
173 :
174 : public:
175 : /// Constructor
176 60 : ArraySlice(ElementPtrType array) : array {array} {}
177 :
178 : /// Implicit conversion from slice to new array (creates a copy).
179 1 : operator Array<T, N>() const { return asArray(); }
180 :
181 3 : Array<T, N> asArray() const {
182 3 : Array<T, N> slice = {{}};
183 14 : for (size_t i = 0; i < N; ++i)
184 11 : slice[i] = (*this)[i];
185 3 : return slice;
186 : }
187 :
188 : using iterator = typename std::conditional<
189 : Reverse, std::reverse_iterator<ElementPtrType>, ElementPtrType>::type;
190 :
191 : /**
192 : * @brief Get the element at the given index.
193 : *
194 : * @note Bounds checking is performed. If fatal errors are disabled, the
195 : * last element is returned if the index is out of bounds.
196 : *
197 : * @param index
198 : * The (zero-based) index of the element to return.
199 : */
200 459 : ElementRefType operator[](size_t index) const {
201 459 : if (index >= N) { // TODO
202 2 : ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDEF);
203 : index = N - 1; // LCOV_EXCL_LINE
204 : } // LCOV_EXCL_LINE
205 : if (Reverse)
206 17 : return *(array - index);
207 : else
208 440 : return *(array + index);
209 : }
210 :
211 3 : iterator begin() const {
212 : if (Reverse)
213 2 : return iterator {array + 1};
214 : else
215 1 : return iterator {array};
216 : }
217 :
218 7 : iterator end() const {
219 : if (Reverse)
220 4 : return iterator {array + 1 - N};
221 : else
222 3 : return iterator {array + N};
223 : }
224 :
225 : template <size_t Start, size_t End>
226 : ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
227 : slice() const;
228 :
229 : private:
230 : ElementPtrType array;
231 : };
232 :
233 : template <class T, size_t N>
234 : template <size_t Start, size_t End>
235 : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false>
236 35 : Array<T, N>::slice() {
237 : static_assert(Start < N, "");
238 : static_assert(End < N, "");
239 35 : return &(*this)[Start];
240 : }
241 :
242 : template <class T, size_t N>
243 : template <size_t Start, size_t End>
244 : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true>
245 23 : Array<T, N>::slice() const {
246 : static_assert(Start < N, "");
247 : static_assert(End < N, "");
248 23 : return &(*this)[Start];
249 : }
250 :
251 : template <class T, size_t N, bool Reverse, bool Const>
252 : template <size_t Start, size_t End>
253 : ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
254 2 : ArraySlice<T, N, Reverse, Const>::slice() const {
255 : static_assert(Start < N, "");
256 : static_assert(End < N, "");
257 2 : return &(*this)[Start];
258 : }
259 :
260 : /// @related ArraySlice<T, N, Reverse, Const>::iterator
261 : template <class T, size_t N, bool Reverse, bool Const>
262 : typename ArraySlice<T, N, Reverse, Const>::iterator operator+(
263 : typename ArraySlice<T, N, Reverse, Const>::iterator::difference_type n,
264 : typename ArraySlice<T, N, Reverse, Const>::iterator a) {
265 : return a + n;
266 : }
267 :
268 : // Equality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
269 :
270 : /// Slice == Slice
271 : /// @related ArraySlice
272 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
273 : bool Reverse2, bool Const1, bool Const2>
274 25 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
275 : ArraySlice<T2, N2, Reverse2, Const2> b) {
276 : static_assert(N1 == N2, "Error: sizes do not match");
277 136 : for (size_t i = 0; i < N1; ++i)
278 117 : if (a[i] != b[i])
279 6 : return false;
280 19 : return true;
281 : }
282 :
283 : /// Array == Slice
284 : /// @related ArraySlice
285 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
286 : bool operator==(const Array<T1, N1> &a,
287 : ArraySlice<T2, N2, Reverse2, Const2> b) {
288 : return a.slice() == b;
289 : }
290 :
291 : /// Slice == Array
292 : /// @related ArraySlice
293 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
294 3 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
295 : const Array<T2, N2> &b) {
296 3 : return a == b.slice();
297 : }
298 :
299 : // Inequality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
300 :
301 : /// Slice != Slice
302 : /// @related ArraySlice
303 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
304 : bool Reverse2, bool Const1, bool Const2>
305 8 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
306 : ArraySlice<T2, N2, Reverse2, Const2> b) {
307 8 : return !(a == b);
308 : }
309 :
310 : /// Array != Slice
311 : /// @related ArraySlice
312 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
313 : bool operator!=(const Array<T1, N1> &a,
314 : ArraySlice<T2, N2, Reverse2, Const2> b) {
315 : return a.slice() != b;
316 : }
317 :
318 : /// Slice != Array
319 : /// @related ArraySlice
320 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
321 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
322 : const Array<T2, N2> &b) {
323 : return a != b.slice();
324 : }
325 :
326 : // Addition ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
327 :
328 : /// Slice + Slice
329 : /// @related ArraySlice
330 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
331 : bool Reverse2, bool Const1, bool Const2>
332 : Array<decltype(T1 {} + T2 {}), N1>
333 2 : operator+(ArraySlice<T1, N1, Reverse1, Const1> a,
334 : ArraySlice<T2, N2, Reverse2, Const2> b) {
335 : static_assert(N1 == N2, "Error: sizes do not match");
336 2 : Array<decltype(T1 {} + T2 {}), N1> result = {{}};
337 8 : for (size_t i = 0; i < N1; ++i)
338 6 : result[i] = a[i] + b[i];
339 2 : return result;
340 : }
341 :
342 : /// Array + Array
343 : /// @related Array
344 : template <class T1, class T2, size_t N1, size_t N2>
345 1 : Array<decltype(T1 {} + T2 {}), N1> operator+(const Array<T1, N1> &a,
346 : const Array<T2, N2> &b) {
347 1 : return a.slice() + b.slice();
348 : }
349 :
350 : /// Slice += Slice
351 : /// @related ArraySlice
352 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
353 : bool Reverse2, bool Const1, bool Const2>
354 : const ArraySlice<T1, N1, Reverse1, Const1> &
355 2 : operator+=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
356 : const ArraySlice<T2, N2, Reverse2, Const2> &b) {
357 : static_assert(N1 == N2, "Error: sizes do not match");
358 8 : for (size_t i = 0; i < N1; ++i)
359 6 : a[i] += b[i];
360 2 : return a;
361 : }
362 :
363 : /// Array += Array
364 : /// @related Array
365 : template <class T1, class T2, size_t N1, size_t N2>
366 1 : Array<T1, N1> &operator+=(Array<T1, N1> &a, const Array<T2, N2> &b) {
367 1 : a.slice() += b.slice();
368 1 : return a;
369 : }
370 :
371 : // Subtraction :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
372 :
373 : /// Slice - Slice
374 : /// @related ArraySlice
375 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
376 : bool Reverse2, bool Const1, bool Const2>
377 : Array<decltype(T1 {} - T2 {}), N1>
378 2 : operator-(ArraySlice<T1, N1, Reverse1, Const1> a,
379 : ArraySlice<T2, N2, Reverse2, Const2> b) {
380 : static_assert(N1 == N2, "Error: sizes do not match");
381 2 : Array<decltype(T1 {} - T2 {}), N1> result = {{}};
382 8 : for (size_t i = 0; i < N1; ++i)
383 6 : result[i] = a[i] - b[i];
384 2 : return result;
385 : }
386 :
387 : /// Array - Array
388 : /// @related Array
389 : template <class T1, class T2, size_t N1, size_t N2>
390 1 : Array<decltype(T1 {} - T2 {}), N1> operator-(const Array<T1, N1> &a,
391 : const Array<T2, N2> &b) {
392 1 : return a.slice() - b.slice();
393 : }
394 :
395 : /// Slice -= Slice
396 : /// @related ArraySlice
397 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
398 : bool Reverse2, bool Const1, bool Const2>
399 : const ArraySlice<T1, N1, Reverse1, Const1> &
400 : operator-=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
401 : const ArraySlice<T2, N2, Reverse2, Const2> &b) {
402 : static_assert(N1 == N2, "Error: sizes do not match");
403 : for (size_t i = 0; i < N1; ++i)
404 : a[i] -= b[i];
405 : return a;
406 : }
407 :
408 : /// Array -= Array
409 : /// @related Array
410 : template <class T1, class T2, size_t N1, size_t N2>
411 : Array<T1, N1> &operator-=(Array<T1, N1> &a, const Array<T2, N2> &b) {
412 : a.slice() -= b.slice();
413 : return a;
414 : }
415 :
416 : // Scalar Multiplication :::::::::::::::::::::::::::::::::::::::::::::::::::::::
417 :
418 : /// Slice * Scalar
419 : /// @related ArraySlice
420 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
421 : Array<decltype(T1 {} * T2 {}), N1>
422 2 : operator*(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
423 2 : Array<decltype(T1 {} * T2 {}), N1> result = {{}};
424 10 : for (size_t i = 0; i < N1; ++i)
425 8 : result[i] = a[i] * b;
426 2 : return result;
427 : }
428 :
429 : /// Array * Scalar
430 : /// @related Array
431 : template <class T1, class T2, size_t N1>
432 1 : Array<decltype(T1 {} * T2 {}), N1> operator*(const Array<T1, N1> &a, T2 b) {
433 1 : return a.slice() * b;
434 : }
435 :
436 : /// Scalar * Slice
437 : /// @related ArraySlice
438 : template <class T1, class T2, size_t N2, bool Reverse2, bool Const2>
439 : Array<decltype(T1 {} * T2 {}), N2>
440 2 : operator*(T1 a, ArraySlice<T2, N2, Reverse2, Const2> b) {
441 2 : Array<decltype(T1 {} * T2 {}), N2> result = {{}};
442 10 : for (size_t i = 0; i < N2; ++i)
443 8 : result[i] = a * b[i];
444 2 : return result;
445 : }
446 :
447 : /// Scalar * Array
448 : /// @related Array
449 : template <class T1, class T2, size_t N2>
450 1 : Array<decltype(T1 {} * T2 {}), N2> operator*(T1 a, const Array<T2, N2> &b) {
451 1 : return a * b.slice();
452 : }
453 :
454 : /// Slice *= Scalar
455 : /// @related ArraySlice
456 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
457 : const ArraySlice<T1, N1, Reverse1, Const1> &
458 2 : operator*=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
459 10 : for (size_t i = 0; i < N1; ++i)
460 8 : a[i] *= b;
461 2 : return a;
462 : }
463 :
464 : /// Array *= Scalar
465 : /// @related Array
466 : template <class T1, class T2, size_t N1>
467 1 : Array<T1, N1> &operator*=(Array<T1, N1> &a, T2 b) {
468 1 : a.slice() *= b;
469 1 : return a;
470 : }
471 :
472 : // Scalar Division :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
473 :
474 : /// Slice / Scalar
475 : /// @related ArraySlice
476 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
477 : Array<decltype(T1 {} / T2 {}), N1>
478 2 : operator/(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
479 2 : Array<decltype(T1 {} / T2 {}), N1> result = {{}};
480 10 : for (size_t i = 0; i < N1; ++i)
481 8 : result[i] = a[i] / b;
482 2 : return result;
483 : }
484 :
485 : /// Array / Scalar
486 : /// @related Array
487 : template <class T1, class T2, size_t N1>
488 1 : Array<decltype(T1 {} / T2 {}), N1> operator/(const Array<T1, N1> &a, T2 b) {
489 1 : return a.slice() / b;
490 : }
491 :
492 : /// Slice /= Scalar
493 : /// @related ArraySlice
494 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
495 : const ArraySlice<T1, N1, Reverse1, Const1> &
496 2 : operator/=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
497 10 : for (size_t i = 0; i < N1; ++i)
498 8 : a[i] /= b;
499 2 : return a;
500 : }
501 :
502 : /// Array /= Scalar
503 : /// @related Array
504 : template <class T1, class T2, size_t N1>
505 1 : Array<T1, N1> &operator/=(Array<T1, N1> &a, T2 b) {
506 1 : a.slice() /= b;
507 1 : return a;
508 : }
509 :
510 : // Negation ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
511 :
512 : /// -Slice
513 : /// @related ArraySlice
514 : template <class T, size_t N, bool Reverse, bool Const>
515 2 : Array<decltype(-T {}), N> operator-(ArraySlice<T, N, Reverse, Const> a) {
516 2 : Array<decltype(-T {}), N> result = {{}};
517 11 : for (size_t i = 0; i < N; ++i)
518 9 : result[i] = -a[i];
519 2 : return result;
520 : }
521 :
522 : /// -Array
523 : /// @related Array
524 : template <class T, size_t N>
525 1 : Array<decltype(-T {}), N> operator-(const Array<T, N> &a) {
526 1 : return -a.slice();
527 : }
528 :
529 : // Type aliases ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
530 :
531 : /// An easy alias for two-dimensional Arrays.
532 : template <class T, size_t NumRows, size_t NumCols>
533 : using Array2D = Array<Array<T, NumCols>, NumRows>;
534 :
535 : /// @}
536 :
537 : END_AH_NAMESPACE
538 :
539 : AH_DIAGNOSTIC_POP()
|