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