/***************************************************************************
                          MeshBenchmarks.h  -  description
                             -------------------
    begin                : Nov 21, 2017
    copyright            : (C) 2017 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

// Implemented by: Jakub Klinkovsky

#pragma once

#include <TNL/Meshes/Grid.h>
#include <TNL/Meshes/Mesh.h>
#include <TNL/Meshes/Geometry/getEntityCenter.h>
#include <TNL/Meshes/Geometry/getEntityMeasure.h>
#include <TNL/Meshes/TypeResolver/resolveMeshType.h>
#include <TNL/Pointers/DevicePointer.h>
#include <TNL/Algorithms/ParallelFor.h>
#include <TNL/Algorithms/staticFor.h>
#include <TNL/Benchmarks/Benchmarks.h>

#ifdef HAVE_CUDA
#include <cuda_profiler_api.h>
#endif

#include "MeshConfigs.h"
#include "MemoryInfo.h"

using namespace TNL;
using namespace TNL::Meshes;
using namespace TNL::Benchmarks;

template< typename Device >
bool checkDevice( const Config::ParameterContainer& parameters )
{
   const String device = parameters.getParameter< String >( "devices" );
   if( device == "all" )
      return true;
   if( std::is_same< Device, Devices::Host >::value && device == "host" )
      return true;
   if( std::is_same< Device, Devices::Cuda >::value && device == "cuda" )
      return true;
   return false;
}

template< typename Real >
__cuda_callable__
Real
getSimplexMeasure( const TNL::Containers::StaticVector< 1, Real > (& points) [2] )
{
    return TNL::l2Norm( points[0] - points[1] );
}

template< typename Real >
__cuda_callable__
Real
getSimplexMeasure( const TNL::Containers::StaticVector< 2, Real > (& points) [3] )
{
    return getTriangleArea( points[0], points[1] );
}

template< typename Real >
__cuda_callable__
Real
getSimplexMeasure( const TNL::Containers::StaticVector< 3, Real > (& points) [4] )
{
    return getTetrahedronVolume( points[0], points[1], points[2] );
}

template< typename Mesh >
struct MeshBenchmarks
{
   static_assert( std::is_same< typename Mesh::DeviceType, Devices::Host >::value, "The mesh should be loaded on the host." );

   static bool run( Benchmark<> & benchmark, const Config::ParameterContainer & parameters )
   {
      const String & meshFile = parameters.getParameter< String >( "mesh-file" );

      Logging::MetadataColumns metadataColumns = {
         {"mesh-file", meshFile},
         {"config", Mesh::Config::getConfigType()},
         {"topology", getType< typename Mesh::Config::CellTopology >().replace("Topologies::", "")},
         {"space dim", std::to_string(Mesh::Config::spaceDimension)},
         {"real", getType< typename Mesh::RealType >()},
         {"gid_t", getType< typename Mesh::GlobalIndexType >()},
         {"lid_t", getType< typename Mesh::LocalIndexType >()},
      };
      benchmark.setMetadataColumns( metadataColumns );

      Mesh mesh;
      if( ! loadMesh( mesh, meshFile ) ) {
         benchmark.addErrorMessage( "Failed to load mesh from file '" + meshFile );
         return false;
      }

      // collect memory usage
      // TODO: pass result to the dispatchAlgorithms to append the timings
//      MemoryBenchmarkResult meminfo = testMemoryUsage( parameters, mesh );
//      auto noop = [](){};
//      benchmark.time< TNL::Devices::Host >( "CPU", noop, meminfo );

#ifdef HAVE_CUDA
      cudaProfilerStart();
#endif

      dispatchAlgorithms( benchmark, parameters, mesh );

#ifdef HAVE_CUDA
      cudaProfilerStop();
#endif

      return true;
   }

   static void dispatchAlgorithms( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const Mesh & mesh )
   {
      Algorithms::staticFor< int, 1, Mesh::getMeshDimension() + 1 >(
            [&] ( auto dim ) {
               CentersDispatch< dim >::exec( benchmark, parameters, mesh );
            }
         );
      Algorithms::staticFor< int, 1, Mesh::getMeshDimension() + 1 >(
            [&] ( auto dim ) {
               MeasuresDispatch< dim >::exec( benchmark, parameters, mesh );
            }
         );
      DualMeasuresDispatch::exec( benchmark, parameters, mesh );
      SpheresDispatch::exec( benchmark, parameters, mesh );
   }

   template< int EntityDimension >
   struct CentersDispatch
   {
      template< typename M,
                typename = typename std::enable_if< M::Config::subentityStorage( M::getMeshDimension(), 0 ) >::type >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
         benchmark.setOperation( String("Centers (d = ") + convertToString(EntityDimension) + ")" );
         benchmark_centers< EntityDimension, Devices::Host >( benchmark, parameters, mesh );
#ifdef HAVE_CUDA
         benchmark_centers< EntityDimension, Devices::Cuda >( benchmark, parameters, mesh );
#endif
      }

      template< typename M,
                typename = typename std::enable_if< ! M::Config::subentityStorage( M::getMeshDimension(), 0 ) >::type,
                typename = void >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
      }
   };

   template< int EntityDimension >
   struct MeasuresDispatch
   {
      template< typename M,
                typename = typename std::enable_if< M::Config::subentityStorage( M::getMeshDimension(), 0 ) >::type >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
         benchmark.setOperation( String("Measures (d = ") + convertToString(EntityDimension) + ")" );
         benchmark_measures< EntityDimension, Devices::Host >( benchmark, parameters, mesh );
#ifdef HAVE_CUDA
         benchmark_measures< EntityDimension, Devices::Cuda >( benchmark, parameters, mesh );
#endif
      }

      template< typename M,
                typename = typename std::enable_if< ! M::Config::subentityStorage( M::getMeshDimension(), 0 ) >::type,
                typename = void >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
      }
   };

   struct DualMeasuresDispatch
   {
      template< typename M,
                typename = typename std::enable_if<
                              std::is_same< typename M::Config::CellTopology, Topologies::Edge >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Triangle >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Tetrahedron >::value
                           >::type >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
         benchmark.setOperation( "Dual M" );
         benchmark_dual_measures< Devices::Host >( benchmark, parameters, mesh );
#ifdef HAVE_CUDA
         benchmark_dual_measures< Devices::Cuda >( benchmark, parameters, mesh );
#endif
      }

      template< typename M,
                typename = typename std::enable_if< !(
                              std::is_same< typename M::Config::CellTopology, Topologies::Edge >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Triangle >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Tetrahedron >::value
                           ) >::type,
                typename = void >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
      }
   };

   struct SpheresDispatch
   {
      template< typename M,
                typename = typename std::enable_if<
                              std::is_same< typename M::Config::CellTopology, Topologies::Triangle >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Tetrahedron >::value
                           >::type >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
         benchmark.setOperation( "Spheres" );
         benchmark_spheres< Devices::Host >( benchmark, parameters, mesh );
#ifdef HAVE_CUDA
         benchmark_spheres< Devices::Cuda >( benchmark, parameters, mesh );
#endif
      }

      template< typename M,
                typename = typename std::enable_if< !(
                              std::is_same< typename M::Config::CellTopology, Topologies::Triangle >::value ||
                              std::is_same< typename M::Config::CellTopology, Topologies::Tetrahedron >::value
                           ) >::type,
                typename = void >
      static void exec( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const M & mesh )
      {
      }
   };

   template< int EntityDimension, typename Device >
   static void benchmark_centers( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const Mesh & mesh_src )
   {
      using Index = typename Mesh::GlobalIndexType;
      using PointType = typename Mesh::PointType;
      using DeviceMesh = Meshes::Mesh< typename Mesh::Config, Device >;

      // skip benchmarks on devices which the user did not select
      if( ! checkDevice< Device >( parameters ) )
         return;

      const Index entitiesCount = mesh_src.template getEntitiesCount< EntityDimension >();

      const DeviceMesh mesh = mesh_src;
      Pointers::DevicePointer< const DeviceMesh > meshPointer( mesh );
      Containers::Array< PointType, Device, Index > centers;
      centers.setSize( PointType::getSize() * entitiesCount );

      auto kernel_centers = [] __cuda_callable__
         ( Index i,
           const DeviceMesh* mesh,
           PointType* array )
      {
         const auto& entity = mesh->template getEntity< EntityDimension >( i );
         array[ i ] = getEntityCenter( *mesh, entity );
      };

      auto reset = [&]() {
         centers.setValue( 0.0 );
      };

      auto benchmark_func = [&] () {
         Algorithms::ParallelFor< Device >::exec(
               (Index) 0, entitiesCount,
               kernel_centers,
               &meshPointer.template getData< Device >(),
               centers.getData() );
      };

      benchmark.time< Device >( reset,
                                (std::is_same< Device, Devices::Host >::value) ? "CPU" : "GPU",
                                benchmark_func );
   }

   template< int EntityDimension, typename Device >
   static void benchmark_measures( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const Mesh & mesh_src )
   {
      using Real = typename Mesh::RealType;
      using Index = typename Mesh::GlobalIndexType;
      using DeviceMesh = Meshes::Mesh< typename Mesh::Config, Device >;

      // skip benchmarks on devices which the user did not select
      if( ! checkDevice< Device >( parameters ) )
         return;

      const Index entitiesCount = mesh_src.template getEntitiesCount< EntityDimension >();

      const DeviceMesh mesh = mesh_src;
      Pointers::DevicePointer< const DeviceMesh > meshPointer( mesh );
      Containers::Array< Real, Device, Index > measures;
      measures.setSize( entitiesCount );

      auto kernel_measures = [] __cuda_callable__
         ( Index i,
           const DeviceMesh* mesh,
           Real* array )
      {
         const auto& entity = mesh->template getEntity< EntityDimension >( i );
         array[ i ] = getEntityMeasure( *mesh, entity );
      };

      auto reset = [&]() {
         measures.setValue( 0.0 );
      };

      auto benchmark_func = [&] () {
         Algorithms::ParallelFor< Device >::exec(
               (Index) 0, entitiesCount,
               kernel_measures,
               &meshPointer.template getData< Device >(),
               measures.getData() );
      };

      benchmark.time< Device >( reset,
                                (std::is_same< Device, Devices::Host >::value) ? "CPU" : "GPU",
                                benchmark_func );
   }

   template< typename Device >
   static void benchmark_dual_measures( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const Mesh & mesh_src )
   {
      static_assert( std::is_same< typename Mesh::Config::CellTopology, Topologies::Edge >::value ||
                     std::is_same< typename Mesh::Config::CellTopology, Topologies::Triangle >::value ||
                     std::is_same< typename Mesh::Config::CellTopology, Topologies::Tetrahedron >::value,
                     "The algorithm works only on simplices." );

      using Real = typename Mesh::RealType;
      using Index = typename Mesh::GlobalIndexType;
      using LocalIndex = typename Mesh::LocalIndexType;
      using PointType = typename Mesh::PointType;
      using DeviceMesh = Meshes::Mesh< typename Mesh::Config, Device >;

      // skip benchmarks on devices which the user did not select
      if( ! checkDevice< Device >( parameters ) )
         return;

      const Index entitiesCount = mesh_src.template getEntitiesCount< Mesh::getMeshDimension() >();

      const DeviceMesh mesh = mesh_src;
      Pointers::DevicePointer< const DeviceMesh > meshPointer( mesh );
      Containers::Array< Real, Device, Index > measures;
      measures.setSize( entitiesCount );

      auto kernel_measures = [] __cuda_callable__
         ( Index i,
           const DeviceMesh* mesh,
           Real* array )
      {
         const auto& entity = mesh->template getEntity< Mesh::getMeshDimension() >( i );
//         constexpr auto facesCount = Mesh::Cell::template getSubentitiesCount< Mesh::getMeshDimension() - 1 >();
         constexpr auto facesCount = Mesh::Cell::template SubentityTraits< Mesh::getMeshDimension() - 1 >::count;
         PointType centers[ facesCount ];

         for( LocalIndex f = 0; f < facesCount; f++ ) {
            const auto fid = entity.template getSubentityIndex< Mesh::getMeshDimension() - 1 >( f );
            const auto& face = mesh->template getEntity< Mesh::getMeshDimension() - 1 >( fid );
            const auto _cells = face.template getSuperentitiesCount< Mesh::getMeshDimension() >();
            if( _cells == 1 )
               // boundary face - take the face center instead of the neighbor
               centers[ f ] = getEntityCenter( *mesh, face );
            else for( LocalIndex c = 0; c < _cells; c++ ) {
               const auto cid = face.template getSuperentityIndex< Mesh::getMeshDimension() >( c );
               if( cid != i ) {
                  const auto& cell = mesh->template getEntity< Mesh::getMeshDimension() >( cid );
                  centers[ f ] = getEntityCenter( *mesh, cell );
               }
            }
         }

         array[ i ] = getSimplexMeasure( centers );
      };

      auto reset = [&]() {
         measures.setValue( 0.0 );
      };

      auto benchmark_func = [&] () {
         Algorithms::ParallelFor< Device >::exec(
               (Index) 0, entitiesCount,
               kernel_measures,
               &meshPointer.template getData< Device >(),
               measures.getData() );
      };

      benchmark.time< Device >( reset,
                                (std::is_same< Device, Devices::Host >::value) ? "CPU" : "GPU",
                                benchmark_func );
   }

   template< typename Device >
   static void benchmark_spheres( Benchmark<> & benchmark, const Config::ParameterContainer & parameters, const Mesh & mesh_src )
   {
      static_assert( std::is_same< typename Mesh::Config::CellTopology, Topologies::Triangle >::value ||
                     std::is_same< typename Mesh::Config::CellTopology, Topologies::Tetrahedron >::value,
                     "The algorithm works only on triangles and tetrahedrons." );

      using Real = typename Mesh::RealType;
      using Index = typename Mesh::GlobalIndexType;
      using LocalIndex = typename Mesh::LocalIndexType;
      using DeviceMesh = Meshes::Mesh< typename Mesh::Config, Device >;

      // skip benchmarks on devices which the user did not select
      if( ! checkDevice< Device >( parameters ) )
         return;

      const Index verticesCount = mesh_src.template getEntitiesCount< 0 >();
      const Index facesCount = mesh_src.template getEntitiesCount< Mesh::getMeshDimension() - 1 >();

      const DeviceMesh mesh = mesh_src;
      Pointers::DevicePointer< const DeviceMesh > meshPointer( mesh );
      Containers::Vector< Real, Device, Index > spheres;
      spheres.setSize( verticesCount );

      auto getLocalFaceIndex = [] __cuda_callable__
         ( const typename DeviceMesh::Cell & cell,
           const Index i )
      {
//         constexpr auto facesCount = Mesh::Cell::template getSubentitiesCount< 0 >();
         constexpr auto facesCount = Mesh::Cell::template SubentityTraits< Mesh::getMeshDimension() - 1 >::count;
         for( LocalIndex f = 0; f < facesCount; f++ ) {
            const auto fid = cell.template getSubentityIndex< Mesh::getMeshDimension() - 1 >( f );
            if( fid == i ) {
               return f;
            }
         }
         TNL_ASSERT( false,
                     std::cerr << "local face index not found -- this is a BUG!" << std::endl; );
         return (LocalIndex) 0;
      };

      auto kernel_spheres = [getLocalFaceIndex] __cuda_callable__
         ( Index fid,
           const DeviceMesh* mesh,
           Real* array )
      {
         const auto& face = mesh->template getEntity< Mesh::getMeshDimension() - 1 >( fid );
         const auto face_measure = getEntityMeasure( *mesh, face );

         const auto cellsCount = face.template getSuperentitiesCount< Mesh::getMeshDimension() >();
         for( LocalIndex c = 0; c < cellsCount; c++ ) {
            const auto cid = face.template getSuperentityIndex< Mesh::getMeshDimension() >( c );
            const auto& cell = mesh->template getEntity< Mesh::getMeshDimension() >( cid );
            // specialized version for simplices (assuming that opposite vertex and face have the same local index)
            const auto v = getLocalFaceIndex( cell, fid );
            const auto vid = cell.template getSubentityIndex< 0 >( v );
            Algorithms::AtomicOperations< Device >::add( array[ vid ], face_measure );
         }
      };

      auto reset = [&]() {
         spheres.setValue( 0.0 );
      };

      auto benchmark_func = [&] () {
         Algorithms::ParallelFor< Device >::exec(
               (Index) 0, facesCount,
               kernel_spheres,
               &meshPointer.template getData< Device >(),
               spheres.getData() );
      };

      benchmark.time< Device >( reset,
                                (std::is_same< Device, Devices::Host >::value) ? "CPU" : "GPU",
                                benchmark_func );
   }
};

template< template< typename, int, typename, typename, typename > class ConfigTemplate,
          typename CellTopology,
          int SpaceDimension,
          typename Real,
          typename GlobalIndex,
          typename LocalIndex >
struct MeshBenchmarksRunner
{
    // IMPORTANT NOTE:
    // The definition of the method must be separate from its declaration,
    // otherwise the compiler would always do implicit instead of explicit
    // instantiation.
    static bool
    run( Benchmark<> & benchmark,
         const Config::ParameterContainer & parameters );
};

template< template< typename, int, typename, typename, typename > class ConfigTemplate,
          typename CellTopology,
          int SpaceDimension,
          typename Real,
          typename GlobalIndex,
          typename LocalIndex >
bool
MeshBenchmarksRunner< ConfigTemplate,  CellTopology, SpaceDimension, Real, GlobalIndex, LocalIndex >::
run( Benchmark<> & benchmark,
     const Config::ParameterContainer & parameters )
{
   using Config = ConfigTemplate< CellTopology, SpaceDimension, Real, GlobalIndex, LocalIndex >;
   using MeshType = Mesh< Config, Devices::Host >;
   return MeshBenchmarks< MeshType >::run( benchmark, parameters );
}

extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, float, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, float, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, float, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, float, long int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, double, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, double, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, double, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Edge, 1, double, long int, int >;

extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, float, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, float, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, float, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, float, long int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, double, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, double, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, double, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Triangle, 2, double, long int, int >;

extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, float, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, float, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, float, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, float, long int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, double, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, double, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, double, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Quadrangle, 2, double, long int, int >;

extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, float, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, float, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, float, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, float, long int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, double, int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, double, int, int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, double, long int, short int >;
extern template struct MeshBenchmarksRunner< FullConfig, Topologies::Tetrahedron, 3, double, long int, int >;

extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, float, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, float, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, float, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, float, long int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, double, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, double, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, double, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Edge, 1, double, long int, int >;

extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, float, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, float, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, float, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, float, long int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, double, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, double, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, double, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Triangle, 2, double, long int, int >;

extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, float, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, float, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, float, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, float, long int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, double, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, double, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, double, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Quadrangle, 2, double, long int, int >;

extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, float, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, float, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, float, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, float, long int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, double, int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, double, int, int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, double, long int, short int >;
extern template struct MeshBenchmarksRunner< MinimalConfig, Topologies::Tetrahedron, 3, double, long int, int >;
