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

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

// Instantiate to ensure full coverage is performed
template class SubItemValuePerItem<size_t, NodeOfCell>;

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

TEST_CASE("SubItemValuePerItem", "[mesh]")
{
  SECTION("default constructors")
  {
    REQUIRE_NOTHROW(NodeValuePerEdge<int>{});
    REQUIRE_NOTHROW(NodeValuePerFace<int>{});
    REQUIRE_NOTHROW(NodeValuePerCell<int>{});

    REQUIRE_NOTHROW(EdgeValuePerNode<int>{});
    REQUIRE_NOTHROW(EdgeValuePerFace<int>{});
    REQUIRE_NOTHROW(EdgeValuePerCell<int>{});

    REQUIRE_NOTHROW(FaceValuePerNode<int>{});
    REQUIRE_NOTHROW(FaceValuePerEdge<int>{});
    REQUIRE_NOTHROW(FaceValuePerCell<int>{});

    REQUIRE_NOTHROW(CellValuePerNode<int>{});
    REQUIRE_NOTHROW(CellValuePerEdge<int>{});
    REQUIRE_NOTHROW(CellValuePerFace<int>{});

    REQUIRE(not NodeValuePerEdge<int>{}.isBuilt());
    REQUIRE(not NodeValuePerFace<int>{}.isBuilt());
    REQUIRE(not NodeValuePerCell<int>{}.isBuilt());

    REQUIRE(not EdgeValuePerNode<int>{}.isBuilt());
    REQUIRE(not EdgeValuePerFace<int>{}.isBuilt());
    REQUIRE(not EdgeValuePerCell<int>{}.isBuilt());

    REQUIRE(not FaceValuePerNode<int>{}.isBuilt());
    REQUIRE(not FaceValuePerEdge<int>{}.isBuilt());
    REQUIRE(not FaceValuePerCell<int>{}.isBuilt());

    REQUIRE(not CellValuePerNode<int>{}.isBuilt());
    REQUIRE(not CellValuePerEdge<int>{}.isBuilt());
    REQUIRE(not CellValuePerFace<int>{}.isBuilt());
  }

  SECTION("dimensions")
  {
    auto number_of_values = [](const auto& sub_item_value_per_item) -> size_t {
      using SubItemValuePerItemType = std::decay_t<decltype(sub_item_value_per_item)>;
      using ItemId                  = typename SubItemValuePerItemType::ItemId;

      size_t number = 0;
      for (ItemId item_id = 0; item_id < sub_item_value_per_item.numberOfItems(); ++item_id) {
        number += sub_item_value_per_item.numberOfSubValues(item_id);
      }
      return number;
    };

    auto check_same_memory = [](const auto& sub_item_value_per_item, auto array) {
      const bool same_size = (sub_item_value_per_item.numberOfValues() == array.size());
      REQUIRE(same_size);
      bool is_same = true;
      for (size_t i = 0; i < sub_item_value_per_item.numberOfValues(); ++i) {
        is_same &= (&(sub_item_value_per_item[i]) == &(array[i]));
      }
      return is_same;
    };

    SECTION("1D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_1d = named_mesh.mesh();

          const Connectivity<1>& connectivity = mesh_1d->connectivity();

          SECTION("per cell")
          {
            NodeValuePerCell<int> node_value_per_cell{connectivity};
            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            const size_t nb_values = number_of_values(node_value_per_cell);
            REQUIRE(node_value_per_cell.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            NodeValuePerCell<int> node_value_per_cell_from_array{connectivity, array};
            REQUIRE(check_same_memory(node_value_per_cell_from_array, array));

            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &=
                  (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and
                  (node_value_per_cell.itemArray(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (const_node_value_per_cell.itemArray(cell_id).size() ==
                               node_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));

            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerCell<int> face_value_per_cell{connectivity};
            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));

            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per face")
          {
            CellValuePerFace<int> cell_value_per_face{connectivity};
            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            const size_t nb_values = number_of_values(cell_value_per_face);
            REQUIRE(cell_value_per_face.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerFace<int> cell_value_per_face_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_face_from_array, array));

            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per edge")
          {
            CellValuePerEdge<int> cell_value_per_edge{connectivity};
            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            const size_t nb_values = number_of_values(cell_value_per_edge);
            REQUIRE(cell_value_per_edge.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerEdge<int> cell_value_per_edge_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_edge_from_array, array));

            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per node")
          {
            CellValuePerNode<int> cell_value_per_node{connectivity};
            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            const size_t nb_values = number_of_values(cell_value_per_node);
            REQUIRE(cell_value_per_node.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerNode<int> cell_value_per_node_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_node_from_array, array));

            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }
          }
        }
      }
    }

    SECTION("2D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_2d = named_mesh.mesh();

          const Connectivity<2>& connectivity = mesh_2d->connectivity();

          SECTION("per cell")
          {
            NodeValuePerCell<int> node_value_per_cell{connectivity};
            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            const size_t nb_values = number_of_values(node_value_per_cell);
            REQUIRE(node_value_per_cell.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            NodeValuePerCell<int> node_value_per_cell_from_array{connectivity, array};
            REQUIRE(check_same_memory(node_value_per_cell_from_array, array));

            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));

            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerCell<int> face_value_per_cell{connectivity};
            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));

            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per face")
          {
            CellValuePerFace<int> cell_value_per_face{connectivity};
            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            const size_t nb_values = number_of_values(cell_value_per_face);
            REQUIRE(cell_value_per_face.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerFace<int> cell_value_per_face_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_face_from_array, array));

            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }

            NodeValuePerFace<int> node_value_per_face{connectivity};
            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));

            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per edge")
          {
            CellValuePerEdge<int> cell_value_per_edge{connectivity};
            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            const size_t nb_values = number_of_values(cell_value_per_edge);
            REQUIRE(cell_value_per_edge.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerEdge<int> cell_value_per_edge_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_edge_from_array, array));

            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }

            NodeValuePerEdge<int> node_value_per_edge{connectivity};
            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));

            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per node")
          {
            EdgeValuePerNode<int> edge_value_per_node{connectivity};
            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            const size_t nb_values = number_of_values(edge_value_per_node);
            REQUIRE(edge_value_per_node.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            EdgeValuePerNode<int> cell_value_per_node_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_node_from_array, array));

            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerNode<int> face_value_per_node{connectivity};
            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));

            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }

            CellValuePerNode<int> cell_value_per_node{connectivity};
            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));

            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }
          }
        }
      }
    }

    SECTION("3D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_3d = named_mesh.mesh();

          const Connectivity<3>& connectivity = mesh_3d->connectivity();

          SECTION("per cell")
          {
            NodeValuePerCell<int> node_value_per_cell{connectivity};
            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            const size_t nb_values = number_of_values(node_value_per_cell);
            REQUIRE(node_value_per_cell.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            NodeValuePerCell<int> node_value_per_cell_from_array{connectivity, array};
            REQUIRE(check_same_memory(node_value_per_cell_from_array, array));

            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));

            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerCell<int> face_value_per_cell{connectivity};
            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));

            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
            {
              bool is_correct = true;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per face")
          {
            CellValuePerFace<int> cell_value_per_face{connectivity};
            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            const size_t nb_values = number_of_values(cell_value_per_face);
            REQUIRE(cell_value_per_face.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerFace<int> cell_value_per_face_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_face_from_array, array));

            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }

            EdgeValuePerFace<int> edge_value_per_face{connectivity};
            REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));

            auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }

            NodeValuePerFace<int> node_value_per_face{connectivity};
            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));

            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
            {
              bool is_correct = true;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per edge")
          {
            CellValuePerEdge<int> cell_value_per_edge{connectivity};
            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            const size_t nb_values = number_of_values(cell_value_per_edge);
            REQUIRE(cell_value_per_edge.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            CellValuePerEdge<int> cell_value_per_edge_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_edge_from_array, array));

            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerEdge<int> face_value_per_edge{connectivity};
            REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));

            auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }

            NodeValuePerEdge<int> node_value_per_edge{connectivity};
            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));

            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
            {
              bool is_correct = true;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
              }
              REQUIRE(is_correct);
            }
          }

          SECTION("per node")
          {
            EdgeValuePerNode<int> edge_value_per_node{connectivity};
            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            const size_t nb_values = number_of_values(edge_value_per_node);
            REQUIRE(edge_value_per_node.numberOfValues() == nb_values);

            Array<int> array{nb_values};
            EdgeValuePerNode<int> cell_value_per_node_from_array{connectivity, array};
            REQUIRE(check_same_memory(cell_value_per_node_from_array, array));

            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }

            FaceValuePerNode<int> face_value_per_node{connectivity};
            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));

            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }

            CellValuePerNode<int> cell_value_per_node{connectivity};
            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));

            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
            {
              bool is_correct = true;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
              }
              REQUIRE(is_correct);
            }
          }
        }
      }
    }
  }

  SECTION("access functions")
  {
    SECTION("1D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_1d = named_mesh.mesh();

          const Connectivity<1>& connectivity = mesh_1d->connectivity();

          SECTION("per cell")
          {
            {
              NodeValuePerCell<int> node_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < node_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_cell.numberOfValues());
            }

            {
              EdgeValuePerCell<int> edge_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < edge_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_cell.numberOfValues());
            }

            {
              FaceValuePerCell<int> face_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < face_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_cell.numberOfValues());
            }
          }

          SECTION("per face")
          {
            CellValuePerFace<int> cell_value_per_face{connectivity};

            bool is_valid  = true;
            size_t counter = 0;
            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
              for (size_t i = 0; i < cell_value_per_face.numberOfSubValues(face_id); ++i) {
                is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face.itemArray(face_id)[i]));
                is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face(face_id, i)));
                counter++;
              }
            }
            REQUIRE(is_valid);
            REQUIRE(counter == cell_value_per_face.numberOfValues());
          }

          SECTION("per edge")
          {
            CellValuePerEdge<int> cell_value_per_edge{connectivity};

            bool is_valid  = true;
            size_t counter = 0;
            for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
              for (size_t i = 0; i < cell_value_per_edge.numberOfSubValues(edge_id); ++i) {
                is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge.itemArray(edge_id)[i]));
                is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge(edge_id, i)));
                counter++;
              }
            }
            REQUIRE(is_valid);
            REQUIRE(counter == cell_value_per_edge.numberOfValues());
          }

          SECTION("per node")
          {
            CellValuePerNode<int> cell_value_per_node{connectivity};

            bool is_valid  = true;
            size_t counter = 0;
            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
              for (size_t i = 0; i < cell_value_per_node.numberOfSubValues(node_id); ++i) {
                is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node.itemArray(node_id)[i]));
                is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node(node_id, i)));
                counter++;
              }
            }
            REQUIRE(is_valid);
            REQUIRE(counter == cell_value_per_node.numberOfValues());
          }
        }
      }
    }

    SECTION("2D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_2d = named_mesh.mesh();

          const Connectivity<2>& connectivity = mesh_2d->connectivity();

          SECTION("per cell")
          {
            {
              NodeValuePerCell<int> node_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < node_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_cell.numberOfValues());
            }

            {
              EdgeValuePerCell<int> edge_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < edge_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_cell.numberOfValues());
            }

            {
              FaceValuePerCell<int> face_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < face_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_cell.numberOfValues());
            }
          }

          SECTION("per face")
          {
            {
              NodeValuePerFace<int> node_value_per_face{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                for (size_t i = 0; i < node_value_per_face.numberOfSubValues(face_id); ++i) {
                  is_valid &= (&(node_value_per_face[face_id][i]) == &(node_value_per_face.itemArray(face_id)[i]));
                  is_valid &= (&(node_value_per_face[face_id][i]) == &(node_value_per_face(face_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_face.numberOfValues());
            }

            {
              CellValuePerFace<int> cell_value_per_face{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                for (size_t i = 0; i < cell_value_per_face.numberOfSubValues(face_id); ++i) {
                  is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face.itemArray(face_id)[i]));
                  is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face(face_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_face.numberOfValues());
            }
          }

          SECTION("per edge")
          {
            {
              NodeValuePerEdge<int> node_value_per_edge{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                for (size_t i = 0; i < node_value_per_edge.numberOfSubValues(edge_id); ++i) {
                  is_valid &= (&(node_value_per_edge[edge_id][i]) == &(node_value_per_edge.itemArray(edge_id)[i]));
                  is_valid &= (&(node_value_per_edge[edge_id][i]) == &(node_value_per_edge(edge_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_edge.numberOfValues());
            }

            {
              CellValuePerEdge<int> cell_value_per_edge{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                for (size_t i = 0; i < cell_value_per_edge.numberOfSubValues(edge_id); ++i) {
                  is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge.itemArray(edge_id)[i]));
                  is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge(edge_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_edge.numberOfValues());
            }
          }

          SECTION("per node")
          {
            {
              EdgeValuePerNode<int> edge_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < edge_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(edge_value_per_node[node_id][i]) == &(edge_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(edge_value_per_node[node_id][i]) == &(edge_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_node.numberOfValues());
            }

            {
              FaceValuePerNode<int> face_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < face_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(face_value_per_node[node_id][i]) == &(face_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(face_value_per_node[node_id][i]) == &(face_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_node.numberOfValues());
            }

            {
              CellValuePerNode<int> cell_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < cell_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_node.numberOfValues());
            }
          }
        }
      }
    }

    SECTION("3D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_3d = named_mesh.mesh();

          const Connectivity<3>& connectivity = mesh_3d->connectivity();

          SECTION("per cell")
          {
            {
              NodeValuePerCell<int> node_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < node_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(node_value_per_cell[cell_id][i]) == &(node_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_cell.numberOfValues());
            }

            {
              EdgeValuePerCell<int> edge_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < edge_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(edge_value_per_cell[cell_id][i]) == &(edge_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_cell.numberOfValues());
            }

            {
              FaceValuePerCell<int> face_value_per_cell{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
                for (size_t i = 0; i < face_value_per_cell.numberOfSubValues(cell_id); ++i) {
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell.itemArray(cell_id)[i]));
                  is_valid &= (&(face_value_per_cell[cell_id][i]) == &(face_value_per_cell(cell_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_cell.numberOfValues());
            }
          }

          SECTION("per face")
          {
            {
              NodeValuePerFace<int> node_value_per_face{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                for (size_t i = 0; i < node_value_per_face.numberOfSubValues(face_id); ++i) {
                  is_valid &= (&(node_value_per_face[face_id][i]) == &(node_value_per_face.itemArray(face_id)[i]));
                  is_valid &= (&(node_value_per_face[face_id][i]) == &(node_value_per_face(face_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_face.numberOfValues());
            }

            {
              EdgeValuePerFace<int> edge_value_per_face{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                for (size_t i = 0; i < edge_value_per_face.numberOfSubValues(face_id); ++i) {
                  is_valid &= (&(edge_value_per_face[face_id][i]) == &(edge_value_per_face.itemArray(face_id)[i]));
                  is_valid &= (&(edge_value_per_face[face_id][i]) == &(edge_value_per_face(face_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_face.numberOfValues());
            }

            {
              CellValuePerFace<int> cell_value_per_face{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
                for (size_t i = 0; i < cell_value_per_face.numberOfSubValues(face_id); ++i) {
                  is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face.itemArray(face_id)[i]));
                  is_valid &= (&(cell_value_per_face[face_id][i]) == &(cell_value_per_face(face_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_face.numberOfValues());
            }
          }

          SECTION("per edge")
          {
            {
              NodeValuePerEdge<int> node_value_per_edge{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                for (size_t i = 0; i < node_value_per_edge.numberOfSubValues(edge_id); ++i) {
                  is_valid &= (&(node_value_per_edge[edge_id][i]) == &(node_value_per_edge.itemArray(edge_id)[i]));
                  is_valid &= (&(node_value_per_edge[edge_id][i]) == &(node_value_per_edge(edge_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == node_value_per_edge.numberOfValues());
            }

            {
              FaceValuePerEdge<int> face_value_per_edge{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                for (size_t i = 0; i < face_value_per_edge.numberOfSubValues(edge_id); ++i) {
                  is_valid &= (&(face_value_per_edge[edge_id][i]) == &(face_value_per_edge.itemArray(edge_id)[i]));
                  is_valid &= (&(face_value_per_edge[edge_id][i]) == &(face_value_per_edge(edge_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_edge.numberOfValues());
            }

            {
              CellValuePerEdge<int> cell_value_per_edge{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
                for (size_t i = 0; i < cell_value_per_edge.numberOfSubValues(edge_id); ++i) {
                  is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge.itemArray(edge_id)[i]));
                  is_valid &= (&(cell_value_per_edge[edge_id][i]) == &(cell_value_per_edge(edge_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_edge.numberOfValues());
            }
          }

          SECTION("per node")
          {
            {
              EdgeValuePerNode<int> edge_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < edge_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(edge_value_per_node[node_id][i]) == &(edge_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(edge_value_per_node[node_id][i]) == &(edge_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == edge_value_per_node.numberOfValues());
            }

            {
              FaceValuePerNode<int> face_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < face_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(face_value_per_node[node_id][i]) == &(face_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(face_value_per_node[node_id][i]) == &(face_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == face_value_per_node.numberOfValues());
            }

            {
              CellValuePerNode<int> cell_value_per_node{connectivity};

              bool is_valid  = true;
              size_t counter = 0;
              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
                for (size_t i = 0; i < cell_value_per_node.numberOfSubValues(node_id); ++i) {
                  is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node.itemArray(node_id)[i]));
                  is_valid &= (&(cell_value_per_node[node_id][i]) == &(cell_value_per_node(node_id, i)));
                  counter++;
                }
              }
              REQUIRE(is_valid);
              REQUIRE(counter == cell_value_per_node.numberOfValues());
            }
          }
        }
      }
    }
  }

  SECTION("array view")
  {
    SECTION("1D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_1d = named_mesh.mesh();

          const Connectivity<1>& connectivity = mesh_1d->connectivity();

          EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
          {
            size_t value = 0;
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
                edge_values_per_cell(cell_id, i_edge) = value++;
              }
            }
          }
          {
            bool is_same = true;
            for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
              is_same &= (edge_values_per_cell[i] == i);
            }
            REQUIRE(is_same);
          }

          for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
            edge_values_per_cell[i] = i * i + 1;
          }
          {
            bool is_same = true;
            size_t i     = 0;
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
                is_same &= (edge_values_per_cell(cell_id, i_edge) == i * i + 1);
              }
            }
            REQUIRE(is_same);
          }
        }
      }
    }

    SECTION("2D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_2d = named_mesh.mesh();

          const Connectivity<2>& connectivity = mesh_2d->connectivity();

          CellValuePerFace<size_t> cell_values_per_face{connectivity};
          {
            size_t value = 0;
            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
                cell_values_per_face(face_id, i_cell) = value++;
              }
            }
          }
          {
            bool is_same = true;
            for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
              is_same &= (cell_values_per_face[i] == i);
            }
            REQUIRE(is_same);
          }
          for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
            cell_values_per_face[i] = 3 * i + 1;
          }
          {
            bool is_same = true;
            size_t i     = 0;
            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
                is_same &= (cell_values_per_face(face_id, i_cell) == 3 * i + 1);
              }
            }
            REQUIRE(is_same);
          }
        }
      }
    }

    SECTION("3D")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_3d = named_mesh.mesh();

          const Connectivity<3>& connectivity = mesh_3d->connectivity();

          FaceValuePerNode<size_t> face_values_per_node{connectivity};
          {
            size_t value = 0;
            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
                face_values_per_node.itemArray(node_id)[i_face] = value++;
              }
            }
          }
          {
            bool is_same = true;
            for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
              is_same &= (face_values_per_node[i] == i);
            }
            REQUIRE(is_same);
          }

          for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
            face_values_per_node[i] = 3 + i * i;
          }
          {
            bool is_same = true;
            size_t i     = 0;
            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
                is_same &= (face_values_per_node.itemArray(node_id)[i_face] == 3 + i * i);
              }
            }
            REQUIRE(is_same);
          }
        }
      }
    }
  }

  SECTION("copy")
  {
    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();

    for (auto named_mesh : mesh_list) {
      SECTION(named_mesh.name())
      {
        auto mesh_3d = named_mesh.mesh();

        const Connectivity<3>& connectivity = mesh_3d->connectivity();

        SECTION("classic")
        {
          NodeValuePerCell<size_t> node_value_per_cell{connectivity};

          {
            size_t value = 0;
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
                node_value_per_cell.itemArray(cell_id)[i_node] = value++;
              }
            }
          }

          NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);

          {
            bool is_same = true;
            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
            }

            REQUIRE(is_same);
          }

          {
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
                node_value_per_cell.itemArray(cell_id)[i_node] = i_node;
              }
            }
          }

          {
            bool is_same = true;
            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
            }

            REQUIRE(not is_same);
          }
        }

        SECTION("from weak")
        {
          WeakNodeValuePerCell<size_t> node_value_per_cell{connectivity};

          {
            size_t value = 0;
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
                node_value_per_cell.itemArray(cell_id)[i_node] = value++;
              }
            }
          }

          WeakNodeValuePerCell<const size_t> node_const_value_per_cell = node_value_per_cell;
          NodeValuePerCell<size_t> copy_node_value_per_cell            = copy(node_const_value_per_cell);

          {
            bool is_same = true;
            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
            }

            REQUIRE(is_same);
          }

          {
            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
                node_value_per_cell.itemArray(cell_id)[i_node] = i_node;
              }
            }
          }

          {
            bool is_same = true;
            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
            }

            REQUIRE(not is_same);
          }
        }
      }
    }
  }

  SECTION("WeakSubItemValuePerItem")
  {
    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();

    for (auto named_mesh : mesh_list) {
      SECTION(named_mesh.name())
      {
        auto mesh_2d = named_mesh.mesh();

        const Connectivity<2>& connectivity = mesh_2d->connectivity();

        WeakFaceValuePerCell<int> weak_face_value_per_cell{connectivity};

        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
          weak_face_value_per_cell[i] = i;
        }

        FaceValuePerCell<const int> face_value_per_cell{weak_face_value_per_cell};

        REQUIRE(face_value_per_cell.connectivity_ptr() == weak_face_value_per_cell.connectivity_ptr());

        bool is_same = true;
        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
          is_same &= (face_value_per_cell[i] == weak_face_value_per_cell[i]);
        }
        REQUIRE(is_same);
      }
    }
  }

#ifndef NDEBUG
  SECTION("error")
  {
    SECTION("checking for build SubItemValuePerItem")
    {
      CellValuePerNode<int> cell_value_per_node;
      REQUIRE_THROWS_WITH(cell_value_per_node[0], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node.itemArray(NodeId{0}), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node[NodeId{0}], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node(NodeId{0}, 0), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node.numberOfValues(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node.numberOfItems(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_node.numberOfSubValues(NodeId{0}), "SubItemValuePerItem is not built");

      FaceValuePerCell<int> face_value_per_cell;
      REQUIRE_THROWS_WITH(face_value_per_cell[0], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell.itemArray(CellId{0}), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell[CellId{0}], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell(CellId{0}, 0), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell.numberOfValues(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell.numberOfItems(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(face_value_per_cell.numberOfSubValues(CellId{0}), "SubItemValuePerItem is not built");

      CellValuePerEdge<int> cell_value_per_edge;
      REQUIRE_THROWS_WITH(cell_value_per_edge[0], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge.itemArray(EdgeId{0}), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge[EdgeId{0}], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge(EdgeId{0}, 0), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge.numberOfValues(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge.numberOfItems(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(cell_value_per_edge.numberOfSubValues(EdgeId{0}), "SubItemValuePerItem is not built");

      NodeValuePerFace<int> node_value_per_face;
      REQUIRE_THROWS_WITH(node_value_per_face[0], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face.itemArray(FaceId{0}), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face[FaceId{0}], "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face(FaceId{0}, 0), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face.numberOfValues(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face.numberOfItems(), "SubItemValuePerItem is not built");
      REQUIRE_THROWS_WITH(node_value_per_face.numberOfSubValues(FaceId{0}), "SubItemValuePerItem is not built");
    }

    SECTION("checking invalid array size in constructor")
    {
      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();

      const Connectivity<3>& connectivity = mesh_3d->connectivity();

      {
        Array<int> array{connectivity.getMatrix(ItemType::node, ItemType::cell).numberOfValues() + 3};
        REQUIRE_THROWS_WITH(CellValuePerNode<int>(connectivity, array), "invalid size of provided values");
      }

      {
        Array<int> array{connectivity.getMatrix(ItemType::face, ItemType::edge).numberOfValues() + 3};
        REQUIRE_THROWS_WITH(EdgeValuePerFace<int>(connectivity, array), "invalid size of provided values");
      }
    }

    SECTION("checking for bounds violation")
    {
      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();

      for (auto named_mesh : mesh_list) {
        SECTION(named_mesh.name())
        {
          auto mesh_3d = named_mesh.mesh();

          const Connectivity<3>& connectivity = mesh_3d->connectivity();

          CellValuePerFace<int> cell_value_per_face{connectivity};
          {
            FaceId invalid_face_id = connectivity.numberOfFaces();
            REQUIRE_THROWS_WITH(cell_value_per_face(invalid_face_id, 0), "invalid item_id");
            REQUIRE_THROWS_WITH(cell_value_per_face[invalid_face_id], "invalid item_id");
          }
          if (connectivity.numberOfFaces() > 0) {
            FaceId face_id          = 0;
            const auto& cell_values = cell_value_per_face.itemArray(face_id);
            REQUIRE_THROWS_WITH(cell_value_per_face(face_id, cell_values.size()), "invalid index");
            REQUIRE_THROWS_WITH(cell_values[cell_values.size()], "invalid index");
            REQUIRE_THROWS_WITH(cell_value_per_face.itemArray(face_id)[cell_values.size()] = 2, "invalid index");
            REQUIRE_THROWS_WITH(cell_value_per_face[face_id][cell_values.size()] = 2, "invalid index");
          }

          FaceValuePerNode<int> face_value_per_node{connectivity};
          {
            NodeId invalid_node_id = connectivity.numberOfNodes();
            REQUIRE_THROWS_WITH(face_value_per_node(invalid_node_id, 0), "invalid item_id");
            REQUIRE_THROWS_WITH(face_value_per_node[invalid_node_id], "invalid item_id");
          }
          if (connectivity.numberOfNodes() > 0) {
            NodeId node_id          = 0;
            const auto& face_values = face_value_per_node.itemArray(node_id);
            REQUIRE_THROWS_WITH(face_value_per_node(node_id, face_values.size()), "invalid index");
            REQUIRE_THROWS_WITH(face_values[face_values.size()], "invalid index");
            REQUIRE_THROWS_WITH(face_value_per_node.itemArray(node_id)[face_values.size()] = 2, "invalid index");
            REQUIRE_THROWS_WITH(face_value_per_node[node_id][face_values.size()] = 2, "invalid index");
          }

          EdgeValuePerCell<int> edge_value_per_cell{connectivity};
          {
            CellId invalid_cell_id = connectivity.numberOfCells();
            REQUIRE_THROWS_WITH(edge_value_per_cell(invalid_cell_id, 0), "invalid item_id");
            REQUIRE_THROWS_WITH(edge_value_per_cell[invalid_cell_id], "invalid item_id");
          }
          if (connectivity.numberOfCells() > 0) {
            CellId cell_id          = 0;
            const auto& edge_values = edge_value_per_cell.itemArray(cell_id);
            REQUIRE_THROWS_WITH(edge_value_per_cell(cell_id, edge_values.size()), "invalid index");
            REQUIRE_THROWS_WITH(edge_values[edge_values.size()], "invalid index");
            REQUIRE_THROWS_WITH(edge_value_per_cell.itemArray(cell_id)[edge_values.size()] = 2, "invalid index");
            REQUIRE_THROWS_WITH(edge_value_per_cell[cell_id][edge_values.size()] = 2, "invalid index");
          }

          NodeValuePerEdge<int> node_value_per_edge{connectivity};
          {
            EdgeId invalid_edge_id = connectivity.numberOfEdges();
            REQUIRE_THROWS_WITH(node_value_per_edge(invalid_edge_id, 0), "invalid item_id");
            REQUIRE_THROWS_WITH(node_value_per_edge[invalid_edge_id], "invalid item_id");
          }
          if (connectivity.numberOfEdges() > 0) {
            EdgeId edge_id          = 0;
            const auto& node_values = node_value_per_edge.itemArray(edge_id);
            REQUIRE_THROWS_WITH(node_value_per_edge(edge_id, node_values.size()), "invalid index");
            REQUIRE_THROWS_WITH(node_values[node_values.size()], "invalid index");
            REQUIRE_THROWS_WITH(node_value_per_edge.itemArray(edge_id)[node_values.size()] = 2, "invalid index");
            REQUIRE_THROWS_WITH(node_value_per_edge[edge_id][node_values.size()] = 2, "invalid index");
          }
        }
      }
    }
  }
#endif   // NDEBUG
}