1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level COPYRIGHT file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 #include "axom/core/Array.hpp"
7 #include "axom/core/ArrayView.hpp"
8 #include "axom/core/memory_management.hpp"
9 
10 #include "gtest/gtest.h"
11 
12 #include <algorithm>
13 
14 namespace axom
15 {
16 namespace internal
17 {
18 /*!
19  * \brief Calculate the new capacity for an Array given an increase in the
20  *  size.
21  * \param [in] v, the Array in question.
22  * \param [in] increase, the amount the size will increase by
23  * \return the new capacity.
24  */
25 template <typename T>
calc_new_capacity(Array<T> & v,IndexType increase)26 IndexType calc_new_capacity(Array<T>& v, IndexType increase)
27 {
28   IndexType new_num_elements = v.size() + increase;
29   if(new_num_elements > v.capacity())
30   {
31     return new_num_elements * v.getResizeRatio() + 0.5;
32   }
33 
34   return v.capacity();
35 }
36 
37 /*!
38  * \brief Check if two Arrays are copies. Does not check the resize ratio.
39  * \param [in] lhs, the first Array to compare.
40  * \param [in] rhs, the second Array to compare.
41  * \return the new capacity.
42  */
43 template <typename T>
check_copy(const Array<T> & lhs,const Array<T> & rhs)44 void check_copy(const Array<T>& lhs, const Array<T>& rhs)
45 {
46   EXPECT_EQ(lhs.size(), rhs.size());
47   EXPECT_EQ(lhs.capacity(), rhs.capacity());
48 
49   const T* lhs_data = lhs.data();
50   const T* rhs_data = rhs.data();
51   EXPECT_EQ(lhs_data, rhs_data);
52 }
53 
54 /*!
55  * \brief Check that the storage of an Array is working properly.
56  * \param [in] v the Array to check.
57  */
58 template <typename T>
check_storage(Array<T> & v)59 void check_storage(Array<T>& v)
60 {
61   EXPECT_TRUE(v.empty());
62   EXPECT_EQ(v.size(), 0);
63 
64   IndexType capacity = v.capacity();
65   const T* data_ptr = v.data();
66 
67   /* Push back up to half the capacity. */
68   for(T i = 0; i < capacity / 2; ++i)
69   {
70     v.push_back(i);
71   }
72 
73   /* Check the array metadata. */
74   EXPECT_TRUE(!v.empty());
75   EXPECT_EQ(v.size(), capacity / 2);
76   EXPECT_EQ(v.capacity(), capacity);
77   EXPECT_EQ(v.data(), data_ptr);
78 
79   /* Push back up to the full capacity. */
80   for(T i = capacity / 2; i < capacity; ++i)
81   {
82     v.push_back(i);
83   }
84 
85   /* Check the array metadata. */
86   EXPECT_TRUE(!v.empty());
87   EXPECT_EQ(v.size(), capacity);
88   EXPECT_EQ(v.capacity(), capacity);
89   EXPECT_EQ(v.data(), data_ptr);
90 
91   /* Check the array data using the [] operator and the raw pointer. */
92   for(IndexType i = 0; i < capacity; ++i)
93   {
94     EXPECT_EQ(v[i], i);
95     EXPECT_EQ(data_ptr[i], i);
96   }
97 
98   /* Set the array data to new values using the [] operator. */
99   for(IndexType i = 0; i < capacity; ++i)
100   {
101     v[i] = i - 5 * i + 7;
102   }
103 
104   /* Check the array data using the [] operator and the raw pointer. */
105   for(IndexType i = 0; i < capacity; ++i)
106   {
107     EXPECT_EQ(v[i], i - 5 * i + 7);
108     EXPECT_EQ(data_ptr[i], i - 5 * i + 7);
109   }
110 
111   /* Check the array metadata. */
112   EXPECT_TRUE(!v.empty());
113   EXPECT_EQ(v.size(), capacity);
114   EXPECT_EQ(v.capacity(), capacity);
115   EXPECT_EQ(v.data(), data_ptr);
116 }
117 
118 /*!
119  * \brief Check that the fill method is working properly.
120  * \param [in] v the Array to check.
121  */
122 template <typename T>
check_fill(Array<T> & v)123 void check_fill(Array<T>& v)
124 {
125   constexpr T MAGIC_NUM_0 = 55;
126   constexpr T MAGIC_NUM_1 = 6834;
127   const IndexType capacity = v.capacity();
128   const IndexType size = v.size();
129   const double ratio = v.getResizeRatio();
130   const T* const data_ptr = v.data();
131 
132   /* Fill the Array with MAGIC_NUM_0. */
133   v.fill(MAGIC_NUM_0);
134 
135   /* Check the meta data. */
136   EXPECT_EQ(capacity, v.capacity());
137   EXPECT_EQ(size, v.size());
138   EXPECT_EQ(ratio, v.getResizeRatio());
139   EXPECT_EQ(data_ptr, v.data());
140 
141   /* Check that the entries are all MAGIC_NUM_0. */
142   for(IndexType i = 0; i < size; ++i)
143   {
144     EXPECT_EQ(v[i], MAGIC_NUM_0);
145   }
146 
147   /* Fill the Array with MAGIC_NUM_1. */
148   v.fill(MAGIC_NUM_1);
149 
150   /* Check the meta data. */
151   EXPECT_EQ(capacity, v.capacity());
152   EXPECT_EQ(size, v.size());
153   EXPECT_EQ(ratio, v.getResizeRatio());
154   EXPECT_EQ(data_ptr, v.data());
155 
156   /* Check that the entries are all MAGIC_NUM_1. */
157   for(IndexType i = 0; i < size; ++i)
158   {
159     EXPECT_EQ(v[i], MAGIC_NUM_1);
160   }
161 }
162 
163 /*!
164  * \brief Check that the set method is working properly.
165  * \param [in] v the Array to check.
166  */
167 template <typename T>
check_set(Array<T> & v)168 void check_set(Array<T>& v)
169 {
170   constexpr T ZERO = 0;
171   const IndexType capacity = v.capacity();
172   const IndexType size = v.size();
173   const double ratio = v.getResizeRatio();
174   const T* const data_ptr = v.data();
175 
176   /* Allocate a buffer half the size of the array. Fill it up with sequential
177    * values. */
178   const IndexType buffer_size = size / 2;
179   T* buffer = allocate<T>(buffer_size);
180   for(IndexType i = 0; i < buffer_size; ++i)
181   {
182     buffer[i] = i;
183   }
184 
185   /* Set all the values in the array to zero. */
186   v.fill(ZERO);
187 
188   /* Set the first half of the elements in the array to the sequential values in
189    * buffer. */
190   v.set(buffer, buffer_size, 0);
191 
192   /* Check the array metadata. */
193   EXPECT_EQ(capacity, v.capacity());
194   EXPECT_EQ(size, v.size());
195   EXPECT_EQ(ratio, v.getResizeRatio());
196   EXPECT_EQ(data_ptr, v.data());
197 
198   /* Check that the first half of the elements in the array are equivalent to
199    * those in buffer. */
200   for(IndexType i = 0; i < buffer_size; ++i)
201   {
202     EXPECT_EQ(v[i], buffer[i]);
203   }
204 
205   /* Check that the second half of the elements in the array are all zero. */
206   for(IndexType i = buffer_size; i < size; ++i)
207   {
208     EXPECT_EQ(v[i], ZERO);
209   }
210 
211   /* Reset the values in buffer to the next sequential values. */
212   for(IndexType i = 0; i < buffer_size; ++i)
213   {
214     buffer[i] = i + buffer_size;
215   }
216 
217   /* Set the second half of the elements in the array to the new sequential
218    * values in buffer. */
219   v.set(buffer, buffer_size, buffer_size);
220 
221   /* Check the array metadata. */
222   EXPECT_EQ(capacity, v.capacity());
223   EXPECT_EQ(size, v.size());
224   EXPECT_EQ(ratio, v.getResizeRatio());
225   EXPECT_EQ(data_ptr, v.data());
226 
227   /* Check that all the elements in the array now hold sequential values. */
228   for(IndexType i = 0; i < 2 * buffer_size; ++i)
229   {
230     EXPECT_EQ(v[i], i);
231   }
232 
233   deallocate(buffer);
234 }
235 
236 /*!
237  * \brief Check that the resizing of an Array is working properly.
238  * \param [in] v the Array to check.
239  */
240 template <typename T>
check_resize(Array<T> & v)241 void check_resize(Array<T>& v)
242 {
243   /* Resize the array up to the capacity */
244   IndexType capacity = v.capacity();
245   v.resize(capacity);
246   IndexType size = capacity;
247 
248   /* Check that the size equals the capacity. */
249   EXPECT_EQ(v.size(), v.capacity());
250 
251   /* Set the existing data in v */
252   for(IndexType i = 0; i < size; ++i)
253   {
254     v[i] = static_cast<T>(i - 5 * i + 7);
255   }
256 
257   /* Push back a new element, should resize. */
258   IndexType old_capacity = capacity;
259   capacity = calc_new_capacity(v, 1);
260   v.push_back(size - 5 * size + 7);
261   size++;
262 
263   /* Check that it resized properly */
264   EXPECT_GT(capacity, old_capacity);
265   EXPECT_EQ(v.capacity(), capacity);
266   EXPECT_EQ(v.size(), size);
267 
268   /* Check that the data is still intact. */
269   for(IndexType i = 0; i < size; ++i)
270   {
271     EXPECT_EQ(v[i], i - 5 * i + 7);
272   }
273 
274   /* Push back 1000 elements */
275   const IndexType n_elements = 1000;
276 
277   T* values = allocate<T>(n_elements);
278   for(IndexType i = 0; i < n_elements; ++i)
279   {
280     IndexType i_real = i + size;
281     values[i] = i_real - 5 * i_real + 7;
282   }
283 
284   /* Push back the new elements. */
285   capacity = calc_new_capacity(v, n_elements);
286   v.insert(size, n_elements, values);
287   size += n_elements;
288 
289   /* Check that size and capacity are as expected. */
290   EXPECT_EQ(v.capacity(), capacity);
291   EXPECT_EQ(v.size(), size);
292 
293   /* Check that the data is still intact. */
294   for(IndexType i = 0; i < size; ++i)
295   {
296     EXPECT_EQ(v[i], i - 5 * i + 7);
297   }
298 
299   /* Reduce the size down to 500 elements */
300   T* data_address = v.data();
301   size = 500;
302   v.resize(size);
303 
304   /* Check the metadata. */
305   EXPECT_EQ(v.size(), size);
306   EXPECT_EQ(v.capacity(), capacity);
307   EXPECT_EQ(v.data(), data_address);
308 
309   /* Check the data. */
310   for(IndexType i = 0; i < size; ++i)
311   {
312     EXPECT_EQ(v[i], i - 5 * i + 7);
313   }
314 
315   /* Shrink the vector */
316   capacity = size;
317   v.shrink();
318 
319   /* Check that the capacity and size are as expected. */
320   EXPECT_EQ(v.capacity(), capacity);
321   EXPECT_EQ(v.size(), size);
322 
323   /* Check that the data is intact. */
324   for(IndexType i = 0; i < size; ++i)
325   {
326     EXPECT_EQ(v[i], i - 5 * i + 7);
327   }
328 
329   /* Push back a new element, should resize. */
330   old_capacity = capacity;
331   capacity = calc_new_capacity(v, 1);
332   v.push_back(size - 5 * size + 7);
333   size++;
334 
335   /* Check the new size and capacity. */
336   EXPECT_GT(capacity, old_capacity);
337   EXPECT_EQ(v.capacity(), capacity);
338   EXPECT_EQ(v.size(), size);
339 
340   /* Check that the data is intact. */
341   for(IndexType i = 0; i < size; ++i)
342   {
343     EXPECT_EQ(v[i], i - 5 * i + 7);
344   }
345 
346   /* Reset the data */
347   T* data_ptr = v.data();
348   for(IndexType i = 0; i < size; ++i)
349   {
350     data_ptr[i] = i;
351   }
352 
353   /* Push back a bunch of elements to fill in up to the capacity. Resize should
354    * not occur. */
355   old_capacity = capacity;
356   for(IndexType i = size; i < old_capacity; ++i)
357   {
358     v.push_back(i);
359     size++;
360     EXPECT_EQ(v.capacity(), old_capacity);
361     EXPECT_EQ(v.size(), size);
362     EXPECT_EQ(v.data(), data_ptr);
363   }
364 
365   EXPECT_EQ(v.size(), old_capacity);
366 
367   /* Push back a final element that should trigger a resize. */
368   capacity = calc_new_capacity(v, old_capacity - size + 1);
369   v.push_back(size);
370   size++;
371 
372   /* Check the new capacity and size. */
373   EXPECT_GT(capacity, old_capacity);
374   EXPECT_EQ(v.capacity(), capacity);
375   EXPECT_EQ(v.size(), size);
376 
377   /* Check the data. */
378   for(IndexType i = 0; i < size; ++i)
379   {
380     EXPECT_EQ(v[i], i);
381   }
382 
383   deallocate(values);
384   values = nullptr;
385 }
386 
387 /*!
388  * \brief Check that the insertion into an Array is working properly.
389  * \param [in] v the Array to check.
390  */
391 template <typename T>
check_insert(Array<T> & v)392 void check_insert(Array<T>& v)
393 {
394   /* Resize the array up to the capacity */
395   IndexType capacity = v.capacity();
396   v.resize(capacity);
397   IndexType size = capacity;
398 
399   EXPECT_EQ(v.size(), v.capacity());
400 
401   /* Set the existing data in v */
402   for(IndexType i = 0; i < size; ++i)
403   {
404     v[i] = i - 5 * i + 7;
405   }
406 
407   /* Insert a new element, should resize. */
408   IndexType old_capacity = capacity;
409   capacity = calc_new_capacity(v, 1);
410   v.insert(v.size(), 1, size - 5 * size + 7);
411   size++;
412 
413   /* Check that it resized properly */
414   EXPECT_GT(capacity, old_capacity);
415   EXPECT_EQ(v.capacity(), capacity);
416   EXPECT_EQ(v.size(), size);
417   for(IndexType i = 0; i < size; ++i)
418   {
419     EXPECT_EQ(v[i], i - 5 * i + 7);
420   }
421 
422   /* Insert 1000 elements */
423   const IndexType n_elements = 1000;
424   T* values = allocate<T>(n_elements);
425   for(IndexType i = 0; i < n_elements; ++i)
426   {
427     IndexType i_real = i + size;
428     values[i] = i_real - 5 * i_real + 7;
429   }
430 
431   capacity = calc_new_capacity(v, n_elements);
432   v.insert(size, n_elements, values);
433   size += n_elements;
434 
435   /* Check that it resizes properly */
436   EXPECT_EQ(v.capacity(), capacity);
437   EXPECT_EQ(v.size(), size);
438   for(IndexType i = 0; i < size; ++i)
439   {
440     EXPECT_EQ(v[i], i - 5 * i + 7);
441   }
442 
443   capacity = size;
444   v.shrink();
445   IndexType n_insert_front = 100;
446 
447   /* Reset the data */
448   T* data_ptr = v.data();
449   for(IndexType i = 0; i < size; ++i)
450   {
451     data_ptr[i] = i + n_insert_front;
452   }
453 
454   /* Insert into the front of the Array. */
455   for(IndexType i = n_insert_front - 1; i >= 0; i--)
456   {
457     capacity = calc_new_capacity(v, 1);
458     v.insert(0, 1, i);
459     size++;
460   }
461 
462   /* Check that the insertion worked as expected */
463   EXPECT_EQ(v.capacity(), capacity);
464   EXPECT_EQ(v.size(), size);
465   for(IndexType i = 0; i < size; ++i)
466   {
467     EXPECT_EQ(v[i], i);
468   }
469 
470   deallocate(values);
471   values = nullptr;
472 }
473 
474 /*!
475  * \brief Check that the insertion into an Array is working properly
476  *        for iterators.
477  * \param [in] v the Array to check.
478  */
479 template <typename T>
check_insert_iterator(Array<T> & v)480 void check_insert_iterator(Array<T>& v)
481 {
482   /* Resize the array up to the capacity */
483   IndexType capacity = v.capacity();
484   v.resize(capacity);
485   IndexType size = capacity;
486 
487   EXPECT_EQ(v.size(), v.capacity());
488 
489   /* Set the existing data in v */
490   for(IndexType i = 0; i < size; ++i)
491   {
492     v[i] = i - 5 * i + 7;
493   }
494 
495   /* Insert a new element, should resize. */
496   IndexType old_capacity = capacity;
497   capacity = calc_new_capacity(v, 1);
498   typename axom::Array<T>::ArrayIterator ret =
499     v.insert(v.end(), 1, size - 5 * size + 7);
500   size++;
501 
502   /* Check that it resized properly */
503   EXPECT_GT(capacity, old_capacity);
504   EXPECT_EQ(v.capacity(), capacity);
505   EXPECT_EQ(v.size(), size);
506   EXPECT_EQ(ret, v.end() - 1);
507   for(IndexType i = 0; i < size; ++i)
508   {
509     EXPECT_EQ(v[i], i - 5 * i + 7);
510   }
511 
512   /* Insert 1000 elements */
513   const IndexType n_elements = 1000;
514   T* values = allocate<T>(n_elements);
515   for(IndexType i = 0; i < n_elements; ++i)
516   {
517     IndexType i_real = i + size;
518     values[i] = i_real - 5 * i_real + 7;
519   }
520 
521   capacity = calc_new_capacity(v, n_elements);
522   typename axom::Array<T>::ArrayIterator ret2 =
523     v.insert(v.end(), n_elements, values);
524 
525   EXPECT_EQ(ret2, v.begin() + size);
526   size += n_elements;
527 
528   /* Check that it resizes properly */
529   EXPECT_EQ(v.capacity(), capacity);
530   EXPECT_EQ(v.size(), size);
531   for(IndexType i = 0; i < size; ++i)
532   {
533     EXPECT_EQ(v[i], i - 5 * i + 7);
534   }
535 
536   capacity = size;
537   v.shrink();
538   IndexType n_insert_front = 100;
539 
540   /* Reset the data */
541   T* data_ptr = v.data();
542   for(IndexType i = 0; i < size; ++i)
543   {
544     data_ptr[i] = i + n_insert_front;
545   }
546 
547   /* Insert into the front of the Array. */
548   for(IndexType i = n_insert_front - 1; i >= 0; i--)
549   {
550     capacity = calc_new_capacity(v, 1);
551     typename axom::Array<T>::ArrayIterator ret3 = v.insert(v.begin(), 1, i);
552     EXPECT_EQ(ret3, v.begin());
553     size++;
554   }
555 
556   /* Check that the insertion worked as expected */
557   EXPECT_EQ(v.capacity(), capacity);
558   EXPECT_EQ(v.size(), size);
559   for(IndexType i = 0; i < size; ++i)
560   {
561     EXPECT_EQ(v[i], i);
562   }
563 
564   deallocate(values);
565   values = nullptr;
566 }
567 
568 /*!
569  * \brief Check that emplace() into an Array is working properly.
570  * \param [in] v the Array to check.
571  */
572 template <typename T>
check_emplace(Array<T> & v)573 void check_emplace(Array<T>& v)
574 {
575   /* Resize the array up to the capacity */
576   IndexType capacity = v.capacity();
577   v.resize(capacity);
578   IndexType size = capacity;
579 
580   EXPECT_EQ(v.size(), v.capacity());
581 
582   /* Set the existing data in v */
583   for(IndexType i = 0; i < size; ++i)
584   {
585     v[i] = i - 5 * i + 7;
586   }
587 
588   /* Emplace a new element, should resize. */
589   IndexType old_capacity = capacity;
590   capacity = calc_new_capacity(v, 1);
591   typename axom::Array<T>::ArrayIterator ret =
592     v.emplace(v.end(), size - 5 * size + 7);
593   size++;
594 
595   /* Check that it resized properly */
596   EXPECT_GT(capacity, old_capacity);
597   EXPECT_EQ(v.size(), size);
598   EXPECT_EQ(ret, v.end() - 1);
599   for(IndexType i = 0; i < size; ++i)
600   {
601     EXPECT_EQ(v[i], i - 5 * i + 7);
602   }
603 
604   /* Emplace_back 1000 elements */
605   const IndexType n_elements = 1000;
606   for(IndexType i = 0; i < n_elements; ++i)
607   {
608     IndexType i_real = i + size;
609     v.emplace_back(i_real - 5 * i_real + 7);
610   }
611 
612   size += n_elements;
613 
614   /* Check that it resizes properly */
615   EXPECT_EQ(v.size(), size);
616   for(IndexType i = 0; i < size; ++i)
617   {
618     EXPECT_EQ(v[i], i - 5 * i + 7);
619   }
620 
621   capacity = size;
622   v.shrink();
623   IndexType n_insert_front = 100;
624 
625   /* Reset the data */
626   T* data_ptr = v.data();
627   for(IndexType i = 0; i < size; ++i)
628   {
629     data_ptr[i] = i + n_insert_front;
630   }
631 
632   /* Emplace into the front of the Array. */
633   for(IndexType i = n_insert_front - 1; i >= 0; i--)
634   {
635     capacity = calc_new_capacity(v, 1);
636     typename axom::Array<T>::ArrayIterator ret3 = v.emplace(v.begin(), i);
637     EXPECT_EQ(ret3, v.begin());
638     size++;
639   }
640 
641   /* Check that the emplace worked as expected */
642   EXPECT_EQ(v.capacity(), capacity);
643   EXPECT_EQ(v.size(), size);
644   for(IndexType i = 0; i < size; ++i)
645   {
646     EXPECT_EQ(v[i], i);
647   }
648 }
649 
650 template <typename T>
check_swap(Array<T> & v)651 void check_swap(Array<T>& v)
652 {
653   axom::Array<T> v_two(v.size());
654 
655   /* Push 0...size elements */
656   for(int i = 0; i < v.size(); i++)
657   {
658     v[i] = i;
659     v_two[i] = -i;
660   }
661 
662   /* Create copies */
663   axom::Array<T> v_copy(v);
664   axom::Array<T> v_two_copy(v_two);
665 
666   EXPECT_EQ(v, v_copy);
667   EXPECT_EQ(v_two, v_two_copy);
668 
669   /* Swap */
670   v.swap(v_two);
671 
672   EXPECT_NE(v, v_two);
673   EXPECT_NE(v, v_copy);
674 
675   /* Swap back */
676   v.swap(v_two);
677 
678   EXPECT_EQ(v, v_copy);
679   EXPECT_EQ(v_two, v_two_copy);
680 }
681 
682 template <typename T, int DIM, axom::MemorySpace SPACE>
check_alloc(Array<T,DIM,SPACE> & v,const int id)683 void check_alloc(Array<T, DIM, SPACE>& v, const int id)
684 {
685   // Verify allocation
686   EXPECT_EQ(v.getAllocatorID(), id);
687   EXPECT_EQ(v.size(), v.capacity());
688 
689 // Use introspection to verify pointer if available
690 #if defined(AXOM_USE_UMPIRE)
691   umpire::ResourceManager& rm = umpire::ResourceManager::getInstance();
692   auto v_allocator = rm.getAllocator(v.data());
693   EXPECT_EQ(v_allocator.getId(), id);
694 #endif
695 }
696 
697 /*!
698  * \brief Check an external array for defects.
699  * \param [in] v the external array to check.
700  */
701 template <typename T>
check_external_view(ArrayView<T> & v)702 void check_external_view(ArrayView<T>& v)
703 {
704   const IndexType size = v.size();
705   const IndexType num_values = size;
706   T* const data_ptr = v.data();
707 
708   /* Set the elements in the array. */
709   for(IndexType i = 0; i < size; ++i)
710   {
711     v[i] = i;
712   }
713 
714   /* Check the elements using the raw pointer. */
715   for(IndexType i = 0; i < num_values; ++i)
716   {
717     EXPECT_EQ(data_ptr[i], i);
718   }
719 
720   /* Set the elements using the raw pointer. */
721   for(IndexType i = 0; i < size; ++i)
722   {
723     data_ptr[i] = i * i;
724   }
725 
726   /* Check the elements using the () operator. */
727   for(IndexType i = 0; i < size; ++i)
728   {
729     EXPECT_EQ(v[i], i * i);
730   }
731 
732   EXPECT_EQ(size, v.size());
733   EXPECT_EQ(data_ptr, v.data());
734 }
735 
736 // FIXME: HIP
737 #if defined(__CUDACC__) && defined(AXOM_USE_UMPIRE)
738 
739 template <typename T>
assign_raw(T * data,int N)740 __global__ void assign_raw(T* data, int N)
741 {
742   for(int i = 0; i < N; i++)
743   {
744     data[i] = i;
745   }
746 }
747 
748 template <typename T, int DIM, axom::MemorySpace SPACE>
assign_view(ArrayView<T,DIM,SPACE> view)749 __global__ void assign_view(ArrayView<T, DIM, SPACE> view)
750 {
751   for(int i = 0; i < view.size(); i++)
752   {
753     view[i] = i * 2;
754   }
755 }
756 
757 /*!
758  * \brief Check that an array can be modified/accessed from device code
759  * \param [in] v the array to check.
760  */
761 template <typename T, int DIM, axom::MemorySpace SPACE>
check_device(Array<T,DIM,SPACE> & v)762 void check_device(Array<T, DIM, SPACE>& v)
763 {
764   const IndexType size = v.size();
765   // Then assign to it via a raw device pointer
766   assign_raw<<<1, 1>>>(v.data(), size);
767 
768   // Check the contents of the array by assigning to a Dynamic array
769   // The default Umpire allocator should be Host, so we can access it from the CPU
770   Array<T, 1> check_raw_array_dynamic = v;
771   EXPECT_EQ(check_raw_array_dynamic.size(), size);
772   for(int i = 0; i < check_raw_array_dynamic.size(); i++)
773   {
774     EXPECT_EQ(check_raw_array_dynamic[i], i);
775   }
776 
777   // Then check the contents by assigning to an explicitly Host array
778   Array<T, 1, axom::MemorySpace::Host> check_raw_array_host = v;
779   EXPECT_EQ(check_raw_array_host.size(), size);
780   for(int i = 0; i < check_raw_array_host.size(); i++)
781   {
782     EXPECT_EQ(check_raw_array_host[i], i);
783   }
784 
785   // Then modify the underlying data via a view
786   ArrayView<T, DIM, SPACE> view(v);
787   assign_view<<<1, 1>>>(view);
788 
789   // Check the contents of the array by assigning to a Dynamic array
790   // The default Umpire allocator should be Host, so we can access it from the CPU
791   Array<T, 1> check_view_array_dynamic = view;
792   EXPECT_EQ(check_view_array_dynamic.size(), size);
793   for(int i = 0; i < check_view_array_dynamic.size(); i++)
794   {
795     EXPECT_EQ(check_view_array_dynamic[i], i * 2);
796   }
797 
798   // Then check the contents by assigning to an explicitly Host array
799   Array<T, 1, axom::MemorySpace::Host> check_view_array_host = view;
800   EXPECT_EQ(check_view_array_host.size(), size);
801   for(int i = 0; i < check_view_array_host.size(); i++)
802   {
803     EXPECT_EQ(check_view_array_host[i], i * 2);
804   }
805 }
806 
807 template <typename T>
assign_raw_2d(T * data,int M,int N)808 __global__ void assign_raw_2d(T* data, int M, int N)
809 {
810   for(int i = 0; i < N; i++)
811   {
812     for(int j = 0; j < N; j++)
813     {
814       data[i * N + j] = i * i + j;
815     }
816   }
817 }
818 
819 template <typename T, axom::MemorySpace SPACE>
assign_view_2d(ArrayView<T,2,SPACE> view)820 __global__ void assign_view_2d(ArrayView<T, 2, SPACE> view)
821 {
822   for(int i = 0; i < view.shape()[0]; i++)
823   {
824     for(int j = 0; j < view.shape()[1]; j++)
825     {
826       view(i, j) = j * j + i;
827     }
828   }
829 }
830 
831 /*!
832  * \brief Check that a 2D array can be modified/accessed from device code
833  * \param [in] v the array to check.
834  */
835 template <typename T, axom::MemorySpace SPACE>
check_device_2D(Array<T,2,SPACE> & v)836 void check_device_2D(Array<T, 2, SPACE>& v)
837 {
838   const IndexType size = v.size();
839   const IndexType M = v.shape()[0];
840   const IndexType N = v.shape()[1];
841   // Then assign to it via a raw device pointer
842   assign_raw_2d<<<1, 1>>>(v.data(), M, N);
843 
844   // Check the contents of the array by assigning to a Dynamic array
845   // The default Umpire allocator should be Host, so we can access it from the CPU
846   Array<T, 2> check_raw_array_dynamic = v;
847   EXPECT_EQ(check_raw_array_dynamic.size(), size);
848   EXPECT_EQ(check_raw_array_dynamic.shape(), v.shape());
849 
850   for(int i = 0; i < M; i++)
851   {
852     for(int j = 0; j < N; j++)
853     {
854       EXPECT_EQ(check_raw_array_dynamic(i, j), i * i + j);
855     }
856   }
857 
858   // Then check the contents by assigning to an explicitly Host array
859   Array<T, 2, axom::MemorySpace::Host> check_raw_array_host = v;
860   EXPECT_EQ(check_raw_array_host.size(), size);
861   EXPECT_EQ(check_raw_array_host.shape(), v.shape());
862 
863   for(int i = 0; i < M; i++)
864   {
865     for(int j = 0; j < N; j++)
866     {
867       EXPECT_EQ(check_raw_array_host(i, j), i * i + j);
868     }
869   }
870 
871   // Then modify the underlying data via a view
872   ArrayView<T, 2, SPACE> view(v);
873   assign_view_2d<<<1, 1>>>(view);
874 
875   // Check the contents of the array by assigning to a Dynamic array
876   // The default Umpire allocator should be Host, so we can access it from the CPU
877   Array<T, 2> check_view_array_dynamic = view;
878   EXPECT_EQ(check_view_array_dynamic.size(), size);
879   EXPECT_EQ(check_view_array_dynamic.shape(), v.shape());
880 
881   for(int i = 0; i < M; i++)
882   {
883     for(int j = 0; j < N; j++)
884     {
885       EXPECT_EQ(check_view_array_dynamic(i, j), j * j + i);
886     }
887   }
888 
889   // Then check the contents by assigning to an explicitly Host array
890   Array<T, 2, axom::MemorySpace::Host> check_view_array_host = view;
891   EXPECT_EQ(check_view_array_host.size(), size);
892   EXPECT_EQ(check_view_array_host.shape(), v.shape());
893 
894   for(int i = 0; i < M; i++)
895   {
896     for(int j = 0; j < N; j++)
897     {
898       EXPECT_EQ(check_view_array_host(i, j), j * j + i);
899     }
900   }
901 }
902 
903 #endif  // defined(__CUDACC__) && defined(AXOM_USE_UMPIRE)
904 
905 } /* end namespace internal */
906 
907 //------------------------------------------------------------------------------
908 // UNIT TESTS
909 //------------------------------------------------------------------------------
910 
911 //------------------------------------------------------------------------------
TEST(core_array,checkStorage)912 TEST(core_array, checkStorage)
913 {
914   constexpr IndexType ZERO = 0;
915 
916   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
917   {
918     Array<int> v_int(ZERO, capacity);
919     internal::check_storage(v_int);
920 
921     Array<double> v_double(ZERO, capacity);
922     internal::check_storage(v_double);
923   }
924 }
925 
926 //------------------------------------------------------------------------------
TEST(core_array,checkFill)927 TEST(core_array, checkFill)
928 {
929   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
930   {
931     IndexType size = capacity / 2;
932     Array<int> v_int(size, capacity);
933     internal::check_fill(v_int);
934 
935     Array<double> v_double(size, capacity);
936     internal::check_fill(v_double);
937   }
938 }
939 
940 //------------------------------------------------------------------------------
TEST(core_array,checkSet)941 TEST(core_array, checkSet)
942 {
943   for(IndexType size = 2; size < 512; size *= 2)
944   {
945     Array<int> v_int(size);
946     internal::check_set(v_int);
947 
948     Array<double> v_double(size);
949     internal::check_set(v_double);
950   }
951 }
952 
953 //------------------------------------------------------------------------------
TEST(core_array,checkResize)954 TEST(core_array, checkResize)
955 {
956   constexpr IndexType ZERO = 0;
957 
958   for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
959   {
960     for(IndexType capacity = 2; capacity <= 512; capacity *= 2)
961     {
962       Array<int> v_int(ZERO, capacity);
963       v_int.setResizeRatio(ratio);
964       internal::check_resize(v_int);
965 
966       Array<double> v_double(ZERO, capacity);
967       v_double.setResizeRatio(ratio);
968       internal::check_resize(v_double);
969     }
970   }
971 }
972 
973 //------------------------------------------------------------------------------
TEST(core_array_DeathTest,checkResize)974 TEST(core_array_DeathTest, checkResize)
975 {
976   const char IGNORE_OUTPUT[] = ".*";
977   constexpr IndexType ZERO = 0;
978   IndexType size = 100;
979 
980   /* Resizing isn't allowed with a ratio less than 1.0. */
981   Array<int> v_int(ZERO, size);
982   v_int.setResizeRatio(0.99);
983   EXPECT_DEATH_IF_SUPPORTED(internal::check_resize(v_int), IGNORE_OUTPUT);
984 }
985 
986 //------------------------------------------------------------------------------
TEST(core_array,checkInsert)987 TEST(core_array, checkInsert)
988 {
989   constexpr IndexType ZERO = 0;
990 
991   for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
992   {
993     for(IndexType capacity = 2; capacity <= 512; capacity *= 2)
994     {
995       Array<int> v_int(ZERO, capacity);
996       v_int.setResizeRatio(ratio);
997       internal::check_insert(v_int);
998 
999       Array<double> v_double(ZERO, capacity);
1000       v_double.setResizeRatio(ratio);
1001       internal::check_insert(v_double);
1002     }
1003   }
1004 }
1005 
1006 //------------------------------------------------------------------------------
TEST(core_array,checkInsertIterator)1007 TEST(core_array, checkInsertIterator)
1008 {
1009   constexpr IndexType ZERO = 0;
1010 
1011   for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1012   {
1013     for(IndexType capacity = 10; capacity <= 512; capacity *= 2)
1014     {
1015       Array<int> v_int(ZERO, capacity);
1016       v_int.setResizeRatio(ratio);
1017       internal::check_insert_iterator(v_int);
1018 
1019       Array<double> v_double(ZERO, capacity);
1020       v_double.setResizeRatio(ratio);
1021       internal::check_insert_iterator(v_double);
1022     }
1023   }
1024 }
1025 
1026 //------------------------------------------------------------------------------
TEST(core_array,checkEmplace)1027 TEST(core_array, checkEmplace)
1028 {
1029   constexpr IndexType ZERO = 0;
1030 
1031   for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1032   {
1033     for(IndexType capacity = 10; capacity <= 512; capacity *= 2)
1034     {
1035       Array<int> v_int(ZERO, capacity);
1036       v_int.setResizeRatio(ratio);
1037       internal::check_emplace(v_int);
1038 
1039       Array<double> v_double(ZERO, capacity);
1040       v_double.setResizeRatio(ratio);
1041       internal::check_emplace(v_double);
1042     }
1043   }
1044 }
1045 
1046 //------------------------------------------------------------------------------
TEST(core_array,checkSwap)1047 TEST(core_array, checkSwap)
1048 {
1049   for(IndexType size = 10; size <= 512; size *= 2)
1050   {
1051     Array<int> v_int(size);
1052     internal::check_swap(v_int);
1053 
1054     Array<double> v_double(size);
1055     internal::check_swap(v_double);
1056   }
1057 }
1058 
1059 //------------------------------------------------------------------------------
TEST(core_array,checkAlloc)1060 TEST(core_array, checkAlloc)
1061 {
1062   std::vector<int> memory_locations
1063   {
1064 #if defined(AXOM_USE_UMPIRE)
1065     axom::getUmpireResourceAllocatorID(umpire::resource::Host)
1066   #if defined(UMPIRE_ENABLE_DEVICE)
1067       ,
1068       axom::getUmpireResourceAllocatorID(umpire::resource::Device)
1069   #endif
1070   #if defined(UMPIRE_ENABLE_UM)
1071         ,
1072       axom::getUmpireResourceAllocatorID(umpire::resource::Unified)
1073   #endif
1074   #if defined(UMPIRE_ENABLE_CONST)
1075         ,
1076       axom::getUmpireResourceAllocatorID(umpire::resource::Constant)
1077   #endif
1078   #if defined(UMPIRE_ENABLE_PINNED)
1079         ,
1080       axom::getUmpireResourceAllocatorID(umpire::resource::Pinned)
1081   #endif
1082 #endif
1083   };
1084 
1085   for(double ratio = 1.0; ratio <= 2.0; ratio += 0.5)
1086   {
1087     for(IndexType capacity = 4; capacity <= 512; capacity *= 2)
1088     {
1089       // First use the dynamic option
1090       for(int id : memory_locations)
1091       {
1092         Array<int, 1, axom::MemorySpace::Dynamic> v_int(capacity, capacity, id);
1093         internal::check_alloc(v_int, id);
1094 
1095         Array<double, 1, axom::MemorySpace::Dynamic> v_double(capacity,
1096                                                               capacity,
1097                                                               id);
1098         internal::check_alloc(v_double, id);
1099       }
1100 // Then, if Umpire is available, we can use the space as an explicit template parameter
1101 #ifdef AXOM_USE_UMPIRE
1102   #ifdef UMPIRE_ENABLE_DEVICE
1103       Array<int, 1, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1104       internal::check_alloc(
1105         v_int_device,
1106         axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1107       Array<double, 1, axom::MemorySpace::Device> v_double_device(capacity,
1108                                                                   capacity);
1109       internal::check_alloc(
1110         v_double_device,
1111         axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1112   #endif
1113   #ifdef UMPIRE_ENABLE_UM
1114       Array<int, 1, axom::MemorySpace::Unified> v_int_unified(capacity, capacity);
1115       internal::check_alloc(
1116         v_int_unified,
1117         axom::getUmpireResourceAllocatorID(umpire::resource::Unified));
1118       Array<double, 1, axom::MemorySpace::Unified> v_double_unified(capacity,
1119                                                                     capacity);
1120       internal::check_alloc(
1121         v_double_unified,
1122         axom::getUmpireResourceAllocatorID(umpire::resource::Unified));
1123   #endif
1124   #ifdef UMPIRE_ENABLE_CONST
1125       Array<int, 1, axom::MemorySpace::Constant> v_int_const(capacity, capacity);
1126       internal::check_alloc(
1127         v_int_const,
1128         axom::getUmpireResourceAllocatorID(umpire::resource::Constant));
1129       Array<double, 1, axom::MemorySpace::Constant> v_double_const(capacity,
1130                                                                    capacity);
1131       internal::check_alloc(
1132         v_double_const,
1133         axom::getUmpireResourceAllocatorID(umpire::resource::Constant));
1134   #endif
1135   #ifdef UMPIRE_ENABLE_PINNED
1136       Array<int, 1, axom::MemorySpace::Pinned> v_int_pinned(capacity, capacity);
1137       internal::check_alloc(
1138         v_int_pinned,
1139         axom::getUmpireResourceAllocatorID(umpire::resource::Pinned));
1140       Array<double, 1, axom::MemorySpace::Pinned> v_double_pinned(capacity,
1141                                                                   capacity);
1142       internal::check_alloc(
1143         v_double_pinned,
1144         axom::getUmpireResourceAllocatorID(umpire::resource::Pinned));
1145   #endif
1146 #endif
1147     }
1148   }
1149 }
1150 
1151 //------------------------------------------------------------------------------
TEST(core_array,checkExternalView)1152 TEST(core_array, checkExternalView)
1153 {
1154   constexpr double MAGIC_NUM = 5683578.8;
1155   constexpr IndexType MAX_SIZE = 256;
1156   constexpr IndexType MAX_VALUES = MAX_SIZE;
1157   union DataBuffer
1158   {
1159     int ints[MAX_SIZE];
1160     double doubles[MAX_SIZE];
1161   };
1162 
1163   DataBuffer buffer;
1164   std::fill_n(buffer.doubles, MAX_VALUES, MAGIC_NUM);
1165 
1166   for(IndexType size = 16; size <= MAX_SIZE; size *= 2)
1167   {
1168     ArrayView<int> v_int_view(buffer.ints, size);
1169     EXPECT_EQ(v_int_view.data(), buffer.ints);
1170     internal::check_external_view(v_int_view);
1171 
1172     ArrayView<double> v_double_view(buffer.doubles, size);
1173     EXPECT_EQ(v_double_view.data(), buffer.doubles);
1174     internal::check_external_view(v_double_view);
1175 
1176     /* Set v_double's data to MAGIC_NUM */
1177     std::fill_n(v_double_view.data(), size, MAGIC_NUM);
1178 
1179     /* Check that the data still exists in the buffer */
1180     for(IndexType i = 0; i < MAX_VALUES; ++i)
1181     {
1182       EXPECT_EQ(buffer.doubles[i], MAGIC_NUM);
1183     }
1184   }
1185 }
1186 
1187 //------------------------------------------------------------------------------
TEST(core_array,checkIterator)1188 TEST(core_array, checkIterator)
1189 {
1190   constexpr int SIZE = 1000;
1191   axom::Array<int> v_int(SIZE);
1192 
1193   /* Push 0...999 elements */
1194   for(int i = 0; i < SIZE; i++)
1195   {
1196     v_int[i] = i;
1197   }
1198 
1199   EXPECT_EQ(*v_int.begin(), 0);
1200   EXPECT_EQ(*(v_int.end() - 1), SIZE - 1);
1201   EXPECT_EQ(v_int.size(), SIZE);
1202 
1203   /* Erase nothing */
1204   axom::Array<int>::ArrayIterator ret1 =
1205     v_int.erase(v_int.begin() + SIZE / 2, v_int.begin() + SIZE / 2);
1206 
1207   EXPECT_EQ(ret1, v_int.begin() + SIZE / 2);
1208   EXPECT_EQ(v_int.size(), SIZE);
1209 
1210   /* Erase half the elements */
1211   axom::Array<int>::ArrayIterator ret2 =
1212     v_int.erase(v_int.begin(), v_int.begin() + SIZE / 2);
1213 
1214   EXPECT_EQ(ret2, v_int.begin());
1215   EXPECT_EQ(*v_int.begin(), SIZE / 2);
1216   EXPECT_EQ(*(v_int.end() - 1), SIZE - 1);
1217   EXPECT_EQ(v_int.size(), SIZE / 2);
1218 
1219   /* Erase first, last elements */
1220   axom::Array<int>::ArrayIterator ret3 = v_int.erase(v_int.begin());
1221 
1222   EXPECT_EQ(ret3, v_int.begin());
1223   EXPECT_EQ(*v_int.begin(), SIZE / 2 + 1);
1224 
1225   axom::Array<int>::ArrayIterator ret4 = v_int.erase(v_int.end() - 1);
1226 
1227   EXPECT_EQ(ret4, v_int.end());
1228   EXPECT_EQ(*(v_int.end() - 1), SIZE - 2);
1229 
1230   /* Clear the rest of the array */
1231   v_int.clear();
1232   EXPECT_EQ(v_int.size(), 0);
1233 }
1234 
1235 //------------------------------------------------------------------------------
TEST(core_array,check_move_copy)1236 TEST(core_array, check_move_copy)
1237 {
1238   constexpr int MAGIC_INT = 255;
1239   constexpr double MAGIC_DOUBLE = 5683578.8;
1240 
1241   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1242   {
1243     IndexType size = capacity;
1244 
1245     /* Check copy and move semantics for Array of ints */
1246     Array<int> v_int(size, capacity);
1247     v_int.fill(MAGIC_INT);
1248 
1249     Array<int> v_int_copy_ctor(v_int);
1250     Array<int> v_int_copy_assign;
1251     v_int_copy_assign = v_int;
1252     EXPECT_EQ(v_int, v_int_copy_ctor);
1253     EXPECT_EQ(v_int, v_int_copy_assign);
1254 
1255     Array<int> v_int_move_assign;
1256     v_int_move_assign = std::move(v_int_copy_assign);
1257     Array<int> v_int_move_ctor = std::move(v_int_copy_ctor);
1258     EXPECT_EQ(v_int, v_int_move_assign);
1259     EXPECT_EQ(v_int, v_int_move_ctor);
1260     EXPECT_EQ(v_int_copy_assign.data(), nullptr);
1261     EXPECT_EQ(v_int_copy_ctor.data(), nullptr);
1262 
1263     /* Check copy and move semantics for Array of doubles */
1264     Array<double> v_double(size, capacity);
1265     v_double.fill(MAGIC_DOUBLE);
1266 
1267     Array<double> v_double_copy_ctor(v_double);
1268     Array<double> v_double_copy_assign;
1269     v_double_copy_assign = v_double;
1270     EXPECT_EQ(v_double, v_double_copy_ctor);
1271     EXPECT_EQ(v_double, v_double_copy_assign);
1272 
1273     Array<double> v_double_move_assign;
1274     v_double_move_assign = std::move(v_double_copy_assign);
1275     Array<double> v_double_move_ctor = std::move(v_double_copy_ctor);
1276     EXPECT_EQ(v_double, v_double_move_assign);
1277     EXPECT_EQ(v_double, v_double_move_ctor);
1278     EXPECT_EQ(v_double_copy_assign.data(), nullptr);
1279     EXPECT_EQ(v_double_copy_ctor.data(), nullptr);
1280   }
1281 }
1282 
1283 //------------------------------------------------------------------------------
TEST(core_array,check_view_move_copy)1284 TEST(core_array, check_view_move_copy)
1285 {
1286   constexpr int MAGIC_INT = 255;
1287   constexpr double MAGIC_DOUBLE = 5683578.8;
1288 
1289   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1290   {
1291     IndexType size = capacity;
1292 
1293     /* Check copy and move semantics for ArrayView of ints */
1294     std::vector<int> ints(size, MAGIC_INT);
1295     ArrayView<int> v_int_view(ints.data(), size);
1296 
1297     ArrayView<int> v_int_view_copy_ctor(v_int_view);
1298     ArrayView<int> v_int_view_copy_assign;
1299     v_int_view_copy_assign = v_int_view;
1300     EXPECT_EQ(v_int_view, v_int_view_copy_ctor);
1301     EXPECT_EQ(v_int_view, v_int_view_copy_assign);
1302 
1303     /* Check copy and move semantics for ArrayView of doubles */
1304     std::vector<double> doubles(size, MAGIC_DOUBLE);
1305     ArrayView<double> v_double_view(doubles.data(), size);
1306 
1307     ArrayView<double> v_double_view_copy_ctor(v_double_view);
1308     ArrayView<double> v_double_view_copy_assign;
1309     v_double_view_copy_assign = v_double_view;
1310     EXPECT_EQ(v_double_view, v_double_view_copy_ctor);
1311     EXPECT_EQ(v_double_view, v_double_view_copy_assign);
1312   }
1313 }
1314 
1315 //------------------------------------------------------------------------------
TEST(core_array,check_multidimensional)1316 TEST(core_array, check_multidimensional)
1317 {
1318   constexpr int MAGIC_INT = 255;
1319   constexpr double MAGIC_DOUBLE = 5683578.8;
1320 
1321   // First test multidimensional int arrays
1322   Array<int, 2> v_int;
1323   v_int.resize(2, 2);
1324   v_int.fill(MAGIC_INT);
1325   // Make sure the number of elements and contents are correct
1326   EXPECT_EQ(v_int.size(), 2 * 2);
1327   StackArray<IndexType, 2> expected_shape = {2, 2};
1328   EXPECT_EQ(v_int.shape(), expected_shape);
1329   for(const auto val : v_int)
1330   {
1331     EXPECT_EQ(val, MAGIC_INT);
1332   }
1333   // Then assign different values to each element
1334   v_int(0, 0) = 1;
1335   v_int(0, 1) = 2;
1336   v_int(1, 0) = 3;
1337   v_int(1, 1) = 4;
1338 
1339   // FIXME: Should we add a std::initializer_list ctor?
1340   Array<int> v_int_flat(4);
1341   v_int_flat[0] = 1;
1342   v_int_flat[1] = 2;
1343   v_int_flat[2] = 3;
1344   v_int_flat[3] = 4;
1345   StackArray<IndexType, 1> expected_flat_shape = {4};
1346   EXPECT_EQ(v_int_flat.shape(), expected_flat_shape);
1347 
1348   for(int i = 0; i < v_int_flat.size(); i++)
1349   {
1350     // For a multidim array, op[] is a "flat" index into the raw data
1351     EXPECT_EQ(v_int[i], v_int_flat[i]);
1352   }
1353 
1354   Array<double, 3> v_double(4, 3, 2);
1355   v_double.fill(MAGIC_DOUBLE);
1356   EXPECT_EQ(v_double.size(), 4 * 3 * 2);
1357   StackArray<IndexType, 3> expected_double_shape = {4, 3, 2};
1358   EXPECT_EQ(v_double.shape(), expected_double_shape);
1359   for(const auto val : v_double)
1360   {
1361     EXPECT_EQ(val, MAGIC_DOUBLE);
1362   }
1363 
1364   Array<double> v_double_flat(4 * 3 * 2);
1365   int double_flat_idx = 0;
1366   for(int i = 0; i < v_double.shape()[0]; i++)
1367   {
1368     for(int j = 0; j < v_double.shape()[1]; j++)
1369     {
1370       for(int k = 0; k < v_double.shape()[2]; k++)
1371       {
1372         v_double(i, j, k) = (i * i) + (5 * j) + k;
1373         v_double_flat[double_flat_idx++] = (i * i) + (5 * j) + k;
1374       }
1375     }
1376   }
1377 
1378   for(int i = 0; i < v_double.size(); i++)
1379   {
1380     // For a multidim array, op[] is a "flat" index into the raw data
1381     EXPECT_EQ(v_double[i], v_double_flat[i]);
1382   }
1383 }
1384 
1385 //------------------------------------------------------------------------------
TEST(core_array,check_multidimensional_view)1386 TEST(core_array, check_multidimensional_view)
1387 {
1388   constexpr int MAGIC_INT = 255;
1389   constexpr double MAGIC_DOUBLE = 5683578.8;
1390 
1391   // First test multidimensional int arrays
1392   int v_int_arr[] = {MAGIC_INT, MAGIC_INT, MAGIC_INT, MAGIC_INT};
1393   ArrayView<int, 2> v_int_view(v_int_arr, 2, 2);
1394   // Make sure the number of elements and contents are correct
1395   EXPECT_EQ(v_int_view.size(), 2 * 2);
1396   StackArray<IndexType, 2> expected_shape = {2, 2};
1397   EXPECT_EQ(v_int_view.shape(), expected_shape);
1398   for(const auto val : v_int_view)
1399   {
1400     EXPECT_EQ(val, MAGIC_INT);
1401   }
1402   // Then assign different values to each element
1403   v_int_view(0, 0) = 1;
1404   v_int_view(0, 1) = 2;
1405   v_int_view(1, 0) = 3;
1406   v_int_view(1, 1) = 4;
1407 
1408   // FIXME: Should we add a std::initializer_list ctor?
1409   int v_int_flat_arr[] = {1, 2, 3, 4};
1410   ArrayView<int> v_int_flat_view(v_int_flat_arr, 4);
1411   StackArray<IndexType, 1> expected_flat_shape = {4};
1412   EXPECT_EQ(v_int_flat_view.shape(), expected_flat_shape);
1413 
1414   for(int i = 0; i < v_int_flat_view.size(); i++)
1415   {
1416     // For a multidim array, op[] is a "flat" index into the raw data
1417     EXPECT_EQ(v_int_view[i], v_int_flat_view[i]);
1418   }
1419 
1420   double v_double_arr[4 * 3 * 2];
1421   std::fill_n(v_double_arr, 4 * 3 * 2, MAGIC_DOUBLE);
1422   ArrayView<double, 3> v_double_view(v_double_arr, 4, 3, 2);
1423   EXPECT_EQ(v_double_view.size(), 4 * 3 * 2);
1424   StackArray<IndexType, 3> expected_double_shape = {4, 3, 2};
1425   EXPECT_EQ(v_double_view.shape(), expected_double_shape);
1426   for(const auto val : v_double_view)
1427   {
1428     EXPECT_EQ(val, MAGIC_DOUBLE);
1429   }
1430 
1431   double v_double_flat_arr[4 * 3 * 2];
1432   ArrayView<double> v_double_flat_view(v_double_flat_arr, 4 * 3 * 2);
1433   int double_flat_idx = 0;
1434   for(int i = 0; i < v_double_view.shape()[0]; i++)
1435   {
1436     for(int j = 0; j < v_double_view.shape()[1]; j++)
1437     {
1438       for(int k = 0; k < v_double_view.shape()[2]; k++)
1439       {
1440         v_double_view(i, j, k) = (i * i) + (5 * j) + k;
1441         v_double_flat_view[double_flat_idx++] = (i * i) + (5 * j) + k;
1442       }
1443     }
1444   }
1445 
1446   for(int i = 0; i < v_double_view.size(); i++)
1447   {
1448     // For a multidim array, op[] is a "flat" index into the raw data
1449     EXPECT_EQ(v_double_view[i], v_double_flat_view[i]);
1450   }
1451 }
1452 
1453 //------------------------------------------------------------------------------
TEST(core_array,checkDevice)1454 TEST(core_array, checkDevice)
1455 {
1456 // FIXME: HIP
1457 #if !defined(__CUDACC__) || !defined(AXOM_USE_UMPIRE) || \
1458   !defined(UMPIRE_ENABLE_DEVICE)
1459   GTEST_SKIP()
1460     << "CUDA is not available, skipping tests that use Array in device code";
1461 #else
1462   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1463   {
1464     // Allocate a Dynamic array in Device memory
1465     Array<int, 1, axom::MemorySpace::Dynamic> v_int_dynamic(
1466       capacity,
1467       capacity,
1468       axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1469 
1470     internal::check_device(v_int_dynamic);
1471 
1472     Array<double, 1, axom::MemorySpace::Dynamic> v_double_dynamic(
1473       capacity,
1474       capacity,
1475       axom::getUmpireResourceAllocatorID(umpire::resource::Device));
1476 
1477     internal::check_device(v_double_dynamic);
1478 
1479     // Then allocate an explicitly Device array
1480     Array<int, 1, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1481     internal::check_device(v_int_device);
1482 
1483     Array<double, 1, axom::MemorySpace::Device> v_double_device(capacity,
1484                                                                 capacity);
1485     internal::check_device(v_double_device);
1486   }
1487 #endif
1488 }
1489 
1490 //------------------------------------------------------------------------------
TEST(core_array,checkDevice2D)1491 TEST(core_array, checkDevice2D)
1492 {
1493 // FIXME: HIP
1494 #if !defined(__CUDACC__) || !defined(AXOM_USE_UMPIRE) || \
1495   !defined(UMPIRE_ENABLE_DEVICE)
1496   GTEST_SKIP()
1497     << "CUDA is not available, skipping tests that use Array in device code";
1498 #else
1499   for(IndexType capacity = 2; capacity < 512; capacity *= 2)
1500   {
1501     // Allocate an explicitly Device array
1502     Array<int, 2, axom::MemorySpace::Device> v_int_device(capacity, capacity);
1503     internal::check_device_2D(v_int_device);
1504 
1505     Array<double, 2, axom::MemorySpace::Device> v_double_device(capacity,
1506                                                                 capacity);
1507     internal::check_device_2D(v_double_device);
1508   }
1509 #endif
1510 }
1511 
1512 } /* end namespace axom */
1513