// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mfem.hpp" #include "unit_tests.hpp" using namespace mfem; TEST_CASE("Array init-list and C-style array constructors", "[Array]") { int ContigData[6] = {6, 5, 4, 3, 2, 1}; // Pointer and size constructor Array a(ContigData, 6); // Braced-list constructor Array b({6, 5, 4, 3, 2, 1}); Array c{6, 5, 4, 3, 2, 1}; // Statically sized C-style array constructor Array d(ContigData); // Convertible type constructors Array e({6.0, 5.0, 4.0, 3.0, 2.0, 1.0}); Array f{6.0, 5.0, 4.0, 3.0, 2.0, 1.0}; for (int i = 0; i < a.Size(); i++) { REQUIRE(a[i] == b[i]); REQUIRE(a[i] == c[i]); REQUIRE(a[i] == d[i]); REQUIRE(a[i] == e[i]); REQUIRE(a[i] == f[i]); } } TEST_CASE("Array entry sorting", "[Array]") { int ContigData[6] = {6, 5, 4, 3, 2, 1}; Array a(ContigData, 6); Array b{1, 2, 3, 3, 2, 1}; a.Sort(); b.Sort(); for (int i = 1; i < a.Size(); i++) { REQUIRE(a[i] >= a[i-1]); } for (int i = 1; i < b.Size(); i++) { REQUIRE(b[i] >= b[i-1]); } } TEST_CASE("Array entry strict sorting", "[Array]") { int ContigData[6] = {6, 1, 4, 1, 2, 1}; Array a(ContigData, 6); Array b{1, 2, 3, 3, 2, 1}; a.Sort(); b.Sort(); a.Unique(); b.Unique(); for (int i = 1; i < a.Size(); i++) { REQUIRE(a[i] > a[i-1]); } for (int i = 1; i < b.Size(); i++) { REQUIRE(b[i] > b[i-1]); } } TEST_CASE("Array stl-interactions", "[Array]") { Array x{0,1,2,3,4}, y; std::copy(x.begin(), x.end(), std::back_inserter(y)); CHECK(y.Size() == 5); for (int i : {0,1,2,3,4}) { CHECK(y[i] == i); } y.DeleteAll(); std::copy_if(x.begin(), x.end(), std::back_inserter(y), [](int x) { return x <= 2; }); CHECK(y.Size() == 3); for (int i : {0,1,2}) { CHECK(y[i] == i); } std::transform(y.begin(), y.end(), y.begin(), [](int x) { return x*x; }); CHECK(y.Size() == 3); for (int i : {0,1,2}) { CHECK(y[i] == i*i); } y.DeleteAll(); for (auto i : x) { y.Append(i); } for (int i : {0,1,2,3,4}) { CHECK(x[i] == y[i]); } y.DeleteAll(); for (const auto &i : x) { y.Append(i); } for (int i : {0,1,2,3,4}) { CHECK(x[i] == y[i]); } } TEST_CASE("Array move assignment to view (MakeRef)", "[Array]") { constexpr int n = 5; // Helper function that returns an Array by value (rvalue) auto make_sequence_array = [](int n) { Array arr(n); for (int i = 0; i < n; i++) { arr[i] = i + 1; // 1, 2, 3, ... } return arr; }; // Create backing storage Array backing1(n), backing2(n); backing1 = 0.0; backing2 = 0.0; // Create a view into the backing storage Array view1, view2; view1.MakeRef(backing1); view2.MakeRef(backing2); auto seq_array = make_sequence_array(n); view1 = seq_array; // copy assign view2 = std::move(seq_array); // move assign CHECK(seq_array.Size() == 0); // seq_array is invalidated by the move // Both assignments should be semantically equivalent. for (int i = 0; i < n; i++) { CHECK(backing1[i] == backing2[i]); } // Create backing storage as raw array int backing3[n] = {0, 0, 0, 0, 0}; // Create a view into the backing storage Array view3; view3.MakeRef(backing3, n); // Assign from rvalue view3 = std::move(view2); for (int i = 0; i < n; i++) { CHECK(backing3[i] == backing1[i]); } } TEST_CASE("Array delete at indices", "[Array],[GPU]") { for (int use_dev = 0; use_dev < 2; use_dev++) { Array test({0,1,2,3,4,5,6,7,8}); Array rm_indices({0, 3,4, 6, 8}); Array result({ 1,2, 5, 7 }); test.GetMemory().UseDevice(use_dev); test.DeleteAt(rm_indices); REQUIRE(test.Size() == result.Size()); test.HostReadWrite(); for (int i = 0; i < test.Size(); i++) { CHECK(test[i] == result[i]); } } }