#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;
    };

    SECTION("1D")
    {
      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
      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());
        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));

        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.itemValues(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.itemValues(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());
        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));

        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());
        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));

        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());
        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("2D")
    {
      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
      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());
        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));

        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());
        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));

        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());
        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));

        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());
        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));

        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")
    {
      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
      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());
        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));

        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());
        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));

        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());
        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));

        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());
        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));

        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("array view")
  {
    SECTION("1D")
    {
      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
      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")
    {
      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
      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")
    {
      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
      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.itemValues(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.itemValues(node_id)[i_face] == 3 + i * i);
          }
        }
        REQUIRE(is_same);
      }
    }
  }

  SECTION("copy")
  {
    const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
    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.itemValues(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.itemValues(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.itemValues(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.itemValues(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")
  {
    const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
    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_AS(cell_value_per_node[0], AssertError);
      REQUIRE_THROWS_AS(cell_value_per_node.itemValues(NodeId{0}), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_node(NodeId{0}, 0), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_node.numberOfValues(), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_node.numberOfItems(), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_node.numberOfSubValues(NodeId{0}), AssertError);

      FaceValuePerCell<int> face_value_per_cell;
      REQUIRE_THROWS_AS(face_value_per_cell[0], AssertError);
      REQUIRE_THROWS_AS(face_value_per_cell.itemValues(CellId{0}), AssertError);
      REQUIRE_THROWS_AS(face_value_per_cell(CellId{0}, 0), AssertError);
      REQUIRE_THROWS_AS(face_value_per_cell.numberOfValues(), AssertError);
      REQUIRE_THROWS_AS(face_value_per_cell.numberOfItems(), AssertError);
      REQUIRE_THROWS_AS(face_value_per_cell.numberOfSubValues(CellId{0}), AssertError);

      CellValuePerEdge<int> cell_value_per_edge;
      REQUIRE_THROWS_AS(cell_value_per_edge[0], AssertError);
      REQUIRE_THROWS_AS(cell_value_per_edge.itemValues(EdgeId{0}), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_edge(EdgeId{0}, 0), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_edge.numberOfValues(), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_edge.numberOfItems(), AssertError);
      REQUIRE_THROWS_AS(cell_value_per_edge.numberOfSubValues(EdgeId{0}), AssertError);

      NodeValuePerFace<int> node_value_per_face;
      REQUIRE_THROWS_AS(node_value_per_face[0], AssertError);
      REQUIRE_THROWS_AS(node_value_per_face.itemValues(FaceId{0}), AssertError);
      REQUIRE_THROWS_AS(node_value_per_face(FaceId{0}, 0), AssertError);
      REQUIRE_THROWS_AS(node_value_per_face.numberOfValues(), AssertError);
      REQUIRE_THROWS_AS(node_value_per_face.numberOfItems(), AssertError);
      REQUIRE_THROWS_AS(node_value_per_face.numberOfSubValues(FaceId{0}), AssertError);
    }

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

      CellValuePerFace<int> cell_value_per_face{connectivity};
      {
        FaceId invalid_face_id = connectivity.numberOfFaces();
        REQUIRE_THROWS_AS(cell_value_per_face(invalid_face_id, 0), AssertError);
      }
      if (connectivity.numberOfFaces() > 0) {
        FaceId face_id          = 0;
        const auto& cell_values = cell_value_per_face.itemValues(face_id);
        REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError);
        REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError);
        REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError);
      }

      FaceValuePerNode<int> face_value_per_node{connectivity};
      {
        NodeId invalid_node_id = connectivity.numberOfNodes();
        REQUIRE_THROWS_AS(face_value_per_node(invalid_node_id, 0), AssertError);
      }
      if (connectivity.numberOfNodes() > 0) {
        NodeId node_id          = 0;
        const auto& face_values = face_value_per_node.itemValues(node_id);
        REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError);
        REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError);
        REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError);
      }

      EdgeValuePerCell<int> edge_value_per_cell{connectivity};
      {
        CellId invalid_cell_id = connectivity.numberOfCells();
        REQUIRE_THROWS_AS(edge_value_per_cell(invalid_cell_id, 0), AssertError);
      }
      if (connectivity.numberOfCells() > 0) {
        CellId cell_id          = 0;
        const auto& edge_values = edge_value_per_cell.itemValues(cell_id);
        REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError);
        REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError);
        REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError);
      }

      NodeValuePerEdge<int> node_value_per_edge{connectivity};
      {
        EdgeId invalid_edge_id = connectivity.numberOfEdges();
        REQUIRE_THROWS_AS(node_value_per_edge(invalid_edge_id, 0), AssertError);
      }
      if (connectivity.numberOfEdges() > 0) {
        EdgeId edge_id          = 0;
        const auto& node_values = node_value_per_edge.itemValues(edge_id);
        REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError);
        REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError);
        REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError);
      }
    }
  }
#endif   // NDEBUG
}