#include <config.h>

#include <dune/geometry/type.hh>
#include <dune/geometry/quadraturerules/gaussquadrature.hh>

#include <dune/localfunctions/utility/field.hh>
#include <dune/localfunctions/orthonormal/orthonormalbasis.hh>

#if HAVE_ALGLIB
  typedef amp::ampf< 128 > StorageField;
  typedef amp::ampf< 512 > ComputeField;
#else
#if HAVE_GMP
  typedef Dune::GMPField< 128 > StorageField;
  typedef Dune::GMPField< 512 > ComputeField;
#else
  typedef double StorageField;
  typedef double ComputeField;
#endif
#endif

template <class Topology>
bool test(unsigned int order)
{
  bool ret = true;
  for (unsigned int o=order;o<=order;--o) 
  {
    std::cout << "Testing " << Topology::name() << " in dimension " << Topology::dimension << " with order " << o << std::endl;
    typedef Dune::OrthonormalBasisFactory<Topology::dimension,StorageField,ComputeField> BasisFactory;
    const typename BasisFactory::Object &basis = *BasisFactory::template create<Topology>(o);

    const unsigned int size = basis.size( );

    std::vector< Dune::FieldVector< double, 1 > > y( size );

    std::vector< Dune::FieldVector< double, 1 > > m( size * size );
    for( unsigned int i = 0; i < size * size; ++i )
      m[ i ] = 0;

    // typedef typename Dune::GenericGeometry::GaussQuadratureProvider<Topology::dimension,double,ComputeField> QuadratureProvider;
    typedef typename Dune::GenericGeometry::GaussQuadratureProvider<Topology::dimension,double,double> QuadratureProvider;
    typedef typename QuadratureProvider::Object Quadrature;
    const Quadrature &quadrature = *QuadratureProvider::template create<Topology>(2*order+1);
    const unsigned int quadratureSize = quadrature.size();
    for( unsigned int qi = 0; qi < quadratureSize; ++qi )
    {
      basis.evaluate( quadrature.position( qi ), y );
      for( unsigned int i = 0; i < size; ++i )
      {
        for( unsigned int j = 0; j < size; ++j )
          m[ i*size + j ] += quadrature.weight( qi ) * y[ i ] * y[ j ];
      }
    }

    for( unsigned int i = 0; i < size; ++i )
    {
      for( unsigned int j = 0; j < size; ++j )
      {
        const double value = m[ i*size + j ];
        if( fabs( value - double( i == j ) ) > 1e-10 ) {
          std::cout << "i = " << i << ", j = " << j << ": " << value << std::endl;
          ret = false;
        }
      }
    }

    QuadratureProvider::release(&quadrature);
    BasisFactory::release(&basis);
  }
  if (!ret) {
    std::cout << "   FAILED !" << std::endl;
  }
  std::cout << std::endl;
  return ret;
}
template <unsigned int dimension>
bool test(unsigned int topologyId, unsigned int order)
{
  Dune::GeometryType gt(topologyId, dimension);
  
  bool ret = true;
  for (unsigned int o=order;o<=order;--o) 
  {
    std::cout << "Testing " << topologyId << " in dimension " << dimension << " with order " << o << std::endl;
    typedef Dune::OrthonormalBasisFactory<dimension,StorageField,ComputeField> BasisFactory;
    const typename BasisFactory::Object &basis = *BasisFactory::create(gt, o);

    const unsigned int size = basis.size( );

    std::vector< Dune::FieldVector< double, 1 > > y( size );

    std::vector< Dune::FieldVector< double, 1 > > m( size * size );
    for( unsigned int i = 0; i < size * size; ++i )
      m[ i ] = 0;

    // typedef typename Dune::GenericGeometry::GaussQuadratureProvider<dimension,double,ComputeField> QuadratureProvider;
    typedef typename Dune::GenericGeometry::GaussQuadratureProvider<dimension,double,double> QuadratureProvider;
    typedef typename QuadratureProvider::Object Quadrature;
    const Quadrature &quadrature = *QuadratureProvider::create(gt, 2*order+1);
    const unsigned int quadratureSize = quadrature.size();
    for( unsigned int qi = 0; qi < quadratureSize; ++qi )
    {
      basis.evaluate( quadrature.position( qi ), y );
      for( unsigned int i = 0; i < size; ++i )
      {
        for( unsigned int j = 0; j < size; ++j )
          m[ i*size + j ] += quadrature.weight( qi ) * y[ i ] * y[ j ];
      }
    }

    for( unsigned int i = 0; i < size; ++i )
    {
      for( unsigned int j = 0; j < size; ++j )
      {
        const double value = m[ i*size + j ];
        if( fabs( value - double( i == j ) ) > 1e-10 ) {
          std::cout << "i = " << i << ", j = " << j << ": " << value << std::endl;
          ret = false;
        }
      }
    }
    
    QuadratureProvider::release(&quadrature);
    BasisFactory::release(&basis);
  }
  if (!ret) {
    std::cout << "   FAILED !" << std::endl;
  }
  std::cout << std::endl;
  return ret;
}

int main ( int argc, char **argv )
{
  using namespace Dune;
  using namespace GenericGeometry;

  if( argc < 2 )
  {
    std::cerr << "Usage: " << argv[ 0 ] << " <p>" << std::endl;
    return 2;
  }

  const unsigned int order = atoi( argv[ 1 ] );
  bool tests = true;
#ifdef TOPOLOGY
  tests &= test<TOPOLOGY>(order);
  tests &= test<TOPOLOGY::dimension>(TOPOLOGY::id,order);
  return 0;
#else
  tests &= test<Prism<Point> > (order);
  tests &= test<Pyramid<Point> > (order);

  tests &= test<Prism<Prism<Point> > > (order);
  tests &= test<Pyramid<Pyramid<Point> > >(order);

  tests &= test<Prism<Prism<Prism<Point> > > >(order);
  tests &= test<Prism<Pyramid<Pyramid<Point> > > >(order);
  tests &= test<Pyramid<Prism<Prism<Point> > > >(order);
  tests &= test<Pyramid<Pyramid<Pyramid<Point> > > >(order);

  tests &= test<Prism<Prism<Prism<Prism<Point> > > > >(order);
  tests &= test<Pyramid<Pyramid<Pyramid<Pyramid<Point> > > > >(order);

  return (tests?0:1);
#endif // TOPOLOGY
}
