inline typename DefaultLocalAssemblerForOperatorsOnSurfacesUtilities<
    BasisFunctionType>::CoordinateType
DefaultLocalAssemblerForOperatorsOnSurfacesUtilities<BasisFunctionType>::
    elementSizeSquared(int elementIndex,
                       const RawGridGeometry<CoordinateType> &rawGeometry) {
  // This implementation could be optimised
  CoordinateType maxEdgeLengthSquared = 0.;
  const arma::Mat<int> &cornerIndices = rawGeometry.elementCornerIndices();
  const arma::Mat<CoordinateType> &vertices = rawGeometry.vertices();
  arma::Col<CoordinateType> edge;
  if (cornerIndices(cornerIndices.n_rows - 1, elementIndex) == -1) {
    // Triangular element
    const int cornerCount = 3;
    for (int i = 0; i < cornerCount; ++i) {
      edge = vertices.col(cornerIndices((i + 1) % cornerCount, elementIndex)) -
             vertices.col(cornerIndices(i, elementIndex));
      CoordinateType edgeLengthSquared = arma::dot(edge, edge);
      maxEdgeLengthSquared = std::max(maxEdgeLengthSquared, edgeLengthSquared);
    }
  } else {
    // Quadrilateral element. We assume it is convex.
    edge = vertices.col(cornerIndices(2, elementIndex)) -
           vertices.col(cornerIndices(0, elementIndex));
    maxEdgeLengthSquared = arma::dot(edge, edge);
    edge = vertices.col(cornerIndices(3, elementIndex)) -
           vertices.col(cornerIndices(1, elementIndex));
    CoordinateType edgeLengthSquared = arma::dot(edge, edge);
    maxEdgeLengthSquared = std::max(maxEdgeLengthSquared, edgeLengthSquared);
  }
  return maxEdgeLengthSquared;
}
void DefaultLocalAssemblerForOperatorsOnSurfacesUtilities<BasisFunctionType>::
    checkConsistencyOfGeometryAndShapesets(
        const RawGridGeometry<CoordinateType> &rawGeometry,
        const std::vector<const Shapeset<BasisFunctionType> *> &shapesets) {
  if (rawGeometry.vertices().n_rows != 3)
    throw std::invalid_argument(
        "DefaultLocalAssemblerForOperatorsOnSurfacesUtilities::"
        "checkConsistencyOfGeometryAndShapesets(): "
        "vertex coordinates must be three-dimensional");
  const size_t elementCount = rawGeometry.elementCornerIndices().n_cols;
  if (rawGeometry.elementCornerIndices().n_rows < 3 ||
      4 < rawGeometry.elementCornerIndices().n_rows)
    throw std::invalid_argument(
        "DefaultLocalAssemblerForOperatorsOnSurfacesUtilities::"
        "checkConsistencyOfGeometryAndShapesets(): "
        "Elements must have either 3 or 4 corners");
  if (!rawGeometry.auxData().is_empty() &&
      rawGeometry.auxData().n_cols != elementCount)
    throw std::invalid_argument(
        "DefaultLocalAssemblerForOperatorsOnSurfacesUtilities::"
        "checkConsistencyOfGeometryAndShapesets(): "
        "number of columns of auxData must match that of "
        "elementCornerIndices");
  if (shapesets.size() != elementCount)
    throw std::invalid_argument(
        "DefaultLocalAssemblerForOperatorsOnSurfacesUtilities::"
        "checkConsistencyOfGeometryAndShapesets(): "
        "size of shapesets must match the number of columns of "
        "elementCornerIndices");
}
inline Vector<typename DefaultLocalAssemblerForOperatorsOnSurfacesUtilities<
    BasisFunctionType>::CoordinateType>
DefaultLocalAssemblerForOperatorsOnSurfacesUtilities<BasisFunctionType>::
    elementCenter(int elementIndex,
                  const RawGridGeometry<CoordinateType> &rawGeometry) {
  const Matrix<int> &cornerIndices = rawGeometry.elementCornerIndices();
  const Matrix<CoordinateType> &vertices = rawGeometry.vertices();
  const int maxCornerCount = cornerIndices.rows();
  // each element has at least one corner
  Vector<CoordinateType> center(vertices.col(cornerIndices(0, elementIndex)));
  int i = 1;
  for (; i < maxCornerCount; ++i) {
    int cornerIndex = cornerIndices(i, elementIndex);
    if (cornerIndex == -1)
      break;
    center += vertices.col(cornerIndex);
  }
  // now i contains the number of corners of the specified element
  center /= i;
  return center;
}