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 496 : 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 450 : T &operator[](size_t index) {
51 450 : 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 449 : return data[index];
56 1 : }
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 976 : const T &operator[](size_t index) const {
68 976 : 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 975 : return data[index];
73 1 : }
74 :
75 : /**
76 : * @brief Get a pointer to the first element.
77 : */
78 35 : T *begin() { return &data[0]; }
79 :
80 : /**
81 : * @brief Get a pointer to the first element.
82 : */
83 28 : const T *begin() const { return &data[0]; }
84 :
85 : /**
86 : * @brief Get a pointer to the memory beyond the array.
87 : */
88 33 : T *end() { return &data[N]; }
89 :
90 : /**
91 : * @brief Get a pointer to the memory beyond the array.
92 : */
93 28 : 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 43 : bool operator==(const Array<T, N> &rhs) const {
102 43 : if (this == &rhs)
103 3 : return true;
104 219 : for (size_t i = 0; i < N; i++)
105 182 : if ((*this)[i] != rhs[i])
106 3 : return false;
107 37 : return true;
108 43 : }
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 : cslice() const {
147 : const Array<T, N> *This = this;
148 : return This->slice();
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 56 : 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 2 : Array<T, N> asArray() const {
182 2 : Array<T, N> slice = {{}};
183 10 : for (size_t i = 0; i < N; ++i)
184 8 : slice[i] = (*this)[i];
185 2 : return slice;
186 : }
187 :
188 : class Iterator {
189 : public:
190 18 : Iterator(ElementPtrType ptr) : ptr(ptr) {}
191 :
192 : using difference_type = std::ptrdiff_t;
193 : using value_type = T;
194 : using pointer = ElementPtrType;
195 : using reference = ElementRefType;
196 : using iterator_category = std::random_access_iterator_tag;
197 :
198 12 : bool operator!=(Iterator rhs) const { return ptr != rhs.ptr; }
199 3 : bool operator==(Iterator rhs) const { return ptr == rhs.ptr; }
200 :
201 62 : reference operator*() const { return *ptr; }
202 :
203 10 : Iterator &operator++() {
204 10 : Reverse ? --ptr : ++ptr;
205 10 : return *this;
206 : }
207 :
208 25 : Iterator &operator--() {
209 25 : Reverse ? ++ptr : --ptr;
210 25 : return *this;
211 : }
212 :
213 8 : difference_type operator-(Iterator rhs) const {
214 8 : return Reverse ? rhs.ptr - ptr : ptr - rhs.ptr;
215 : }
216 :
217 4 : Iterator operator+(difference_type rhs) const {
218 4 : return Reverse ? ptr - rhs : ptr + rhs;
219 : }
220 :
221 4 : Iterator operator-(difference_type rhs) const {
222 4 : return Reverse ? ptr + rhs : ptr - rhs;
223 : }
224 :
225 3 : bool operator<(Iterator rhs) const {
226 3 : return Reverse ? rhs.ptr < ptr : ptr < rhs.ptr;
227 : }
228 :
229 : private:
230 : ElementPtrType ptr;
231 : };
232 :
233 : /**
234 : * @brief Get the element at the given index.
235 : *
236 : * @note Bounds checking is performed. If fatal errors are disabled, the
237 : * last element is returned if the index is out of bounds.
238 : *
239 : * @param index
240 : * The (zero-based) index of the element to return.
241 : */
242 361 : ElementRefType operator[](size_t index) const {
243 361 : if (index >= N) { // TODO
244 2 : ERROR(F("Index out of bounds: ") << index << F(" ≥ ") << N, 0xEDEF);
245 : index = N - 1; // LCOV_EXCL_LINE
246 : } // LCOV_EXCL_LINE
247 : if (Reverse)
248 12 : return array[-index];
249 : else
250 347 : return array[index];
251 2 : }
252 :
253 3 : Iterator begin() const {
254 : if (Reverse)
255 2 : return array;
256 : else
257 1 : return array;
258 : }
259 :
260 7 : Iterator end() const {
261 : if (Reverse)
262 4 : return array - N;
263 : else
264 3 : return array + N;
265 : }
266 :
267 : template <size_t Start, size_t End>
268 : ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
269 : slice() const;
270 :
271 : private:
272 : ElementPtrType array;
273 : };
274 :
275 : template <class T, size_t N>
276 : template <size_t Start, size_t End>
277 : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), false>
278 35 : Array<T, N>::slice() {
279 : static_assert(Start < N, "");
280 : static_assert(End < N, "");
281 35 : return &(*this)[Start];
282 : }
283 :
284 : template <class T, size_t N>
285 : template <size_t Start, size_t End>
286 : inline ArraySlice<T, abs_diff(Start, End) + 1, (End < Start), true>
287 19 : Array<T, N>::slice() const {
288 : static_assert(Start < N, "");
289 : static_assert(End < N, "");
290 19 : return &(*this)[Start];
291 : }
292 :
293 : template <class T, size_t N, bool Reverse, bool Const>
294 : template <size_t Start, size_t End>
295 : ArraySlice<T, abs_diff(End, Start) + 1, Reverse ^ (End < Start), Const>
296 2 : ArraySlice<T, N, Reverse, Const>::slice() const {
297 : static_assert(Start < N, "");
298 : static_assert(End < N, "");
299 2 : return &(*this)[Start];
300 : }
301 :
302 : // Equality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
303 :
304 : /// Slice == Slice
305 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
306 : bool Reverse2, bool Const1, bool Const2>
307 16 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
308 : ArraySlice<T2, N2, Reverse2, Const2> b) {
309 : static_assert(N1 == N2, "Error: sizes do not match");
310 85 : for (size_t i = 0; i < N1; ++i)
311 72 : if (a[i] != b[i])
312 3 : return false;
313 13 : return true;
314 16 : }
315 :
316 : /// Array == Slice
317 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
318 : bool operator==(const Array<T1, N1> &a,
319 : ArraySlice<T2, N2, Reverse2, Const2> b) {
320 : return a.slice() == b;
321 : }
322 :
323 : /// Slice == Array
324 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
325 3 : bool operator==(ArraySlice<T1, N1, Reverse1, Const1> a,
326 : const Array<T2, N2> &b) {
327 3 : return a == b.slice();
328 : }
329 :
330 : // Inequality ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
331 :
332 : /// Slice != Slice
333 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
334 : bool Reverse2, bool Const1, bool Const2>
335 4 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
336 : ArraySlice<T2, N2, Reverse2, Const2> b) {
337 4 : return !(a == b);
338 : }
339 :
340 : /// Array != Slice
341 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse2, bool Const2>
342 : bool operator!=(const Array<T1, N1> &a,
343 : ArraySlice<T2, N2, Reverse2, Const2> b) {
344 : return a.slice() != b;
345 : }
346 :
347 : /// Slice != Array
348 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1, bool Const1>
349 : bool operator!=(ArraySlice<T1, N1, Reverse1, Const1> a,
350 : const Array<T2, N2> &b) {
351 : return a != b.slice();
352 : }
353 :
354 : // Addition ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
355 :
356 : /// Slice + Slice
357 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
358 : bool Reverse2, bool Const1, bool Const2>
359 : Array<decltype(T1{} + T2{}), N1>
360 2 : operator+(ArraySlice<T1, N1, Reverse1, Const1> a,
361 : ArraySlice<T2, N2, Reverse2, Const2> b) {
362 : static_assert(N1 == N2, "Error: sizes do not match");
363 2 : Array<decltype(T1{} + T2{}), N1> result = {{}};
364 8 : for (size_t i = 0; i < N1; ++i)
365 6 : result[i] = a[i] + b[i];
366 2 : return result;
367 : }
368 :
369 : /// Array + Array
370 : template <class T1, class T2, size_t N1, size_t N2>
371 1 : Array<decltype(T1{} + T2{}), N1> operator+(const Array<T1, N1> &a,
372 : const Array<T2, N2> &b) {
373 1 : return a.slice() + b.slice();
374 : }
375 :
376 : /// Slice += Slice
377 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
378 : bool Reverse2, bool Const1, bool Const2>
379 : const ArraySlice<T1, N1, Reverse1, Const1> &
380 2 : operator+=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
381 : const ArraySlice<T2, N2, Reverse2, Const2> &b) {
382 : static_assert(N1 == N2, "Error: sizes do not match");
383 8 : for (size_t i = 0; i < N1; ++i)
384 6 : a[i] += b[i];
385 2 : return a;
386 : }
387 :
388 : /// Array += Array
389 : template <class T1, class T2, size_t N1, size_t N2>
390 1 : Array<T1, N1> &operator+=(Array<T1, N1> &a, const Array<T2, N2> &b) {
391 1 : a.slice() += b.slice();
392 1 : return a;
393 : }
394 :
395 : // Subtraction :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
396 :
397 : /// Slice - Slice
398 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
399 : bool Reverse2, bool Const1, bool Const2>
400 : Array<decltype(T1{} - T2{}), N1>
401 2 : operator-(ArraySlice<T1, N1, Reverse1, Const1> a,
402 : ArraySlice<T2, N2, Reverse2, Const2> b) {
403 : static_assert(N1 == N2, "Error: sizes do not match");
404 2 : Array<decltype(T1{} - T2{}), N1> result = {{}};
405 8 : for (size_t i = 0; i < N1; ++i)
406 6 : result[i] = a[i] - b[i];
407 2 : return result;
408 : }
409 :
410 : /// Array - Array
411 : template <class T1, class T2, size_t N1, size_t N2>
412 1 : Array<decltype(T1{} - T2{}), N1> operator-(const Array<T1, N1> &a,
413 : const Array<T2, N2> &b) {
414 1 : return a.slice() - b.slice();
415 : }
416 :
417 : /// Slice -= Slice
418 : template <class T1, class T2, size_t N1, size_t N2, bool Reverse1,
419 : bool Reverse2, bool Const1, bool Const2>
420 : const ArraySlice<T1, N1, Reverse1, Const1> &
421 : operator-=(const ArraySlice<T1, N1, Reverse1, Const1> &a,
422 : const ArraySlice<T2, N2, Reverse2, Const2> &b) {
423 : static_assert(N1 == N2, "Error: sizes do not match");
424 : for (size_t i = 0; i < N1; ++i)
425 : a[i] -= b[i];
426 : return a;
427 : }
428 :
429 : /// Array -= Array
430 : template <class T1, class T2, size_t N1, size_t N2>
431 : Array<T1, N1> &operator-=(Array<T1, N1> &a, const Array<T2, N2> &b) {
432 : a.slice() -= b.slice();
433 : return a;
434 : }
435 :
436 : // Scalar Multiplication :::::::::::::::::::::::::::::::::::::::::::::::::::::::
437 :
438 : /// Slice * Scalar
439 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
440 : Array<decltype(T1{} * T2{}), N1>
441 2 : operator*(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
442 2 : Array<decltype(T1{} * T2{}), N1> result = {{}};
443 10 : for (size_t i = 0; i < N1; ++i)
444 8 : result[i] = a[i] * b;
445 2 : return result;
446 : }
447 :
448 : /// Array * Scalar
449 : template <class T1, class T2, size_t N1>
450 1 : Array<decltype(T1{} * T2{}), N1> operator*(const Array<T1, N1> &a, T2 b) {
451 1 : return a.slice() * b;
452 : }
453 :
454 : /// Scalar * Slice
455 : template <class T1, class T2, size_t N2, bool Reverse2, bool Const2>
456 : Array<decltype(T1{} * T2{}), N2>
457 2 : operator*(T1 a, ArraySlice<T2, N2, Reverse2, Const2> b) {
458 2 : Array<decltype(T1{} * T2{}), N2> result = {{}};
459 10 : for (size_t i = 0; i < N2; ++i)
460 8 : result[i] = a * b[i];
461 2 : return result;
462 : }
463 :
464 : /// Scalar * Array
465 : template <class T1, class T2, size_t N2>
466 1 : Array<decltype(T1{} * T2{}), N2> operator*(T1 a, const Array<T2, N2> &b) {
467 1 : return a * b.slice();
468 : }
469 :
470 : /// Slice *= Scalar
471 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
472 : const ArraySlice<T1, N1, Reverse1, Const1> &
473 2 : operator*=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
474 10 : for (size_t i = 0; i < N1; ++i)
475 8 : a[i] *= b;
476 2 : return a;
477 : }
478 :
479 : /// Array *= Scalar
480 : template <class T1, class T2, size_t N1>
481 1 : Array<T1, N1> &operator*=(Array<T1, N1> &a, T2 b) {
482 1 : a.slice() *= b;
483 1 : return a;
484 : }
485 :
486 : // Scalar Division :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
487 :
488 : /// Slice / Scalar
489 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
490 : Array<decltype(T1{} / T2{}), N1>
491 2 : operator/(ArraySlice<T1, N1, Reverse1, Const1> a, T2 b) {
492 2 : Array<decltype(T1{} / T2{}), N1> result = {{}};
493 10 : for (size_t i = 0; i < N1; ++i)
494 8 : result[i] = a[i] / b;
495 2 : return result;
496 : }
497 :
498 : /// Array / Scalar
499 : template <class T1, class T2, size_t N1>
500 1 : Array<decltype(T1{} / T2{}), N1> operator/(const Array<T1, N1> &a, T2 b) {
501 1 : return a.slice() / b;
502 : }
503 :
504 : /// Slice /= Scalar
505 : template <class T1, class T2, size_t N1, bool Reverse1, bool Const1>
506 : const ArraySlice<T1, N1, Reverse1, Const1> &
507 2 : operator/=(const ArraySlice<T1, N1, Reverse1, Const1> &a, T2 b) {
508 10 : for (size_t i = 0; i < N1; ++i)
509 8 : a[i] /= b;
510 2 : return a;
511 : }
512 :
513 : /// Array /= Scalar
514 : template <class T1, class T2, size_t N1>
515 1 : Array<T1, N1> &operator/=(Array<T1, N1> &a, T2 b) {
516 1 : a.slice() /= b;
517 1 : return a;
518 : }
519 :
520 : // Negation ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
521 :
522 : /// -Slice
523 : template <class T, size_t N, bool Reverse, bool Const>
524 2 : Array<decltype(-T{}), N> operator-(ArraySlice<T, N, Reverse, Const> a) {
525 2 : Array<decltype(-T{}), N> result = {{}};
526 11 : for (size_t i = 0; i < N; ++i)
527 9 : result[i] = -a[i];
528 2 : return result;
529 : }
530 :
531 : /// -Array
532 : template <class T, size_t N>
533 1 : Array<decltype(-T{}), N> operator-(const Array<T, N> &a) {
534 1 : return -a.slice();
535 : }
536 :
537 : // Type aliases ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
538 :
539 : /// An easy alias for two-dimensional Arrays.
540 : template <class T, size_t nb_rows, size_t nb_cols>
541 : using Array2D = Array<Array<T, nb_cols>, nb_rows>;
542 :
543 : /// @}
544 :
545 : END_AH_NAMESPACE
546 :
547 : AH_DIAGNOSTIC_POP()
|