// Copyright 2002 - 2008, 2010, 2011 National Technology Engineering
// Solutions of Sandia, LLC (NTESS). Under the terms of Contract
// DE-NA0003525 with NTESS, the U.S. Government retains certain rights
// in this software.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#ifndef RegressionTestLocalRefiner_hpp
#define RegressionTestLocalRefiner_hpp

#include <stdexcept>
#include <sstream>
#include <vector>
#include <cmath>
#include <iostream>
#include <fstream>
#include <string>
#include <typeinfo>

#include <math.h>
#if defined( STK_HAS_MPI )
#include <mpi.h>
#endif

#include <gtest/gtest.h>

#include <stk_util/environment/WallTime.hpp>
#include <stk_util/diag/PrintTable.hpp>

#include <Teuchos_ScalarTraits.hpp>

#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <stk_io/IossBridge.hpp>

#include <percept/Percept.hpp>
#include <percept/Util.hpp>
#include <percept/ExceptionWatch.hpp>

#include <percept/function/StringFunction.hpp>
#include <percept/function/FieldFunction.hpp>
#include <percept/function/ConstantFunction.hpp>
#include <percept/PerceptMesh.hpp>

#include <adapt/UniformRefinerPattern.hpp>
#include <adapt/Refiner.hpp>

#include <adapt/sierra_element/StdMeshObjTopologies.hpp>
#include <percept/RunEnvironment.hpp>

#include <percept/fixtures/Fixture.hpp>
#include <percept/fixtures/BeamFixture.hpp>
#include <percept/fixtures/HeterogeneousFixture.hpp>
#include <percept/fixtures/QuadFixture.hpp>
#include <percept/fixtures/WedgeFixture.hpp>

#include <adapt/IEdgeBasedAdapterPredicate.hpp>
#include <adapt/IElementBasedAdapterPredicate.hpp>
#include <adapt/PredicateBasedEdgeAdapter.hpp>

#include <percept/function/ElementOp.hpp>

  namespace percept
  {
    namespace regression_tests
    {

      //=============================================================================
      //=============================================================================
      //=============================================================================

      void normalize(double input_normal[3], double normal[3]);

      void normalize(double input_output_normal[3]);

      double distance(double c0[3], double c1[3]);

      void difference(double v01[3], double c0[3], double c1[3]);

      double dot(double c0[3], double c1[3]);

      double plane_dot_product(double plane_point[3], double plane_normal[3], double point[3]);

      class SetElementRefineFieldValue : public percept::ElementOp
      {
        RefineFieldType_type m_val;
      public:
        SetElementRefineFieldValue(percept::PerceptMesh& eMesh, int val=0) : m_val(val) {
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          f_data[0] = m_val;

          return false;  // don't terminate the loop
        }
        virtual void init_elementOp() {}
        virtual void fini_elementOp() {}

      };

      class SetRefineField : public percept::ElementOp
      {
        percept::PerceptMesh& m_eMesh;
      public:
        SetRefineField(percept::PerceptMesh& eMesh) : m_eMesh(eMesh) {
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          double plane_point[3] = {2,0,0};
          double plane_normal[3] = {1, .5, -.5};

          const percept::MyPairIterRelation elem_nodes (m_eMesh, element, stk::topology::NODE_RANK );

          unsigned num_node = elem_nodes.size();
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

          bool found = false;
          for (unsigned inode=0; inode < num_node-1; inode++)
            {
              stk::mesh::Entity node_i = elem_nodes[ inode ].entity();
              double *coord_data_i = m_eMesh.field_data(coordField, node_i);

              for (unsigned jnode=inode+1; jnode < num_node; jnode++)
                {
                  stk::mesh::Entity node_j = elem_nodes[ jnode ].entity();
                  double *coord_data_j = m_eMesh.field_data(coordField, node_j);

                  double dot_0 = plane_dot_product(plane_point, plane_normal, coord_data_i);
                  double dot_1 = plane_dot_product(plane_point, plane_normal, coord_data_j);

                  // if edge crosses the plane...
                  if (dot_0*dot_1 < 0)
                    {
                      found=true;
                      break;
                    }
                }
            }
          if (found)
            f_data[0] = 1;
          else
            f_data[0] = 0;

          return false;  // don't terminate the loop
        }
        virtual void init_elementOp() {}
        virtual void fini_elementOp() {}
      };

      class SetUnrefineField : public percept::ElementOp
      {
        percept::PerceptMesh& m_eMesh;
      public:
        SetUnrefineField(percept::PerceptMesh& eMesh) : m_eMesh(eMesh) {}
        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          const percept::MyPairIterRelation elem_nodes (m_eMesh, element, stk::topology::NODE_RANK );
          unsigned num_node = elem_nodes.size();
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

          bool found = true;
          for (unsigned inode=0; inode < num_node; inode++)
            {
              stk::mesh::Entity node = elem_nodes[ inode ].entity();
              double *coord_data = m_eMesh.field_data(coordField, node);

              if (coord_data[0] < 0.0 || coord_data[1] < 0.0) // || coord_data[2] > 1.1)
                {
                  found=false;
                  break;
                }
            }
          if (found)
            f_data[0] = -1;
          else
            f_data[0] = 0;

          return false;  // don't terminate the loop
        }
        virtual void init_elementOp() {}
        virtual void fini_elementOp() {}
      };

      struct MyEdgeBasedRefinePredicate : public IEdgeBasedAdapterPredicate {

        MyEdgeBasedRefinePredicate(PerceptMesh& eMesh, stk::mesh::Selector * selector=0, stk::mesh::FieldBase *field=0, double tolerance=0.0) :
          IEdgeBasedAdapterPredicate(eMesh, selector, field, tolerance) {}

        /// Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element, unsigned which_edge, stk::mesh::Entity node0, stk::mesh::Entity node1,
                       double *coord0, double *coord1, std::vector<int>* existing_edge_marks)
        {
          int mark = 0;
          if (0 == m_selector || (*m_selector)(m_eMesh.bucket(element)))
            {
              double plane_point[3] = {2,0,0};
              double plane_normal[3] = {1, 0, 0};

              double dot_0 = plane_dot_product(plane_point, plane_normal, coord0);
              double dot_1 = plane_dot_product(plane_point, plane_normal, coord1);

              // if edge crosses the plane...
              if (dot_0 * dot_1 < 0)
                {
                  mark |= DO_REFINE;
                }

              if (coord0[1] < 0 && coord1[1] < 0)
                {
                  mark |= DO_UNREFINE;
                }
            }

          return mark;
        }
      };

      struct PlaneShock
      {
        double plane_point_init[3]; // = {2 + shock_displacement,0,0};
        double plane_normal[3]; // = {1, 0, 0};
        double plane_point[3]; // = {2 + shock_displacement,0,0};
        double shock_width;

        PlaneShock();

        void setCurrentPlanePoint(double shock_displacement);

        double shock_function(double x);
      };

      double shock_diff(stk::mesh::FieldBase* nodal_refine_field, percept::PerceptMesh& eMesh,
                        stk::mesh::Entity node0, stk::mesh::Entity node1,
                        double *coord0, double *coord1, PlaneShock& shock, double shock_displacement);

      int shock_diff1(stk::mesh::FieldBase* nodal_refine_field, percept::PerceptMesh& eMesh,
                      stk::mesh::Entity node0, stk::mesh::Entity node1,
                      double *coord0, double *coord1, PlaneShock& shock, double shock_displacement);

      // This can be used as an edge or element-based predicate

      struct ShockBasedRefinePredicate : public IEdgeBasedAdapterPredicate, IElementBasedAdapterPredicate {

        percept::PerceptMesh& m_eMesh;
        stk::mesh::FieldBase * m_nodal_refine_field;
        PlaneShock m_shock;
        double m_shock_displacement;
        double m_shock_diff_criterion;

        ShockBasedRefinePredicate(stk::mesh::FieldBase* nodal_refine_field, percept::PerceptMesh& eMesh, stk::mesh::Selector* selector, stk::mesh::FieldBase *field, double tolerance,
                                  PlaneShock shock, double shock_displacement=0, double shock_diff_criterion=0.4) :
          IEdgeBasedAdapterPredicate(eMesh, selector, field, tolerance),
          IElementBasedAdapterPredicate(eMesh, selector, field, tolerance),
          m_eMesh(eMesh),m_nodal_refine_field(nodal_refine_field),
          m_shock(shock), m_shock_displacement(shock_displacement),
          m_shock_diff_criterion(shock_diff_criterion) {}

        /// Element-based: Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element)
        {
          if (0 == m_eb_selector || (*m_eb_selector)(m_eMesh.bucket(element)))
            {

              const CellTopologyData * const cell_topo_data = m_eMesh.get_cell_topology(element);
              int spatialDimension = m_eMesh.get_spatial_dim();
              CellTopology cell_topo(cell_topo_data);
              const percept::MyPairIterRelation elem_nodes (m_eMesh, element,stk::topology::NODE_RANK);

              CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

              unsigned numSubDimNeededEntities = 0;
              numSubDimNeededEntities = cell_topo_data->edge_count;

              unsigned ref_count=0, unref_count=0;
              for (unsigned iSubDimOrd = 0; iSubDimOrd < numSubDimNeededEntities; iSubDimOrd++)
                {
                  stk::mesh::Entity node0 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[0]].entity();
                  stk::mesh::Entity node1 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[1]].entity();
                  double * const coord0 = m_eMesh.field_data( *coordField , node0 );
                  double * const coord1 = m_eMesh.field_data( *coordField , node1 );
                  double  dcoord0[3] = {coord0[0],coord0[1], (spatialDimension==2?0:coord0[2])};
                  double  dcoord1[3] = {coord1[0],coord1[1], (spatialDimension==2?0:coord1[2])};

                  int markInfo = this->operator()(element, iSubDimOrd, node0, node1, dcoord0, dcoord1, 0);
                  bool do_ref = markInfo & DO_REFINE;
                  if (do_ref)
                    {
                      ++ref_count;
                    }
                  bool do_unref = markInfo & DO_UNREFINE;
                  if (!do_ref && do_unref)
                    {
                      ++unref_count;
                    }
                }
              if (ref_count == numSubDimNeededEntities)
                return DO_REFINE;
              if (unref_count == numSubDimNeededEntities)
                return DO_UNREFINE;
            }

          return DO_NOTHING;
        }

        /// Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element, unsigned which_edge, stk::mesh::Entity node0, stk::mesh::Entity node1,
                        double *coord0, double *coord1, std::vector<int>* existing_edge_marks)
        {
          int mark=0;
          if (0 == m_selector || (*m_selector)(m_eMesh.bucket(element)))
            {

              // refine check
              double d01 = shock_diff(m_nodal_refine_field, m_eMesh, node0, node1, coord0, coord1, m_shock, m_shock_displacement);
              if ( d01 > m_shock_diff_criterion)  // 0.05, 0.2
                {
                  mark |= DO_REFINE;
                }

              // unrefine check
              d01 = shock_diff(0, m_eMesh, node0, node1, coord0, coord1, m_shock, m_shock_displacement);
              if ( d01 <= m_shock_diff_criterion/2.0 )  // 0.05, 0.2
                {
                  mark |= DO_UNREFINE;
                }
            }
          return mark;
        }
      };

      // This can be used as an edge or element-based predicate

      struct ShockBasedRefinePredicate1 : public IEdgeBasedAdapterPredicate, IElementBasedAdapterPredicate, percept::ElementOp,  GenericFunction {

        percept::PerceptMesh& m_eMesh;
        stk::mesh::FieldBase * m_nodal_refine_field;
        PlaneShock m_shock;
        double m_shock_displacement;
        double m_shock_diff_criterion;

        ShockBasedRefinePredicate1(stk::mesh::FieldBase* nodal_refine_field, percept::PerceptMesh& eMesh, stk::mesh::Selector* selector, stk::mesh::FieldBase *field, double tolerance,
                                  PlaneShock shock, double shock_displacement=0, double shock_diff_criterion=0.4) :
          IEdgeBasedAdapterPredicate(eMesh, selector, field, tolerance),
          IElementBasedAdapterPredicate(eMesh, selector, field, tolerance),
          m_eMesh(eMesh),m_nodal_refine_field(nodal_refine_field),
          m_shock(shock), m_shock_displacement(shock_displacement),
          m_shock_diff_criterion(shock_diff_criterion) {}

        /// Element-based: Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element)
        {
          if (0 == m_eb_selector || (*m_eb_selector)(m_eMesh.bucket(element)))
            {

              const CellTopologyData * const cell_topo_data = m_eMesh.get_cell_topology(element);
              int spatialDimension = m_eMesh.get_spatial_dim();
              CellTopology cell_topo(cell_topo_data);
              const percept::MyPairIterRelation elem_nodes (m_eMesh, element,stk::topology::NODE_RANK);

              CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

              unsigned numSubDimNeededEntities = 0;
              numSubDimNeededEntities = cell_topo_data->edge_count;

              unsigned ref_count=0, unref_count=0;
              for (unsigned iSubDimOrd = 0; iSubDimOrd < numSubDimNeededEntities; iSubDimOrd++)
                {
                  stk::mesh::Entity node0 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[0]].entity();
                  stk::mesh::Entity node1 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[1]].entity();
                  double * const coord0 = m_eMesh.field_data( *coordField , node0 );
                  double * const coord1 = m_eMesh.field_data( *coordField , node1 );
                  double  dcoord0[3] = {coord0[0],coord0[1], (spatialDimension==2?0:coord0[2])};
                  double  dcoord1[3] = {coord1[0],coord1[1], (spatialDimension==2?0:coord1[2])};

                  int markInfo = this->operator()(element, iSubDimOrd, node0, node1, dcoord0, dcoord1, 0);
                  bool do_ref = markInfo & DO_REFINE;
                  if (do_ref)
                    {
                      ++ref_count;
                    }
                  bool do_unref = markInfo & DO_UNREFINE;
                  if (!do_ref && do_unref)
                    {
                      ++unref_count;
                    }
                }
              if (ref_count == numSubDimNeededEntities)
                return DO_REFINE;
              if (unref_count == numSubDimNeededEntities)
                return DO_UNREFINE;
            }

          return DO_NOTHING;
        }

        /// Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element, unsigned which_edge, stk::mesh::Entity node0, stk::mesh::Entity node1,
                        double *coord0, double *coord1, std::vector<int>* existing_edge_marks)
        {
          int mark=0;
          if (0 == m_selector || (*m_selector)(m_eMesh.bucket(element)))
            {

              // refine check
              mark = shock_diff1(m_nodal_refine_field, m_eMesh, node0, node1, coord0, coord1, m_shock, m_shock_displacement);
            }
          return mark;
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          const percept::MyPairIterRelation elem_nodes (m_eMesh, element, stk::topology::NODE_RANK );

          unsigned num_node = elem_nodes.size();
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

          bool found = false;
          for (unsigned inode=0; inode < num_node-1; inode++)
            {
              stk::mesh::Entity node_i = elem_nodes[ inode ].entity();
              //mesh::Entity node_i = elem_nodes_e[ inode ];
              double *coord_data_i = m_eMesh.field_data(coordField, node_i);

              for (unsigned jnode=inode+1; jnode < num_node; jnode++)
                {
                  stk::mesh::Entity node_j = elem_nodes[ jnode ].entity();
                  double *coord_data_j = m_eMesh.field_data(coordField, node_j);

                  int mark = shock_diff1(m_nodal_refine_field, m_eMesh, node_i, node_j, coord_data_i, coord_data_j, m_shock, m_shock_displacement);
                  if (mark & DO_REFINE)
                    {
                      found=true;
                      break;
                    }
                }
            }
          if (found)
            f_data[0] = 1;
          else
            f_data[0] = -1;

          return false;  // don't terminate the loop
        }
        virtual void init_elementOp() {}
        virtual void fini_elementOp() {}

        // nodal op
        virtual void operator()(MDArray& domain, MDArray& codomain, double time_value_optional=0.0)
        {
          double x = domain(0);
          double y = domain(1);
          int spatialDimension = m_eMesh.get_spatial_dim();
          double z = (spatialDimension==3?domain(2):0);
          m_shock.setCurrentPlanePoint(m_shock_displacement);
          double *plane_point = m_shock.plane_point;
          double *plane_normal = m_shock.plane_normal;

          double coord0[] = {x,y,z};
          double dot_0 = plane_dot_product(plane_point, plane_normal, coord0);
          codomain(0) = dot_0 < 0? -1 : 1;
        }

      };

      class SetRefineField1 : public percept::ElementOp
      {
        IEdgeAdapter& m_breaker;
      public:
        SetRefineField1(IEdgeAdapter& breaker) : m_breaker(breaker) {
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          bool isParent = m_breaker.getMesh().isParentElement(element, false);
          bool hasFamilyTree = m_breaker.getMesh().hasFamilyTree(element);
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          int mark = m_breaker.markUnrefine(element);
          f_data[0] = 0;
          if (hasFamilyTree && !isParent)
            {
              if (mark & DO_UNREFINE)
                f_data[0] = -2;
              else
                f_data[0] = 2;
            }

          return false;
        }
        virtual void init_elementOp() {}
        virtual void fini_elementOp() {}
      };

      class SetElementFieldBoundingBox : public percept::ElementOp
      {
        percept::PerceptMesh& m_eMesh;
        stk::mesh::FieldBase *m_field;
      public:
        std::vector<double> m_bbox;
        SetElementFieldBoundingBox(percept::PerceptMesh& eMesh, const std::vector<double>& bbox, stk::mesh::FieldBase *field) : m_eMesh(eMesh), m_field(field), m_bbox(bbox) {
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          stk::mesh::FieldBase* coord_field = m_eMesh.get_coordinates_field();

          const CellTopologyData * const cell_topo_data = m_eMesh.get_cell_topology(element);
          //int spatialDimension = m_eMesh.get_spatial_dim();
          CellTopology cell_topo(cell_topo_data);

          const MyPairIterRelation elem_nodes(bulkData, element, stk::topology::NODE_RANK );

          unsigned num_node = elem_nodes.size();
          double c[] = {0,0,0};
          for (unsigned inode=0; inode < num_node; inode++)
            {
              stk::mesh::Entity node = elem_nodes[ inode ].entity();
              double *c_data = m_eMesh.field_data(coord_field, node);
              c[0] += c_data[0]/double(num_node);
              c[1] += c_data[1]/double(num_node);
              c[2] += c_data[2]/double(num_node);
            }
          std::vector<double> cc(&c[0], &c[0]+3);
          if (0) std::cout << "SetElementFieldBoundingBox element " << m_eMesh.identifier(element) << " topo= " << cell_topo.getName()
                    << " c= " << cc << " bbox= " << m_bbox
                    <<  std::endl;

          if (
              (m_bbox[0] < c[0] && c[0] < m_bbox[1]) &&
              (m_bbox[2] < c[1] && c[1] < m_bbox[3]) &&
              (m_bbox[4] < c[2] && c[2] < m_bbox[5])
              )
            {
              if (0) std::cout << "SetElementFieldBoundingBox marking element " << m_eMesh.identifier(element) << " for refine" << std::endl;
              f_data[0] = 1;
            }
          else
            {
              f_data[0] = -1;
            }
          return false;  // don't terminate the loop
        }
        virtual void init_elementOp()
        {
          std::vector< const stk::mesh::FieldBase *> fields(1,m_field);
          stk::mesh::communicate_field_data(m_eMesh.get_bulk_data()->aura_ghosting(), fields);
        }
        virtual void fini_elementOp() {
          std::vector< const stk::mesh::FieldBase *> fields(1,m_field);
          stk::mesh::communicate_field_data(m_eMesh.get_bulk_data()->aura_ghosting(), fields);
        }
      };

      class SetElementFieldSelector : public percept::ElementOp
      {
        percept::PerceptMesh& m_eMesh;
        stk::mesh::FieldBase *m_field;
        stk::mesh::Selector m_selector;
      public:
        SetElementFieldSelector(percept::PerceptMesh& eMesh, stk::mesh::Selector sel, stk::mesh::FieldBase *field) : m_eMesh(eMesh), m_field(field), m_selector(sel) {
        }

        virtual bool operator()(const stk::mesh::Entity element, stk::mesh::FieldBase *field,  const stk::mesh::BulkData& bulkData)
        {
          RefineFieldType_type *f_data = stk::mesh::field_data(*static_cast<RefineFieldType *>(field), element);
          if (m_selector(m_eMesh.bucket(element)))
            {
              f_data[0] = 1;
            }
          else
            {
              f_data[0] = -1;
            }
          return false;  // don't terminate the loop
        }
        virtual void init_elementOp()
        {
          std::vector< const stk::mesh::FieldBase *> fields(1,m_field);
          stk::mesh::communicate_field_data(m_eMesh.get_bulk_data()->aura_ghosting(), fields);
        }
        virtual void fini_elementOp() {
          std::vector< const stk::mesh::FieldBase *> fields(1,m_field);
          stk::mesh::communicate_field_data(m_eMesh.get_bulk_data()->aura_ghosting(), fields);
        }
      };

      struct PointSourceRefinePredicate : public IElementBasedAdapterPredicate
      {

        percept::PerceptMesh& m_eMesh;
        std::vector<double> m_pointSourcePosition;

        PointSourceRefinePredicate(percept::PerceptMesh& eMesh, stk::mesh::Selector* selector, stk::mesh::FieldBase *field, double tolerance,
                                   std::vector<double>& pointSourcePosition) :
        IElementBasedAdapterPredicate(eMesh, selector, field, tolerance),
          m_eMesh(eMesh), m_pointSourcePosition(pointSourcePosition)
        {
        }

        /// Element-based: Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element)
        {
          if (0 == m_eb_selector || (*m_eb_selector)(m_eMesh.bucket(element)))
            {
              const CellTopologyData * const cell_topo_data = m_eMesh.get_cell_topology(element);
              //int spatialDimension = m_eMesh.get_spatial_dim();
              CellTopology cell_topo(cell_topo_data);
              const percept::MyPairIterRelation elem_nodes (m_eMesh, element,stk::topology::NODE_RANK);
              VERIFY_OP_ON(elem_nodes.size(), ==, 3, "only for tris");

              CoordinatesFieldType* coordField = m_eMesh.get_coordinates_field();

              unsigned numSubDimNeededEntities = 0;
              numSubDimNeededEntities = cell_topo_data->edge_count;

              //int count=0;
              unsigned ref_count=0, unref_count=0;
              for (unsigned iSubDimOrd = 0; iSubDimOrd < numSubDimNeededEntities; iSubDimOrd++)
                {
                  stk::mesh::Entity node0 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[0]].entity();
                  stk::mesh::Entity node1 = elem_nodes[cell_topo_data->edge[iSubDimOrd].node[1]].entity();
                  double * const coord0 = m_eMesh.field_data( *coordField , node0 );
                  double * const coord1 = m_eMesh.field_data( *coordField , node1 );
                  double cross =
                    (coord1[0] - coord0[0])*(m_pointSourcePosition[1] - coord0[1])
                    - (coord1[1] - coord0[1])*(m_pointSourcePosition[0] - coord0[0]);

                  if (cross > 0)
                    ++ref_count;
                  else
                    ++unref_count;
                }
              if (ref_count == numSubDimNeededEntities)
                return DO_REFINE;
              else
                return DO_UNREFINE;

            }

          return DO_NOTHING;
        }

      };
    }

    struct EdgeBasedInterpError : public IEdgeBasedAdapterPredicate {

    	  EdgeBasedInterpError(PerceptMesh& eMesh, stk::mesh::Selector * selector=0, stk::mesh::FieldBase *field=0, double tolerance=0.0) :
          IEdgeBasedAdapterPredicate(eMesh, selector, field, tolerance) {}

        /// Return DO_NOTHING, DO_REFINE, DO_UNREFINE or sum of these
        int operator()(const stk::mesh::Entity element, unsigned which_edge, stk::mesh::Entity node0, stk::mesh::Entity node1,
                       double *coord0, double *coord1, std::vector<int>* existing_edge_marks)
        {
          int mark = DO_NOTHING;

          if (0 == m_selector || (*m_selector)(m_eMesh.bucket(element)))
            {
              double edge_vec[2];
              edge_vec[0] = coord0[0] - coord1[0];
              edge_vec[1] = coord0[1] - coord1[1];

              double midpoint[2];
              midpoint[0] = 0.5*(coord0[0] + coord1[0]);
              midpoint[1] = 0.5*(coord0[1] + coord1[1]);

              double Hess[2][2];
              memset(&Hess[0][0], 0, sizeof(double)*4);

              // assume u(x,y) = x*x*x
              Hess[0][0]= 6.0*midpoint[0]; // 6*x

              double error = 0;

              for (int i=0; i<2; i++) {
            	  for (int j=0; j<2; j++) {
            		  error += Hess[i][j] * edge_vec[i] * edge_vec[j];
            	  }
              }

              if (error > 0.05) {
            	  mark |= DO_REFINE;
              }
            }

          return mark;
        }
      };

  }

#endif


#include <boost/preprocessor/arithmetic.hpp>


///////////////////////////////////////////////////////////////
//  Copyright 2012 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_

//
// Compare arithmetic results using fixed_int to GMP results.
//

#ifdef _MSC_VER
#  define _SCL_SECURE_NO_WARNINGS
#endif

#include <boost/multiprecision/cpp_int.hpp>
#include "test.hpp"

template <class Number>
void test()
{
   using namespace boost::multiprecision;
   typedef Number test_type;

   if(std::numeric_limits<test_type>::is_bounded)
   {
      test_type val = (std::numeric_limits<test_type>::max)();
      BOOST_CHECK_THROW(++val, std::overflow_error);
      val = (std::numeric_limits<test_type>::max)();
      BOOST_CHECK_THROW(test_type(1 + val), std::overflow_error);
      BOOST_CHECK_THROW(test_type(val + 1), std::overflow_error);
      BOOST_CHECK_THROW(test_type(2 * val), std::overflow_error);
      val /= 2;
      val += 1;
      BOOST_CHECK_THROW(test_type(2 * val), std::overflow_error);

      if(std::numeric_limits<test_type>::is_signed)
      {
         val = (std::numeric_limits<test_type>::min)();
         BOOST_CHECK_THROW(--val, std::overflow_error);
         val = (std::numeric_limits<test_type>::min)();
         BOOST_CHECK_THROW(test_type(val - 1), std::overflow_error);
         BOOST_CHECK_THROW(test_type(2 * val), std::overflow_error);
         val /= 2;
         val -= 1;
         BOOST_CHECK_THROW(test_type(2 * val), std::overflow_error);
      }
      else
      {
         val = (std::numeric_limits<test_type>::min)();
         BOOST_CHECK_THROW(--val, std::range_error);
         val = (std::numeric_limits<test_type>::min)();
         BOOST_CHECK_THROW(test_type(val - 1), std::range_error);
      }
   }

   if(std::numeric_limits<test_type>::is_signed)
   {
      test_type a = -1;
      test_type b = 1;
      BOOST_CHECK_THROW(test_type(a | b), std::range_error);
      BOOST_CHECK_THROW(test_type(a & b), std::range_error);
      BOOST_CHECK_THROW(test_type(a ^ b), std::range_error);
   }
   else
   {
      // Constructing from a negative value is not allowed:
      BOOST_CHECK_THROW(test_type(-2), std::range_error);
      BOOST_CHECK_THROW(test_type("-2"), std::range_error);
   }
   if(std::numeric_limits<test_type>::digits < std::numeric_limits<long long>::digits)
   {
      long long llm = (std::numeric_limits<long long>::max)();
      test_type t;
      BOOST_CHECK_THROW(t = llm, std::range_error);
      BOOST_CHECK_THROW(t = static_cast<test_type>(llm), std::range_error);
      unsigned long long ullm = (std::numeric_limits<unsigned long long>::max)();
      BOOST_CHECK_THROW(t = ullm, std::range_error);
      BOOST_CHECK_THROW(t = static_cast<test_type>(ullm), std::range_error);

      static const checked_uint512_t big = (std::numeric_limits<checked_uint512_t>::max)();
      BOOST_CHECK_THROW(t = static_cast<test_type>(big), std::range_error);
   }
   //
   // String errors:
   //
   BOOST_CHECK_THROW(test_type("12A"), std::runtime_error);
   BOOST_CHECK_THROW(test_type("0658"), std::runtime_error);

   if(std::numeric_limits<test_type>::is_signed)
   {
      BOOST_CHECK_THROW(test_type(-2).str(0, std::ios_base::hex), std::runtime_error);
      BOOST_CHECK_THROW(test_type(-2).str(0, std::ios_base::oct), std::runtime_error);
   }
}

int main()
{
   using namespace boost::multiprecision;

   test<number<cpp_int_backend<0, 0, signed_magnitude, checked> > >();
   test<checked_int512_t>();
   test<checked_uint512_t>();
   test<number<cpp_int_backend<32, 32, signed_magnitude, checked, void> > >();
   test<number<cpp_int_backend<32, 32, unsigned_magnitude, checked, void> > >();

   //
   // We also need to test type with "odd" bit counts in order to ensure full code coverage:
   //
   test<number<cpp_int_backend<528, 528, signed_magnitude, checked, void> > >();
   test<number<cpp_int_backend<528, 528, unsigned_magnitude, checked, void> > >();
   test<number<cpp_int_backend<48, 48, signed_magnitude, checked, void> > >();
   test<number<cpp_int_backend<48, 48, unsigned_magnitude, checked, void> > >();
   return boost::report_errors();
}


// Copyright 2008 Willow Garage, Inc.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the Willow Garage, Inc. nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

/** \Author Ioan Sucan */

#include <geometric_shapes/bodies.h>
#include <geometric_shapes/shape_operations.h>
#include <geometric_shapes/body_operations.h>
#include <boost/filesystem.hpp>
#include <gtest/gtest.h>
#include "resources/config.h"

// We expect surface points are counted inside.
#define EXPECT_SURF EXPECT_TRUE

// split length into the largest number elem, such that sqrt(elem^2 + elem^2) <= length
double largestComponentForLength2D(const double length)
{
  // HACK: sqrt(2) / 2 is a problem due to rounding errors since the distance
  //       is computed as 1.0000000000000002 . In such case we subtract an
  //       epsilon.

  double sq2 = sqrt(length / 2);
  while (sq2 * sq2 + sq2 * sq2 > length)
    sq2 -= std::numeric_limits<double>::epsilon();
  return sq2;
}

Eigen::Isometry3d getRandomPose(random_numbers::RandomNumberGenerator& g)
{
  const Eigen::Vector3d t(g.uniformReal(-100, 100), g.uniformReal(-100, 100), g.uniformReal(-100, 100));

  double quat[4];
  g.quaternion(quat);
  const Eigen::Quaterniond r({ quat[3], quat[0], quat[1], quat[2] });

  return Eigen::Isometry3d::TranslationType(t) * r;
}

TEST(SpherePointContainment, Basic)
{
  shapes::Sphere shape(1.0);
  bodies::Sphere sphere(&shape);

  // clang-format off
  // zero
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.00)));
  // general point outside
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(1.00, 1.00, 1.00)));

  // near single-axis maximum
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.99, 0.00, 0.00)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(1.00, 0.00, 0.00)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(1.01, 0.00, 0.00)));
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.00, 0.99, 0.00)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(0.00, 1.00, 0.00)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.00, 1.01, 0.00)));
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.99)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.00)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.01)));

  // near two-axis maximum
  const double sq2e = largestComponentForLength2D(1.0);
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.70, 0.70, 0.00)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(sq2e, sq2e, 0.00)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.71, 0.71, 0.00)));
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.70, 0.00, 0.70)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(sq2e, 0.00, sq2e)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.71, 0.00, 0.71)));
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.00, 0.70, 0.70)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(0.00, sq2e, sq2e)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.00, 0.71, 0.71)));

  // near three-axis maximum
  const double sq3 = sqrt(3) / 3;
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.57, 0.57, 0.57)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(sq3 , sq3 , sq3 )));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.58, 0.58, 0.58)));

  // near three-axis maximum with translation
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 0.0, 0.0);
  sphere.setPose(pose);

  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(1.57, 0.57, 0.57)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(1+sq3,sq3 , sq3 )));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(1.58, 0.58, 0.58)));

  pose.translation() = Eigen::Vector3d(0.0, 1.0, 0.0);
  sphere.setPose(pose);
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.57, 1.57, 0.57)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(sq3 ,1+sq3, sq3 )));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.58, 1.58, 0.58)));

  pose.translation() = Eigen::Vector3d(0.0, 0.0, 1.0);
  sphere.setPose(pose);
  EXPECT_TRUE( sphere.containsPoint(Eigen::Vector3d(0.57, 0.57, 1.57)));
  EXPECT_SURF( sphere.containsPoint(Eigen::Vector3d(sq3 , sq3, 1+sq3)));
  EXPECT_FALSE(sphere.containsPoint(Eigen::Vector3d(0.58, 0.58, 1.58)));
  // clang-format on
}

TEST(SpherePointContainment, SimpleInside)
{
  shapes::Sphere shape(1.0);
  bodies::Body* sphere = new bodies::Sphere(&shape);
  sphere->setScale(1.05);
  EXPECT_TRUE(sphere->containsPoint(0, 0, 1.0));

  random_numbers::RandomNumberGenerator r(0);
  Eigen::Vector3d p;
  for (int i = 0; i < 1000; ++i)
  {
    const Eigen::Isometry3d pos = getRandomPose(r);
    sphere->setPose(pos);
    sphere->setScale(r.uniformReal(0.1, 100.0));
    sphere->setPadding(r.uniformReal(-0.001, 10.0));

    EXPECT_TRUE(sphere->samplePointInside(r, 100, p));
    EXPECT_TRUE(sphere->containsPoint(p));
  }
  delete sphere;
}

TEST(SpherePointContainment, SimpleOutside)
{
  shapes::Sphere shape(1.0);
  bodies::Body* sphere = new bodies::Sphere(&shape);
  sphere->setScale(0.95);
  bool contains = sphere->containsPoint(0, 0, 1.0);
  delete sphere;
  EXPECT_FALSE(contains);
}

TEST(SpherePointContainment, ComplexInside)
{
  shapes::Sphere shape(1.0);
  bodies::Body* sphere = new bodies::Sphere(&shape);
  sphere->setScale(0.95);
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 1.0, 1.0);
  sphere->setPose(pose);
  bool contains = sphere->containsPoint(0.5, 1, 1.0);
  delete sphere;
  EXPECT_TRUE(contains);
}

TEST(SpherePointContainment, ComplexOutside)
{
  shapes::Sphere shape(1.0);
  bodies::Body* sphere = new bodies::Sphere(&shape);
  sphere->setScale(0.95);
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 1.0, 1.0);
  sphere->setPose(pose);
  bool contains = sphere->containsPoint(0.5, 0.0, 0.0);
  delete sphere;
  EXPECT_FALSE(contains);
}

TEST(BoxPointContainment, Basic)
{
  shapes::Box shape(2.0, 2.0, 2.0);
  bodies::Box box(&shape);

  // clang-format off
  // zero
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.00)));
  // general point outside
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(2.00, 2.00, 2.00)));

  // near single-axis maximum
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 0.00, 0.00)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 0.00, 0.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 0.00, 0.00)));
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.00, 0.99, 0.00)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(0.00, 1.00, 0.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(0.00, 1.01, 0.00)));
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.01)));

  // near two-axis maximum
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 0.99, 0.00)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 1.00, 0.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 1.01, 0.00)));
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 0.00, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 0.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 0.00, 1.01)));
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.00, 0.99, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(0.00, 1.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(0.00, 1.01, 1.01)));

  // near three-axis maximum
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 0.99, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 1.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 1.01, 1.01)));

  // near three-axis maximum with translation
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 0.0, 0.0);
  box.setPose(pose);

  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(1.99, 0.99, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(2.00, 1.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(2.01, 1.01, 1.01)));

  pose.translation() = Eigen::Vector3d(0.0, 1.0, 0.0);
  box.setPose(pose);
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 1.99, 0.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 2.00, 1.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 2.01, 1.01)));

  pose.translation() = Eigen::Vector3d(0.0, 0.0, 1.0);
  box.setPose(pose);
  EXPECT_TRUE( box.containsPoint(Eigen::Vector3d(0.99, 0.99, 1.99)));
  EXPECT_SURF( box.containsPoint(Eigen::Vector3d(1.00, 1.00, 2.00)));
  EXPECT_FALSE(box.containsPoint(Eigen::Vector3d(1.01, 1.01, 2.01)));
  // clang-format on
}

TEST(BoxPointContainment, SimpleInside)
{
  shapes::Box shape(1.0, 2.0, 3.0);
  bodies::Body* box = new bodies::Box(&shape);
  box->setScale(0.95);
  bool contains = box->containsPoint(0, 0, 1.0);
  EXPECT_TRUE(contains);

  random_numbers::RandomNumberGenerator r;
  Eigen::Vector3d p;
  EXPECT_TRUE(box->samplePointInside(r, 100, p));
  EXPECT_TRUE(box->containsPoint(p));

  delete box;
}

TEST(BoxPointContainment, SimpleOutside)
{
  shapes::Box shape(1.0, 2.0, 3.0);
  bodies::Body* box = new bodies::Box(&shape);
  box->setScale(0.95);
  bool contains = box->containsPoint(0, 0, 3.0);
  delete box;
  EXPECT_FALSE(contains);
}

TEST(BoxPointContainment, ComplexInside)
{
  shapes::Box shape(1.0, 1.0, 1.0);
  bodies::Body* box = new bodies::Box(&shape);
  box->setScale(1.01);
  Eigen::Isometry3d pose(Eigen::AngleAxisd(M_PI / 3.0, Eigen::Vector3d::UnitX()));
  pose.translation() = Eigen::Vector3d(1.0, 1.0, 1.0);
  box->setPose(pose);

  bool contains = box->containsPoint(1.5, 1.0, 1.5);
  EXPECT_TRUE(contains);

  delete box;
}

TEST(BoxPointContainment, Sampled)
{
  shapes::Box shape(1.0, 2.0, 3.0);
  bodies::Box box(&shape);

  random_numbers::RandomNumberGenerator r(0);
  Eigen::Vector3d p;
  for (int i = 0; i < 1000; ++i)
  {
    const Eigen::Isometry3d pos = getRandomPose(r);
    box.setPose(pos);
    box.setScale(r.uniformReal(0.1, 100.0));
    box.setPadding(r.uniformReal(-0.001, 10.0));

    EXPECT_TRUE(box.samplePointInside(r, 100, p));
    EXPECT_TRUE(box.containsPoint(p));
  }
}

TEST(BoxPointContainment, ComplexOutside)
{
  shapes::Box shape(1.0, 1.0, 1.0);
  bodies::Body* box = new bodies::Box(&shape);
  box->setScale(1.01);
  Eigen::Isometry3d pose(Eigen::AngleAxisd(M_PI / 3.0, Eigen::Vector3d::UnitX()));
  pose.translation() = Eigen::Vector3d(1.0, 1.0, 1.0);
  box->setPose(pose);

  bool contains = box->containsPoint(1.5, 1.5, 1.5);
  delete box;
  EXPECT_FALSE(contains);
}

TEST(CylinderPointContainment, Basic)
{
  shapes::Cylinder shape(1.0, 4.0);
  bodies::Cylinder cylinder(&shape);

  // clang-format off
  // zero
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.00)));
  // general point outside
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(1.00, 1.00, 4.00)));

  // near single-axis maximum
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.99, 0.00, 0.00)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(1.00, 0.00, 0.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(1.01, 0.00, 0.00)));
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.00, 0.99, 0.00)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(0.00, 1.00, 0.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.00, 1.01, 0.00)));
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(0.00, 0.00, 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.00, 0.00, 2.01)));

  // near two-axis maximum
  const double sq2 = sqrt(2) / 2;
  const double sq2e = largestComponentForLength2D(1);

  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.70, 0.70, 0.00)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(sq2e, sq2e, 0.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.71, 0.71, 0.00)));
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.99, 0.00, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(1.00, 0.00, 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(1.01, 0.00, 2.01)));
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.00, 0.99, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(0.00, 1.00, 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.00, 1.01, 2.01)));

  // near three-axis maximum
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.70, 0.70, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(sq2e, sq2e, 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.71, 0.71, 2.01)));

  // near three-axis maximum with translation
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 0.0, 0.0);
  cylinder.setPose(pose);

  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(1.70, 0.70, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(1+sq2,sq2 , 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(1.71, 0.71, 2.01)));

  pose.translation() = Eigen::Vector3d(0.0, 1.0, 0.0);
  cylinder.setPose(pose);
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.70, 1.70, 1.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(sq2 ,1+sq2, 2.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.71, 1.71, 2.01)));

  pose.translation() = Eigen::Vector3d(0.0, 0.0, 1.0);
  cylinder.setPose(pose);
  EXPECT_TRUE( cylinder.containsPoint(Eigen::Vector3d(0.70, 0.70, 2.99)));
  EXPECT_SURF( cylinder.containsPoint(Eigen::Vector3d(sq2e, sq2e, 3.00)));
  EXPECT_FALSE(cylinder.containsPoint(Eigen::Vector3d(0.71, 0.71, 3.01)));
  // clang-format on
}

TEST(CylinderPointContainment, SimpleInside)
{
  shapes::Cylinder shape(1.0, 4.0);
  bodies::Body* cylinder = new bodies::Cylinder(&shape);
  cylinder->setScale(1.05);
  bool contains = cylinder->containsPoint(0, 0, 2.0);
  delete cylinder;
  EXPECT_TRUE(contains);
}

TEST(CylinderPointContainment, SimpleOutside)
{
  shapes::Cylinder shape(1.0, 4.0);
  bodies::Body* cylinder = new bodies::Cylinder(&shape);
  cylinder->setScale(0.95);
  bool contains = cylinder->containsPoint(0, 0, 2.0);
  delete cylinder;
  EXPECT_FALSE(contains);
}

TEST(CylinderPointContainment, CylinderPadding)
{
  shapes::Cylinder shape(1.0, 4.0);
  bodies::Body* cylinder = new bodies::Cylinder(&shape);
  bool contains = cylinder->containsPoint(0, 1.01, 0);
  EXPECT_FALSE(contains);
  cylinder->setPadding(.02);
  contains = cylinder->containsPoint(0, 1.01, 0);
  EXPECT_TRUE(contains);
  cylinder->setPadding(0.0);
  bodies::BoundingSphere bsphere;
  cylinder->computeBoundingSphere(bsphere);
  EXPECT_TRUE(bsphere.radius > 2.0);

  delete cylinder;
}

TEST(CylinderPointContainment, Sampled)
{
  shapes::Cylinder shape(1.0, 4.0);
  bodies::Cylinder cylinder(&shape);

  random_numbers::RandomNumberGenerator r(0);
  Eigen::Vector3d p;
  for (int i = 0; i < 1000; ++i)
  {
    const Eigen::Isometry3d pos = getRandomPose(r);
    cylinder.setPose(pos);
    cylinder.setScale(r.uniformReal(0.1, 100.0));
    cylinder.setPadding(r.uniformReal(-0.001, 10.0));

    EXPECT_TRUE(cylinder.samplePointInside(r, 100, p));
    EXPECT_TRUE(cylinder.containsPoint(p));
  }
}

TEST(MeshPointContainment, Basic)
{
  // clang-format off
  shapes::Mesh *ms = shapes::createMeshFromResource("file://" +
    (boost::filesystem::path(TEST_RESOURCES_DIR) / "/box.dae").string());
  ASSERT_TRUE(ms != nullptr);
  bodies::ConvexMesh cubeMesh(ms);
  cubeMesh.setScale(1.5);
  cubeMesh.setPadding(0.5 * sqrt(3));

  // zero
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 0.00, 0.00)));
  // general point outside
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(3.00, 3.00, 3.00)));

  // near single-axis maximum
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 0.00, 0.00)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 0.00, 0.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 0.00, 0.00)));
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 1.99, 0.00)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 2.00, 0.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(0.00, 2.01, 0.00)));
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 0.00, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 0.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(0.00, 0.00, 2.01)));

  // near two-axis maximum
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 1.99, 0.00)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 2.00, 0.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 2.01, 0.00)));
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 0.00, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 0.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 0.00, 2.01)));
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 1.99, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(0.00, 2.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(0.00, 2.01, 2.01)));

  // near three-axis maximum
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 1.99, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 2.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 2.01, 2.01)));

  // near three-axis maximum with translation
  Eigen::Isometry3d pose;
  pose.setIdentity();
  pose.translation() = Eigen::Vector3d(1.0, 0.0, 0.0);
  cubeMesh.setPose(pose);

  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(2.99, 1.99, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(3.00, 2.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(3.01, 2.01, 2.01)));

  pose.translation() = Eigen::Vector3d(0.0, 1.0, 0.0);
  cubeMesh.setPose(pose);
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 2.99, 1.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 3.00, 2.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 3.01, 2.01)));

  pose.translation() = Eigen::Vector3d(0.0, 0.0, 1.0);
  cubeMesh.setPose(pose);
  EXPECT_TRUE( cubeMesh.containsPoint(Eigen::Vector3d(1.99, 1.99, 2.99)));
  EXPECT_SURF( cubeMesh.containsPoint(Eigen::Vector3d(2.00, 2.00, 3.00)));
  EXPECT_FALSE(cubeMesh.containsPoint(Eigen::Vector3d(2.01, 2.01, 3.01)));
  // clang-format on

  delete ms;
}

TEST(MeshPointContainment, Pr2Forearm)
{
  shapes::Mesh* ms = shapes::createMeshFromResource(
      "file://" + (boost::filesystem::path(TEST_RESOURCES_DIR) / "/forearm_roll.stl").string());
  ASSERT_TRUE(ms != nullptr);
  bodies::Body* m = new bodies::ConvexMesh(ms);
  ASSERT_TRUE(m != nullptr);
  Eigen::Isometry3d t(Eigen::Isometry3d::Identity());
  t.translation().x() = 1.0;
  EXPECT_FALSE(m->cloneAt(t)->containsPoint(-1.0, 0.0, 0.0));

  random_numbers::RandomNumberGenerator r(0);
  Eigen::Vector3d p;
  bool found = true;
  for (int i = 0; i < 10; ++i)
  {
    if (m->samplePointInside(r, 10000, p))
    {
      found = true;
      EXPECT_TRUE(m->containsPoint(p));
    }
  }
  EXPECT_TRUE(found);

  delete m;
  delete ms;
}

TEST(MergeBoundingSpheres, MergeTwoSpheres)
{
  std::vector<bodies::BoundingSphere> spheres;

  bodies::BoundingSphere s1;
  s1.center = Eigen::Vector3d(5.0, 0.0, 0.0);
  s1.radius = 1.0;

  bodies::BoundingSphere s2;
  s2.center = Eigen::Vector3d(-5.1, 0.0, 0.0);
  s2.radius = 1.0;

  spheres.push_back(s1);
  spheres.push_back(s2);

  bodies::BoundingSphere merged_sphere;
  bodies::mergeBoundingSpheres(spheres, merged_sphere);

  EXPECT_NEAR(merged_sphere.center[0], -.05, .00001);
  EXPECT_EQ(merged_sphere.radius, 6.05);
}

int main(int argc, char** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <cassert>
#include <iostream>
#include <string>
// own includes
#include "base/eigen2hdf.hpp"
#include "base/numbers.hpp"
#include "collision_tensor_galerkin.hpp"
#include "spectral/macroscopic_quantities.hpp"

using namespace std;

namespace boltzmann {

// --------------------------------------------------------------------------------------
CollisionTensorGalerkin::CollisionTensorGalerkin(const basis_t& basis)
    : N_(basis.n_dofs())
    , slices_(basis.n_dofs())
{
  /* empty */
  MQEval Mq(basis);

  // l2 projection
  Ht_.resize(4, N_);
  Ht_.setZero();
  Ht_.block(0, 0, 1, Mq.cmass().rows()) = Mq.cmass().transpose();
  Ht_.block(1, 0, 1, Mq.cenergy().rows()) = Mq.cenergy().transpose();
  Ht_.block(2, 0, 1, Mq.cux().rows()) = Mq.cux().transpose();
  Ht_.block(3, 0, 1, Mq.cuy().rows()) = Mq.cuy().transpose();

  auto make_mass_vdiag = [](const basis_t& basis) {
    Eigen::VectorXd v(basis.n_dofs());
    for (unsigned int i = 0; i < basis.n_dofs(); ++i) {
      auto& elem = basis.get_elem(i);
      typedef typename basis_t::elem_t elem_t;
      typedef typename std::tuple_element<0, typename elem_t::container_t>::type angular_elem_t;
      typedef typename std::tuple_element<1, typename elem_t::container_t>::type radial_elem_t;
      typename elem_t::Acc::template get<angular_elem_t> get_ang;
      typename elem_t::Acc::template get<radial_elem_t> get_rad;
      assert(get_rad(elem).get_id().fw == 0.5);
      if (get_ang(elem).get_id().l == 0) {
        v[i] = numbers::PI;
      } else {
        v[i] = 2 * numbers::PI;
      }
    }
    return v;
  };

  auto s = make_mass_vdiag(basis);
  vec_t sinv = s.array().inverse();
  Sinv_ = diag_t(sinv);
  HtHinv_ = (Ht_ * Sinv_ * Ht_.transpose()).inverse();
}

// --------------------------------------------------------------------------------------
void CollisionTensorGalerkin::add(ptr_t& slice, unsigned int j)
{
  if (j < N_)
    slices_[j] = slice;
  else {
    slices_.push_back(slice);
    ++N_;
  }
}

// --------------------------------------------------------------------------------------
const CollisionTensorGalerkin::sparse_matrix_t& CollisionTensorGalerkin::get(int j) const
{
  return *(this->slices_[j]);
}

// --------------------------------------------------------------------------------------
void CollisionTensorGalerkin::read_hdf5(const char* fname)
{
  ASSERT_MSG(boost::filesystem::exists(fname), "collision tensor file not found");
  slices_.resize(N_);
  hid_t file = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT);

  for (unsigned int i = 0; i < N_; ++i) {
    slices_[i] = ptr_t(new sparse_matrix_t(N_, N_));
    eigen2hdf::load_sparse(file, boost::lexical_cast<string>(i), *slices_[i]);
    // make sure this matrix is stored in compressed format
    slices_[i]->makeCompressed();
  }

  ASSERT_MSG((N_ == static_cast<unsigned int>(slices_[0]->rows())) &&
                 (N_ == static_cast<unsigned int>(slices_[0]->cols())),
             "Basis does not match collision tensor");

  H5Fclose(file);
}
}  // end boltzmann


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_TOOLBOX_SWAR_FUNCTIONS_COMPLEX_SIMD_COMMON_CUMSUM_HPP_INCLUDED
#define NT2_TOOLBOX_SWAR_FUNCTIONS_COMPLEX_SIMD_COMMON_CUMSUM_HPP_INCLUDED

#include <nt2/toolbox/swar/functions/cumsum.hpp>
#include <nt2/include/functions/load.hpp>
#include <nt2/include/functions/store.hpp>
#include <boost/simd/sdk/memory/aligned_type.hpp>
#include <boost/simd/sdk/meta/cardinal_of.hpp>
#include <boost/simd/sdk/meta/scalar_of.hpp>
#include <nt2/sdk/complex/meta/as_complex.hpp>
#include <nt2/sdk/complex/meta/as_real.hpp>
#include <nt2/sdk/complex/meta/as_dry.hpp>
#include <nt2/include/functions/imag.hpp>
#include <nt2/include/functions/real.hpp>

#include <algorithm>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::cumsum_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<complex_<arithmetic_<A0> >,X>))
                            )
  {
    typedef A0 result_type;
    NT2_FUNCTOR_CALL(1)
    {
      return result_type(cumsum(nt2::real(a0)), cumsum(nt2::imag(a0)));
    }
  };

  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::cumsum_, tag::cpu_
                                     , (A0)(X)
                                     , ((simd_<imaginary_<arithmetic_<A0> >,X>))
                                     )
  {
    typedef A0 result_type;
    NT2_FUNCTOR_CALL(1)
      {
        return result_type(cumsum(nt2::imag(a0)));
      }
  };
  NT2_FUNCTOR_IMPLEMENTATION( nt2::tag::cumsum_, tag::cpu_
                                     , (A0)(X)
                                     , ((simd_<dry_<arithmetic_<A0> >,X>))
                                     )
  {
    typedef A0 result_type;
    NT2_FUNCTOR_CALL(1)
      {
        return result_type(cumsum(nt2::real(a0)));
      }
  };
} }

#endif


/* boost random_speed.cpp performance measurements
 *
 * Copyright Jens Maurer 2000
 * Copyright D. E. Shaw Research, 2014
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * $Id: //depot/desrad/main/sw/libs/Random123/boost/libs/random/performance/prf_speed.cpp#1 $
 */

#include <iostream>
#include <cstdlib>
#include <string>
#include <boost/config.hpp>
#include <boost/random.hpp>
#include <boost/progress.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/random/philox.hpp>
#include <boost/random/threefry.hpp>
#include <boost/random/counter_based_engine.hpp>

/*
 * Configuration Section
 */

// define if your C library supports the non-standard drand48 family
//#define HAVE_DRAND48

// define if you have the original mt19937int.c (with commented out main())
#undef HAVE_MT19937INT_C

// define if you have the original mt19937ar.c (with commented out main())
// #define HAVE_MT19937AR_C

// set to your CPU frequency
static const double cpu_frequency = 3.07 * 1e9;

using namespace boost::random;

/*
 * End of Configuration Section
 */

/*
 * General portability note:
 * MSVC mis-compiles explicit function template instantiations.
 * For example, f<A>() and f<B>() are both compiled to call f<A>().
 * BCC is unable to implicitly convert a "const char *" to a std::string
 * when using explicit function template instantiations.
 *
 * Therefore, avoid explicit function template instantiations.
 */

// provides an "identity" prf, just for comparison
template <unsigned _N, typename UINT>
struct IdentityPrf{
    static const unsigned Ndomain=_N;
    static const unsigned Nrange=_N;
    static const unsigned Nkey=1;
    static const unsigned range_bits=std::numeric_limits<UINT>::digits*Nrange;
    typedef boost::array<UINT, Ndomain> domain_type;
    typedef boost::array<UINT, Nrange> range_type;
    typedef boost::array<UINT, 1> key_type;
    typedef UINT dvalue_type;
    typedef UINT rvalue_type;
    IdentityPrf(key_type){}
    IdentityPrf(UINT){}

    range_type operator()(domain_type c){ return c; }
};

// start implementation of measuring timing

void show_elapsed(double end, int iter, const std::string & name, size_t bytes_per_iter)
{
  double usec = end/iter*1e6;
  double cycles = usec * cpu_frequency/1e6;
  std::cout << name << ": "
            << usec*1e3 << " nsec/loop = "
            << cycles << " CPU cycles = "
            << cycles/bytes_per_iter << " CPB"
            << std::endl;
}

template<class RNG>
void run(int iter, const std::string & name, RNG rng)
{
  std::cout << (RNG::has_fixed_range ? "fixed-range " : "");
  // BCC has trouble with string autoconversion for explicit specializations

  // make sure we're not optimizing too much
  std::cout << "Engine: ";
  typename RNG::result_type tmp = 0;
  boost::timer t;
  for(int i = 0; i < iter; i++){
      tmp ^= rng();
  }
  show_elapsed(t.elapsed(), iter, name, sizeof(tmp));
  if(tmp==0)
      std::cerr << name << ": The xor is zero.  That's surprising!\n";
}

template <typename Otype, typename Prf>
void  __attribute__((noinline)) run_cbeng(const std::string& name, int iter){
    std::string pfx= "counter_based_engine<" + name + ">";
    // N.B.  Use 24 bits of iter as the key.  Not really necessary, but
    // it prevents the compiler from possibly performing some of the key
    // arithmetic at compile-time.
    run(iter, pfx, counter_based_engine<Otype, Prf>(iter&0xffffff));
}

void do_threefry(int iter){
  // WARNING - if we include the 2x32 tests, then gcc-4.8 reports
  // *much lower* (3x) performance for some of the other functions.
  // Wild guess - we've hit some limit meant to prevent too much
  std::cout << "Threefry: with recommended safety margin\n";
  //run_cbeng<uint64_t, threefry<4, uint64_t> >("threefry4x64", iter);
  //run_cbeng<uint32_t, threefry<4, uint64_t> >("threfry4x64/32", iter);
  //run_cbeng<uint32_t, threefry<2, uint32_t> >("threefry2x32", iter);
  //run_cbeng<uint32_t, threefry<4, uint32_t> >("threefry4x32", iter);
  //run_cbeng<uint64_t, threefry<2, uint64_t> >("threefry2x64", iter);

  std::cout << "Threefry:  Crush-resistant, with no safety margin\n";
  // Note - on a 3.07GHz Xeon 5667 (Westmere) threefry4x64-12 should
  // be the winner at around 1.7 CPB.
  run_cbeng<uint64_t, threefry<4, uint64_t, 12> >("threefry4x64-12", iter);
  run_cbeng<uint32_t, threefry<4, uint64_t, 12> >("threefry4x64-12/32", iter);
  //run_cbeng<uint64_t, threefry<4, uint32_t, 13> >("threefry4x32-13/64", iter);
  //run_cbeng<threefry<4, uint32_t, 12> >("threefry4x32-12", iter);
  //run_cbeng<threefry<2, uint64_t, 13> >("threefry2x64-13", iter);
  //run_cbeng<threefry<2, uint32_t, 13> >("threefry2x32-13", iter);
}

void do_philox(int iter){
  std::cout << "Philox: with recommended safety margin\n";
  run_cbeng<uint64_t, philox<4, uint64_t> >("philox4x64", iter);
  run_cbeng<uint32_t, philox<4, uint32_t> >("philox4x32", iter);
  run_cbeng<uint64_t, philox<2, uint64_t> >("philox2x64", iter);
  run_cbeng<uint32_t, philox<2, uint32_t> >("philox2x32", iter);

  std::cout << "Philox:  Crush-resistant, with no safety margin\n";
  run_cbeng<uint64_t, philox<4, uint64_t, 7> >("philox4x64-7", iter);
  run_cbeng<uint32_t, philox<4, uint32_t, 7> >("philox4x32-7", iter);
  run_cbeng<uint64_t, philox<2, uint64_t, 6> >("philox2x64-6", iter);
  run_cbeng<uint32_t, philox<2, uint32_t, 7> >("philox2x32-7", iter);
}

int main(int argc, char*argv[])
{
  if(argc != 2) {
    std::cerr << "usage: " << argv[0] << " iterations" << std::endl;
    return 1;
  }

  // okay, it's ugly, but it's only used here
  int iter =
#ifndef BOOST_NO_STDC_NAMESPACE
    std::
#endif
    atoi(argv[1]);

  std::cout << "\nPseudo-random functions:\n";
  //run_cbeng<uint64_t, IdentityPrf<2, uint64_t> >("Ident2x64", iter);

  do_threefry(iter);
  do_philox(iter);

  return 0;
}