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