#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>

#include <MeshDataBaseForTests.hpp>
#include <mesh/Connectivity.hpp>
#include <mesh/ItemArray.hpp>
#include <mesh/ItemArrayUtils.hpp>
#include <mesh/Mesh.hpp>
#include <utils/Messenger.hpp>

// Instantiate to ensure full coverage is performed
template class ItemArray<int, ItemType::cell>;

// clazy:excludeall=non-pod-global-static

TEST_CASE("ItemArrayUtils", "[mesh]")
{
  SECTION("Synchronize")
  {
    SECTION("1D")
    {
      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
      const Connectivity<1>& connectivity  = mesh_1d.connectivity();

      SECTION("node")
      {
        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
        auto node_number = connectivity.nodeNumber();

        for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
          SubArray array      = weak_node_array[i_node];
          const size_t number = node_number[i_node];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        NodeArray<const size_t> node_array{weak_node_array};

        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());

        {   // before synchronization
          auto node_owner    = connectivity.nodeOwner();
          auto node_is_owned = connectivity.nodeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            if (node_is_owned[i_node]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_node_array);

        {   // after synchronization
          auto node_owner = connectivity.nodeOwner();

          bool is_synchronized = true;
          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("edge")
      {
        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
        auto edge_number = connectivity.edgeNumber();

        for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
          SubArray array      = weak_edge_array[i_edge];
          const size_t number = edge_number[i_edge];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        EdgeArray<const size_t> edge_array{weak_edge_array};

        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());

        {   // before synchronization
          auto edge_owner    = connectivity.edgeOwner();
          auto edge_is_owned = connectivity.edgeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            if (edge_is_owned[i_edge]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_edge_array);

        {   // after synchronization
          auto edge_owner = connectivity.edgeOwner();

          bool is_synchronized = true;
          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("face")
      {
        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
        auto face_number = connectivity.faceNumber();

        for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
          SubArray array      = weak_face_array[i_face];
          const size_t number = face_number[i_face];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        FaceArray<const size_t> face_array{weak_face_array};

        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());

        {   // before synchronization
          auto face_owner    = connectivity.faceOwner();
          auto face_is_owned = connectivity.faceIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            if (face_is_owned[i_face]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_face_array);

        {   // after synchronization
          auto face_owner = connectivity.faceOwner();

          bool is_synchronized = true;
          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("cell")
      {
        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
        auto cell_number = connectivity.cellNumber();

        for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
          SubArray array      = weak_cell_array[i_cell];
          const size_t number = cell_number[i_cell];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        CellArray<const size_t> cell_array{weak_cell_array};

        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());

        {   // before synchronization
          auto cell_owner    = connectivity.cellOwner();
          auto cell_is_owned = connectivity.cellIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            if (cell_is_owned[i_cell]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_cell_array);

        {   // after synchronization
          auto cell_owner = connectivity.cellOwner();

          bool is_synchronized = true;
          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }
    }

    SECTION("2D")
    {
      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
      const Connectivity<2>& connectivity  = mesh_2d.connectivity();

      SECTION("node")
      {
        WeakNodeArray<size_t> weak_node_array{connectivity, 3};
        auto node_number = connectivity.nodeNumber();

        for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
          SubArray array      = weak_node_array[i_node];
          const size_t number = node_number[i_node];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        NodeArray<const size_t> node_array{weak_node_array};

        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());

        {   // before synchronization
          auto node_owner    = connectivity.nodeOwner();
          auto node_is_owned = connectivity.nodeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            if (node_is_owned[i_node]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_node_array);

        {   // after synchronization
          auto node_owner = connectivity.nodeOwner();

          bool is_synchronized = true;
          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("edge")
      {
        WeakEdgeArray<size_t> weak_edge_array{connectivity, 3};
        auto edge_number = connectivity.edgeNumber();

        for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
          SubArray array      = weak_edge_array[i_edge];
          const size_t number = edge_number[i_edge];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        EdgeArray<const size_t> edge_array{weak_edge_array};

        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());

        {   // before synchronization
          auto edge_owner    = connectivity.edgeOwner();
          auto edge_is_owned = connectivity.edgeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            if (edge_is_owned[i_edge]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_edge_array);

        {   // after synchronization
          auto edge_owner = connectivity.edgeOwner();

          bool is_synchronized = true;
          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("face")
      {
        WeakFaceArray<size_t> weak_face_array{connectivity, 3};
        auto face_number = connectivity.faceNumber();

        for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
          SubArray array      = weak_face_array[i_face];
          const size_t number = face_number[i_face];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        FaceArray<const size_t> face_array{weak_face_array};

        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());

        {   // before synchronization
          auto face_owner    = connectivity.faceOwner();
          auto face_is_owned = connectivity.faceIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            if (face_is_owned[i_face]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_face_array);

        {   // after synchronization
          auto face_owner = connectivity.faceOwner();

          bool is_synchronized = true;
          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("cell")
      {
        WeakCellArray<size_t> weak_cell_array{connectivity, 3};
        auto cell_number = connectivity.cellNumber();

        for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
          SubArray array      = weak_cell_array[i_cell];
          const size_t number = cell_number[i_cell];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        CellArray<const size_t> cell_array{weak_cell_array};

        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());

        {   // before synchronization
          auto cell_owner    = connectivity.cellOwner();
          auto cell_is_owned = connectivity.cellIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            if (cell_is_owned[i_cell]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_cell_array);

        {   // after synchronization
          auto cell_owner = connectivity.cellOwner();

          bool is_synchronized = true;
          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }
    }

    SECTION("3D")
    {
      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
      const Connectivity<3>& connectivity  = mesh_3d.connectivity();

      SECTION("node")
      {
        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
        auto node_number = connectivity.nodeNumber();

        for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
          SubArray array      = weak_node_array[i_node];
          const size_t number = node_number[i_node];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        NodeArray<const size_t> node_array{weak_node_array};

        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());

        {   // before synchronization
          auto node_owner    = connectivity.nodeOwner();
          auto node_is_owned = connectivity.nodeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            if (node_is_owned[i_node]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_node_array);

        {   // after synchronization
          auto node_owner = connectivity.nodeOwner();

          bool is_synchronized = true;
          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
            SubArray array      = node_array[i_node];
            const size_t number = node_number[i_node];
            const size_t owner  = node_owner[i_node];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("edge")
      {
        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
        auto edge_number = connectivity.edgeNumber();

        for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
          SubArray array      = weak_edge_array[i_edge];
          const size_t number = edge_number[i_edge];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        EdgeArray<const size_t> edge_array{weak_edge_array};

        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());

        {   // before synchronization
          auto edge_owner    = connectivity.edgeOwner();
          auto edge_is_owned = connectivity.edgeIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            if (edge_is_owned[i_edge]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_edge_array);

        {   // after synchronization
          auto edge_owner = connectivity.edgeOwner();

          bool is_synchronized = true;
          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
            SubArray array      = edge_array[i_edge];
            const size_t number = edge_number[i_edge];
            const size_t owner  = edge_owner[i_edge];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("face")
      {
        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
        auto face_number = connectivity.faceNumber();

        for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
          SubArray array      = weak_face_array[i_face];
          const size_t number = face_number[i_face];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        FaceArray<const size_t> face_array{weak_face_array};

        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());

        {   // before synchronization
          auto face_owner    = connectivity.faceOwner();
          auto face_is_owned = connectivity.faceIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            if (face_is_owned[i_face]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_face_array);

        {   // after synchronization
          auto face_owner = connectivity.faceOwner();

          bool is_synchronized = true;
          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
            SubArray array      = face_array[i_face];
            const size_t number = face_number[i_face];
            const size_t owner  = face_owner[i_face];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }

      SECTION("cell")
      {
        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
        auto cell_number = connectivity.cellNumber();

        for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
          SubArray array      = weak_cell_array[i_cell];
          const size_t number = cell_number[i_cell];
          for (size_t i = 0; i < array.size(); ++i) {
            array[i] = number + (parallel::rank() + 1) * i;
          }
        }

        CellArray<const size_t> cell_array{weak_cell_array};

        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());

        {   // before synchronization
          auto cell_owner    = connectivity.cellOwner();
          auto cell_is_owned = connectivity.cellIsOwned();

          bool is_synchronized = (parallel::size() > 1);
          bool is_valid        = true;
          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            if (cell_is_owned[i_cell]) {
              for (size_t i = 0; i < array.size(); ++i) {
                is_valid &= (array[i] == number + (owner + 1) * i);
              }
            } else {
              for (size_t i = 0; i < array.size(); ++i) {
                is_synchronized &= (array[i] == number + (owner + 1) * i);
              }
            }
          }

          REQUIRE(is_valid);
          REQUIRE(not is_synchronized);
        }

        synchronize(weak_cell_array);

        {   // after synchronization
          auto cell_owner = connectivity.cellOwner();

          bool is_synchronized = true;
          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
            SubArray array      = cell_array[i_cell];
            const size_t number = cell_number[i_cell];
            const size_t owner  = cell_owner[i_cell];
            for (size_t i = 0; i < array.size(); ++i) {
              is_synchronized &= (array[i] == number + (owner + 1) * i);
            }
          }

          REQUIRE(is_synchronized);
        }
      }
    }
  }
}