// Copyright 2015-2020 The ALMA Project Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

/// @file
/// Test if the implementation of regular q-point grids works.

#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/mpi.hpp>
#include "gtest/gtest.h"
#include <vasp_io.hpp>
#include <qpoint_grid.hpp>

TEST(qpoint_grid_case, qpoint_grid_test) {
    boost::mpi::environment env;

    auto basedir = boost::filesystem::path(TEST_RESOURCE_DIR);
    auto si_dir = basedir / boost::filesystem::path("Si");
    auto poscar_path = si_dir / boost::filesystem::path("POSCAR");
    auto ifc_path = si_dir / boost::filesystem::path("FORCE_CONSTANTS");

    auto poscar = alma::load_POSCAR(poscar_path.string().c_str());
    auto syms = alma::Symmetry_operations(*poscar);
    auto force_constants =
        alma::load_FORCE_CONSTANTS(ifc_path.string().c_str(), *poscar, 5, 5, 5);

    auto grid = std::make_unique<alma::Gamma_grid>(
        *poscar, syms, *force_constants, 5, 5, 5);

    grid->enforce_asr();
    EXPECT_EQ(125u, grid->nqpoints);
    EXPECT_EQ(10u, grid->get_nequivalences());
    auto atgamma = grid->get_spectrum_at_q(0);

    for (auto i = 0; i < 3; ++i)
        EXPECT_EQ(0., atgamma.omega(i));

    unsigned int reference[] = {1, 8, 8, 6, 24, 24, 12, 6, 12, 24};

    for (std::size_t i = 0; i < grid->get_nequivalences(); ++i)
        EXPECT_EQ(reference[i], grid->get_cardinal(i));

    grid = std::make_unique<alma::Gamma_grid>(
        *poscar, syms, *force_constants, 5, 5, 3);
    grid->enforce_asr();
    EXPECT_EQ(75u, grid->nqpoints);
    EXPECT_EQ(24u, grid->get_nequivalences());

    grid = std::make_unique<alma::Gamma_grid>(
        *poscar, syms, *force_constants, 8, 5, 3);
    grid->enforce_asr();
    EXPECT_EQ(120u, grid->nqpoints);
    EXPECT_EQ(61u, grid->get_nequivalences());
}


#include <spatialops/SpatialOpsTools.h>
#include <spatialops/OperatorDatabase.h>

#include <spatialops/structured/stencil/FVStaggeredOperatorTypes.h>
#include <spatialops/structured/stencil/OneSidedOperatorTypes.h>
#include <spatialops/structured/stencil/StencilBuilder.h>
#include <spatialops/NeboStencilBuilder.h>
#include <spatialops/structured/FieldComparisons.h>
#include <spatialops/structured/Grid.h>
#include <spatialops/Nebo.h>

#include <test/TestHelper.h>
#include <spatialops/structured/FieldHelper.h>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

using namespace SpatialOps;

#include <stdexcept>
using std::cout;
using std::endl;

#include <algorithm>
#include <functional>
#include <type_traits>
#include <vector>

typedef SVolField FieldT;

#define STATEMENT_COUNT 1

//#define COMPILE_PLUS_STATEMENT
//#define COMPILE_VARYING_STENCIL
//#define COMPILE_NINE_POINT_STENCIL
//#define COMPILE_FOUR_POINT_STENCIL
//#define COMPILE_TWO_POINT_STENCIL
//#define COMPILE_REDUCTION_SUM

//--------------------------------------------------------------------

#if defined(COMPILE_NINE_POINT_STENCIL) || defined(COMPILE_VARYING_STENCIL) || defined(COMPILE_FOUR_POINT_STENCIL)
#define CUSTOM_STENCIL_COMPILATION
#endif
#if defined(COMPILE_TWO_POINT_STENCIL)
#define TRADITIONAL_STENCIL_COMPILATION
#endif

//--------------------------------------------------------------------

#define MACRO_CAT(A, B) A##B

#define OP_PAREN(OP, TEXT) (TEXT)
#define OP_BETWEEN(BETWEEN, TEXT) BETWEEN TEXT

#define REPEAT_EXPRESSION_SUMMED(N, EXPRESSION, ARG) MACRO_CAT(REPEAT_EXPRESSION_,N)(+, OP_BETWEEN, EXPRESSION OP_PAREN(,ARG), EXPRESSION OP_PAREN(,ARG))
#define REPEAT_EXPRESSION_NESTED(N, EXPRESSION, ARG) MACRO_CAT(REPEAT_EXPRESSION_,N)(, OP_PAREN, EXPRESSION, ARG)
#define REPEAT_EXPRESSION_OP(N, BETWEEN, ARG) MACRO_CAT(REPEAT_EXPRESSION_,N)(BETWEEN, OP_BETWEEN, ARG, ARG)

#define REPEAT_EXPRESSION_1(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, ARG)
#define REPEAT_EXPRESSION_2(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_1(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_3(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_2(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_4(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_3(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_5(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_4(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_6(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_5(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_7(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_6(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_8(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_7(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_9(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_8(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_10(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_9(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_11(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_10(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_12(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_11(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_13(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_12(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_14(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_13(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_15(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_14(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_16(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_15(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_17(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_16(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_18(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_17(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_19(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_18(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_20(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_19(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_21(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_20(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_22(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_21(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_23(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_22(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_24(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_23(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_25(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_24(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_26(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_25(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_27(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_26(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_28(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_27(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_29(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_28(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_30(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_29(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_31(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_30(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_32(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_31(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_33(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_32(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_34(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_33(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_35(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_34(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_36(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_35(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_37(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_36(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_38(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_37(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_39(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_38(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_40(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_39(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_41(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_40(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_42(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_41(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_43(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_42(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_44(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_43(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_45(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_44(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_46(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_45(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_47(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_46(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_48(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_47(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_49(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_48(BETWEEN, OP, EXPRESSION, ARG) )
#define REPEAT_EXPRESSION_50(BETWEEN, OP, EXPRESSION, ARG) EXPRESSION OP(BETWEEN, REPEAT_EXPRESSION_49(BETWEEN, OP, EXPRESSION, ARG) )

//--------------------------------------------------------------------

namespace
{
#ifdef CUSTOM_STENCIL_COMPILATION
  typedef XDIR DirT;
  typedef UnitTriplet<DirT>::type DirTripletT;
  typedef Multiply<IndexTriplet<2, 2, 2>, DirTripletT>::result TwoDirTripletT;
  typedef Multiply<IndexTriplet<3, 3, 3>, DirTripletT>::result ThreeDirTripletT;
  typedef Multiply<IndexTriplet<4, 4, 4>, DirTripletT>::result FourDirTripletT;
#endif
#ifdef CUSTOM_STENCIL_COMPILATION
  //Main Point
  namespace
  {
    typedef NEBO_FIRST_IJK( 0, 0, 0)
          ::NEBO_ADD_POINT  ( DirTripletT )
          ::NEBO_ADD_POINT  ( TwoDirTripletT )
          ::NEBO_ADD_POINT  ( ThreeDirTripletT )
          ::NEBO_ADD_POINT  ( FourDirTripletT )
          ::NEBO_ADD_POINT  ( DirTripletT::Negate )
          ::NEBO_ADD_POINT  ( TwoDirTripletT::Negate )
          ::NEBO_ADD_POINT  ( ThreeDirTripletT::Negate )
          ::NEBO_ADD_POINT  ( FourDirTripletT::Negate )
      MainPointStencilCollectionT;
    typedef NeboStencilBuilder<Gradient,
                               MainPointStencilCollectionT,
                               FieldT,
                               FieldT>
            MainPointStencilT;
    const NeboStencilCoefCollection<MainPointStencilCollectionT::length> mainCoefs
      = build_coef_collection(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0);
  }
#endif
#ifdef COMPILE_VARYING_STENCIL
  //One Point
  namespace
  {
    //Positive
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
        OnePointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   OnePointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicOneT;
        const NeboStencilCoefCollection<OnePointPositiveStencilCollectionT::length> pCoefOne
          = build_coef_collection(1.0);
    }
    //Negative
    namespace
    {
      typedef OnePointPositiveStencilCollectionT OnePointNegativeStencilCollectionT;
      typedef PBasicOneT NBasicOneT;
      const NeboStencilCoefCollection<OnePointPositiveStencilCollectionT::length> nCoefOne
        = pCoefOne;
    }
  }
  //Two Point One Sided
  namespace
  {
    //Two Point Positive
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
            ::NEBO_ADD_POINT  ( DirTripletT )
            //::NEBO_ADD_POINT  ( TwoDirTripletT )
        TwoPointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   TwoPointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicTwoT;
        const NeboStencilCoefCollection<TwoPointPositiveStencilCollectionT::length> pCoefTwo
          = build_coef_collection(1.0)(1.0);
    }
    //Two Point Negative
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
            ::NEBO_ADD_POINT  ( DirTripletT::Negate )
            //::NEBO_ADD_POINT  ( TwoDirTripletT::Negate )
        TwoPointNegativeStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   TwoPointNegativeStencilCollectionT,
                                   FieldT,
                                   FieldT>
                NBasicTwoT;
        const NeboStencilCoefCollection<TwoPointPositiveStencilCollectionT::length> nCoefTwo
          = pCoefTwo;
    }
  }
  //Three Point Centered
  namespace
  {
    //Point Positive
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
            ::NEBO_ADD_POINT  ( DirTripletT )
            ::NEBO_ADD_POINT  ( DirTripletT::Negate )
        ThreePointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   ThreePointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicThreeT;
        const NeboStencilCoefCollection<ThreePointPositiveStencilCollectionT::length> pCoefThree
          = build_coef_collection(1.0)(1.0)(1.0);
    }
    //Negative
    namespace
    {
      typedef ThreePointPositiveStencilCollectionT ThreePointNegativeStencilCollectionT;
      typedef PBasicThreeT NBasicThreeT;
        const NeboStencilCoefCollection<ThreePointPositiveStencilCollectionT::length> nCoefThree
          = pCoefThree;
    }
  }
  //Four Point Slightly Centered
  namespace
  {
#endif /* COMPILE_VARYING_STENCIL */
#if defined(COMPILE_VARYING_STENCIL) || defined(COMPILE_FOUR_POINT_STENCIL)
    //Point Positive
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
            ::NEBO_ADD_POINT  ( DirTripletT )
            ::NEBO_ADD_POINT  ( TwoDirTripletT )
            ::NEBO_ADD_POINT  ( DirTripletT::Negate )
        FourPointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   FourPointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicFourT;
        const NeboStencilCoefCollection<FourPointPositiveStencilCollectionT::length> pCoefFour
          = build_coef_collection(1.0)(1.0)(1.0)(1.0);
    }
#endif /* COMPILE_VARYING_STENCIL || COMPILE_FOUR_POINT_STENCIL */
#ifdef COMPILE_VARYING_STENCIL
    //Negative
    namespace
    {
      typedef NEBO_FIRST_IJK( 0, 0, 0)
            ::NEBO_ADD_POINT  ( DirTripletT::Negate )
            ::NEBO_ADD_POINT  ( TwoDirTripletT::Negate )
            ::NEBO_ADD_POINT  ( DirTripletT )
        FourPointNegativeStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   FourPointNegativeStencilCollectionT,
                                   FieldT,
                                   FieldT>
                NBasicFourT;
        const NeboStencilCoefCollection<FourPointPositiveStencilCollectionT::length> nCoefFour
          = pCoefFour;
    }
  }

  typedef NeboGenericEmptyTypeList::AddType<NBasicOneT>::Result
                                  ::AddType<NBasicTwoT>::Result
                                  ::AddType<NBasicThreeT>::Result
                                  ::AddType<NBasicFourT>::Result
    NegativeStencilListT;

  typedef NeboGenericEmptyTypeList::AddType<PBasicOneT>::Result
                                  ::AddType<PBasicTwoT>::Result
                                  ::AddType<PBasicThreeT>::Result
                                  ::AddType<PBasicFourT>::Result
    PositiveStencilListT;

  typedef NeboVaryingEdgeStencilBuilder<DirT,
                                        FieldT,
                                        FieldT,
                                        MainPointStencilT,
                                        NegativeStencilListT,
                                        PositiveStencilListT>
          VaryEdgeOpT;
#endif /* COMPILE_VARYING_STENCIL */
#ifdef CUSTOM_STENCIL_COMPILATION
  void build_varying_stencils( const unsigned int nx,
                               const unsigned int ny,
                               const unsigned int nz,
                               const double Lx,
                               const double Ly,
                               const double Lz,
                               OperatorDatabase& opdb )
  {
    //Create main five point stencil
    {
      opdb.register_new_operator( new MainPointStencilT(mainCoefs) );
    }

#ifdef COMPILE_VARYING_STENCIL
    //Create one point stencils
    {
      opdb.register_new_operator( new PBasicOneT(pCoefOne) ); //Same type as negative
    }

    //Create two point stencils
    {
      opdb.register_new_operator( new PBasicTwoT(pCoefTwo) );
      opdb.register_new_operator( new NBasicTwoT(nCoefTwo) );
    }

    //Create three point stencils
    {
      opdb.register_new_operator( new PBasicThreeT(pCoefThree) ); //Same type as negative
    }

    //Create four point stencils
    {
      opdb.register_new_operator( new PBasicFourT(pCoefFour) );
      opdb.register_new_operator( new NBasicFourT(nCoefFour) );
    }

    {
      auto& mainStencil   = *opdb.retrieve_operator<MainPointStencilT>();
      auto& pOnePointStencil = *opdb.retrieve_operator<PBasicOneT>();
      auto& nOnePointStencil = *opdb.retrieve_operator<NBasicOneT>();
      auto& pTwoPointStencil = *opdb.retrieve_operator<PBasicTwoT>();
      auto& nTwoPointStencil = *opdb.retrieve_operator<NBasicTwoT>();
      auto& pThreePointStencil = *opdb.retrieve_operator<PBasicThreeT>();
      auto& nThreePointStencil = *opdb.retrieve_operator<NBasicThreeT>();
      auto& pFourPointStencil = *opdb.retrieve_operator<PBasicFourT>();
      auto& nFourPointStencil = *opdb.retrieve_operator<NBasicFourT>();
      opdb.register_new_operator( new VaryEdgeOpT (mainStencil,
                                                   NeboGenericEmptyTypeList()(nOnePointStencil)
                                                                             (nTwoPointStencil)
                                                                             (nThreePointStencil)
                                                                             (nFourPointStencil),
                                                   NeboGenericEmptyTypeList()(pOnePointStencil)
                                                                             (pTwoPointStencil)
                                                                             (pThreePointStencil)
                                                                             (pFourPointStencil)) );
    }
#endif /* COMPILE_VARYING_STENCIL */
  }

  void build_varying_stencils( const Grid& grid, OperatorDatabase& opDB )
  {
    build_stencils( grid.extent(0), grid.extent(1), grid.extent(2),
                    grid.length(0), grid.length(1), grid.length(2),
                    opDB );
  }
#endif /* CUSTOM_STENCIL_COMPILATION */
}

//--------------------------------------------------------------------

#ifdef COMPILE_PLUS_STATEMENT
bool Plus( IntVec i_npts )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_OP(STATEMENT_COUNT, +, x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_PLUS_STATEMENT */

#ifdef COMPILE_VARYING_STENCIL
bool VaryingStencil( IntVec i_npts, OperatorDatabase const & i_opdb, Grid const & i_grid )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  //const IntVec bcMinus(true, false, false);
  //const IntVec bcPlus(true, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  // set field values
  i_grid.set_coord<XDIR>(x);

  VaryEdgeOpT& op = *i_opdb.retrieve_operator<VaryEdgeOpT>();
  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_NESTED(STATEMENT_COUNT, op.operator(), x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_VARYING_STENCIL */

#ifdef COMPILE_NINE_POINT_STENCIL
bool NinePointStencil( IntVec i_npts, OperatorDatabase const & i_opdb, Grid const & i_grid )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  // set field values
  i_grid.set_coord<XDIR>(x);

  MainPointStencilT& op = *i_opdb.retrieve_operator<MainPointStencilT>();
  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_NESTED(STATEMENT_COUNT, op.operator(), x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_NINE_POINT_STENCIL */
#ifdef COMPILE_FOUR_POINT_STENCIL
bool FourPointStencil( IntVec i_npts, OperatorDatabase const & i_opdb, Grid const & i_grid )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  // set field values
  i_grid.set_coord<XDIR>(x);

  PBasicFourT& op = *i_opdb.retrieve_operator<PBasicFourT>();
  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_NESTED(STATEMENT_COUNT, op.operator(), x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_FOUR_POINT_STENCIL */
#ifdef COMPILE_TWO_POINT_STENCIL
bool TwoPointStencil( IntVec i_npts, OperatorDatabase const & i_opdb, Grid const & i_grid )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  // set field values
  i_grid.set_coord<XDIR>(x);

  typedef OperatorTypeBuilder<GradientX, FieldT, FieldT>::type GradX;
  GradX& op = *i_opdb.retrieve_operator<GradX>();
  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_NESTED(STATEMENT_COUNT, op.operator(), x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_TWO_POINT_STENCIL */

#ifdef COMPILE_REDUCTION_SUM
bool ReductionSum( IntVec i_npts )
{
  const GhostData ghost(0);

  const IntVec bcMinus(false, false, false);
  const IntVec bcPlus(false, false, false);
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(bcMinus, bcPlus);
  const MemoryWindow mw = get_window_with_ghost(i_npts, ghost, bcinfo);

  FieldT x     ( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );
  FieldT result( mw, bcinfo, ghost, NULL, InternalStorage, CPU_INDEX );

  x <<= 1;
  result <<= 0;
  //Timed code
  {
    result <<= REPEAT_EXPRESSION_SUMMED(STATEMENT_COUNT, nebo_sum, x);
  }
  {
    print_field(result, std::cout);
  }

  return true;
}
#endif /* COMPILE_REDUCTION_SUM */

//--------------------------------------------------------------------

int main( int iarg, char* carg[] )
{
  int nx, ny, nz;
  double length;
  {
    po::options_description desc("Supported Options");
    desc.add_options()
          ( "help", "print help message\n" )
          ( "nx", po::value<int>   ( &nx     )->default_value( 32   ), "number of points in x-dir for base mesh" )
          ( "ny", po::value<int>   ( &ny     )->default_value( 32   ), "number of points in y-dir for base mesh" )
          ( "nz", po::value<int>   ( &nz     )->default_value( 32   ), "number of points in z-dir for base mesh" )
          ( "l",  po::value<double>( &length )->default_value( 0.01 ), "length of the domain"                    );

    po::variables_map args;
    po::store( po::parse_command_line(iarg,carg,desc), args );
    po::notify(args);

    if( args.count("help") ){
      cout << desc << endl
          << "Example:" << endl
          << "  test_varying_edge_stencil --nx 5 --ny 10 --nz 3 " << endl
          << endl;
      return -1;
    }

  }

  TestHelper status( true );
  const IntVec npts(nx,ny,nz);

  {
    cout << "  domain : " << npts << endl
         << endl;
  }

#ifdef TRADITIONAL_STENCIL_COMPILATION
  OperatorDatabase opdb1;
  build_stencils        ( npts[0], npts[1], npts[2], length, length, length, opdb1 );
#endif
#ifdef CUSTOM_STENCIL_COMPILATION
  OperatorDatabase opdb2;
  build_varying_stencils( npts[0], npts[1], npts[2], length, length, length, opdb2 );
#endif
#if defined(CUSTOM_STENCIL_COMPILATION) || defined(TRADITIONAL_STENCIL_COMPILATION)
  const Grid grid( npts, DoubleVec(length, length, length) );
#endif

#ifdef COMPILE_PLUS_STATEMENT
  status( Plus(npts) );
#endif
#ifdef COMPILE_VARYING_STENCIL
  status( VaryingStencil(npts, opdb2, grid) );
#endif
#ifdef COMPILE_NINE_POINT_STENCIL
  status( NinePointStencil(npts, opdb2, grid) );
#endif
#ifdef COMPILE_FOUR_POINT_STENCIL
  status( FourPointStencil(npts, opdb2, grid) );
#endif
#ifdef COMPILE_TWO_POINT_STENCIL
  status( TwoPointStencil(npts, opdb1, grid) );
#endif
#ifdef COMPILE_REDUCTION_SUM
  status( ReductionSum(npts) );
#endif

  if( status.ok() ){
    cout << "Tests passed" << endl;
    return 0;
  }
  else {
    cout << "******************************" << endl
        << "At least one test did not pass" << endl
        << "******************************" << endl;
    return -1;
  }
}

//--------------------------------------------------------------------


/*=============================================================================

  MYPROJECT: A software package for whatever.

  Copyright (c) University College London (UCL). All rights reserved.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.

  See LICENSE.txt in the top level directory for details.

=============================================================================*/

#include <mpMyFunctions.h>
#include <iostream>

#ifdef BUILD_Eigen
#include <Eigen/Dense>
#endif

#ifdef BUILD_Boost
#include <boost/math/special_functions/round.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/path.hpp>
#endif

#ifdef BUILD_OpenCV
#include <cv.h>
#endif

/**
 * \brief Demo file to check that #includes and library linkage is correct.
 */
int main(int argc, char** argv)
{

#ifdef BUILD_Eigen
  Eigen::MatrixXd m(2,2);
  std::cout << "Printing 2x2 matrix ..." << m << std::endl;
#endif

#ifdef BUILD_Boost
  std::cout << "Rounding to ... " << boost::math::round(0.123) << std::endl;
  boost::posix_time::ptime startTime = boost::posix_time::second_clock::local_time();
  boost::filesystem::path pathname( "/tmp/tmp.txt" );
#endif

#ifdef BUILD_OpenCV
  cv::Matx44d matrix = cv::Matx44d::eye();
  std::cout << "Printing 4x4 matrix ..." << matrix << std::endl;
#endif

  std::cout << "Calculating ... " << mp::MyFirstFunction(1) << std::endl;
  return 0;
}


/**
 * @example types/test/sequence_test.cc
 */
#include <boost/test/unit_test.hpp>
#include <usml/types/types.h>
#include <iostream>
#include <boost/foreach.hpp>
#include <cstdlib>
#include <iomanip>

BOOST_AUTO_TEST_SUITE(sequence_test)

using namespace boost::unit_test;
using namespace usml::types;

/**
 * @ingroup types_test
 * @{
 */

/**
 * Basic tests for a linearly spaced sequence of numbers.
 *
 *   - test (min,inc,max) form of constructor
 *   - test sequence use as a uBLAS vector
 *   - test simple accessors:
 *          use operator() form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments
 *   - test conversion into a uBLAS vector
 *   - test iterator looping
 *   - separate dereferencing and pre-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_linear_test1 ) {

    cout << "=== sequence_test: sequence_linear_test1() ===" << endl;

    // test (min,inc,max) form of contructor
    // test sequence use as a uBLAS vector
    seq_linear seq( 1.0, 2.0, 15.0 );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator() form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 8u );
    cout << "increment(3)       => " << seq.increment(3) << endl;
    BOOST_CHECK_CLOSE( seq.increment(3), 2.0, 1e-6 );
    cout << "seq(3)             => " << seq(3) << endl;
    BOOST_CHECK_CLOSE( seq(3), 7.0, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    cout << "find_index(-2)     => " << seq.find_index(-2) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(-2), 0u );
    cout << "find_index(11.5)   => " << seq.find_index(11.5) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(11.5), 5u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 6u );
    cout << "find_index(100.0)  => " << seq.find_index(100.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(100.0), 6u );

    // test iterator looping
    // separate dereferencing and pre-increment operations
    seq_linear::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current << " ";
        ++current;
    }
    cout << endl;

    // test conversion into a uBLAS vector
    vector<double> vect;
    vect = seq;
    cout << "vect               => " << vect << endl;
}

/**
 * Alternate tests for a linearly spaced sequence of numbers.
 *
 *   - test (min,inc,number) form of constructor
 *   - test sequence use as a uBLAS vector
 *   - test simple accessors:
 *          use operator[] form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_linear_test2 ) {

    cout << "=== sequence_test: sequence_linear_test2() ===" << endl;

    // test (min,inc,number) form of contructor
    // test use as a uBLAS vector
    seq_linear seq( 1.0, 3.5, 8 );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator[] form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 8u );
    cout << "increment(3)       => " << seq.increment(3) << endl;
    BOOST_CHECK_CLOSE( seq.increment(3), 3.5, 1e-6 );
    cout << "seq[3]             => " << seq[3] << endl;
    BOOST_CHECK_CLOSE( seq[3], 11.5, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    cout << "find_index(-2)     => " << seq.find_index(-2) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(-2), 0u );
    cout << "find_index(11.5)   => " << seq.find_index(11.5) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(11.5), 3u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 3u );
    cout << "find_index(100.0)  => " << seq.find_index(100.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(100.0), 6u );

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_linear::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Basic tests for a logarithmically spaced sequence of numbers.
 *
 *   - test (min,inc,number) form of constructor (third octave bands)
 *   - test sequence use as a uBLAS vector
 *   - test simple accessors:
 *          use operator[] form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments,
 *          note problem when searching exactly on element
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent, except for
 * the increment test which is only required to be within 1E-4 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_log_test ) {

    cout << "=== sequence_test: sequence_log_test ===" << endl;

    // test (min,inc,number) form of contructor (third octave bands)
    // test use as a uBLAS vector
    seq_log seq( 10.0, std::pow(2.0,1.0/3.0), 10 );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator[] form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 10u );
    cout << "increment(3)       => " << seq.increment(3) << endl;
    BOOST_CHECK_CLOSE( seq.increment(3), 5.19842, 1e-4 );
    cout << "seq[3]             => " << seq[3] << endl;
    BOOST_CHECK_CLOSE( seq[3], 20.0, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    // note problem when searching exactly on element
    cout << "find_index(-2)     => " << seq.find_index(-2) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(-2), 0u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 1u );
    cout << "find_index(20.0)   => " << seq.find_index(20.0) << endl;
    cout << "find_index(20.0+e) => " << seq.find_index(20.0+1e-10) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(20.0+1e-10), 3u );
    cout << "find_index(1000.0) => " << seq.find_index(1000.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(1000.0), 8u );

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_log::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Basic test for a sequence defined by an unevenly spaced vector of points.
 *
 *   - test data input form of constructor
 *   - test sequence use as a uBLAS vector
 *   - test simple accessors:
 *          use operator[] form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments,
 *          note problem when searching exactly on element
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent, except for
 * the increment test which is only required to be within 1e-4 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_data_test1 ) {

    cout << "=== sequence_test: sequence_data_test1() ===" << endl;

    // test data input form of contructor
    // test use as a uBLAS vector
    int N = 10;
    double data[] = {10.0,12.5,16.0,20.0,25.0,32.0,40.0,50.0,63.5,80.0};
    vector<double> vect(N);
    std::copy( data, data+N, vect.begin() );

//    seq_data seq( vect-0.1 );
    seq_data seq( vect );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator[] form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 10u );
    cout << "increment(3)       => " << seq.increment(3) << endl;
    BOOST_CHECK_CLOSE( seq.increment(3), 5.0, 1e-4 );
    cout << "seq[3]             => " << seq[3] << endl;
    BOOST_CHECK_CLOSE( seq[3], 20.0, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    // note problem when searching exactly on element
    cout << "find_index(-2)     => " << seq.find_index(-2) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(-2), 0u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 1u );
    cout << "find_index(20.0)   => " << seq.find_index(20.0) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(20.0), 3u );
    cout << "find_index(1000.0) => " << seq.find_index(1000.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(1000.0), 8u );

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_data::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Alternate test for a sequence defined by an unevenly spaced vector of points.
 *
 *   - test decreasing sequence form of constructor
 *   - test sequence use as a uBLAS vector
 *   - test simple accessors:
 *          use operator[] form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments,
 *          note problem when searching exactly on element
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent, except for
 * the increment test which is only required to be within 1e-4 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_data_test2 ) {

    cout << "=== sequence_test: sequence_data_test2() ===" << endl;

    // test decreasing sequence form of contructor
    // test use as a uBLAS vector
    int N = 10;
    double data[] = {80.0,63.5,50.0,40.0,32.0,25.0,20.0,16.0,12.5,10.0};
    vector<double> vect(N);
    std::copy( data, data+N, vect.begin() );

    seq_data seq( vect );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator[] form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 10u );
    cout << "increment(3)       => " << seq.increment(3) << endl;
    BOOST_CHECK_CLOSE( seq.increment(3), -8.0, 1e-4 );
    cout << "seq[3]             => " << seq[3] << endl;
    BOOST_CHECK_CLOSE( seq[3], 40.0, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    // note problem when searching exactly on element
    cout << "find_index(-2)     => " << seq.find_index(-2) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(-2), 8u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 7u );
    cout << "find_index(20.0)   => " << seq.find_index(20.0) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(20.0), 6u );
    cout << "find_index(1000.0) => " << seq.find_index(1000.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(1000.0), 0u );

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_data::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Test for a sequence defined by a single point.
 *
 *   - test data set size of one
 *   - test simple accessors:
 *          use operator[] form of element access
 *   - test find_index() methods:
 *          less than min, between elements, on element, greater than max,
 *          mix of float and int arguments,
 *          note problem when searching exactly on element
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent, except for
 * the increment test which is only required to be within 1e-4 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_data_test3 ) {

    cout << "=== sequence_test: sequence_data_test3() ===" << endl;

    // test decreasing sequence form of constructor
    // test use as a uBLAS vector
    int N = 1;
    double data[] = {123.5};
    vector<double> vect(N);
    std::copy( data, data+N, vect.begin() );

    seq_data seq( vect );
    cout << "seq                => " << seq << endl;

    // test simple accessors
    // use operator[] form of element access
    cout << "size               => " << seq.size() << endl;
    BOOST_CHECK_EQUAL( seq.size(), 1u );
    cout << "increment(0)       => " << seq.increment(0) << endl;
    BOOST_CHECK_CLOSE( seq.increment(0), 0.0, 1e-4 );
    cout << "seq[0]             => " << seq[0] << endl;
    BOOST_CHECK_CLOSE( seq[0], 123.5, 1e-6 );

    // test find_index() methods
    // less than min, between elements, on element, greater than max
    // mix of float and int arguments
    // note problem when searching exactly on element
    cout << "find_index(123.5)  => " << seq.find_index(123.5) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(123.5), 0u );
    cout << "find_index(13)     => " << seq.find_index(13) << endl;
    BOOST_CHECK_EQUAL( seq.find_index(13), 0u );
    cout << "find_index(1000.0) => " << seq.find_index(1000.0) <<endl;
    BOOST_CHECK_EQUAL( seq.find_index(1000.0), 0u );

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_data::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Basic tests for a gaussian spaced sequence of angles.
 *
 *   - test (min,max,number) form of constructor (7 beams)
 *   - test sequence use as a uBLAS vector
 *   - test iterator looping:
 *          integrate dereferencing and post-increment operations
 *
 * Generate errors if values differ by more that 1E-6 percent, except for
 * the increment test which is only required to be within 1e-4 percent.
 */
BOOST_AUTO_TEST_CASE( sequence_rayfan_test ) {

    cout << "=== sequence_test: sequence_rayfan_test ===" << endl;

    // test (min,max,number) form of contructor
    // test use as a uBLAS vector
    seq_rayfan seq( -90.0, 90.0, 7, 10.0 );
    cout << "seq                => " << seq << endl;

    // test iterator looping
    // integrate dereferencing and post-increment operations
    seq_log::iterator current = seq.begin();
    cout << "iterator           => ";
    while ( current != seq.end() ) {
        cout << *current++ << " ";
    }
    cout << endl;
}

/**
 * Tests the functionality of BOOST_FOREACH with all
 * sequence classes. Because of the inheritance chain
 * of seq_vectors, boost was unable to deduce the type
 * of iterator that was appropriate for the seq_vector
 * classes provided. Creating specialized template typedefs
 * for each subclass of seq_vector resolves this issue.
 */
BOOST_AUTO_TEST_CASE( sequence_foreach_test ) {

    using namespace boost ;

    cout << "=== sequence_test: sequence_foreach_test ===" << endl ;
    bool complete = false ;
    size_t N = 5 ;

    // Tests foreach for rayfan
    seq_rayfan fan( -5.0, 5.0, N ) ;
    cout << "seq_rayfan: " << fan << endl ;
    cout << "using foreach: " ;
    BOOST_FOREACH( double i, fan )
        complete = false, cout << i << " ", complete = true ;
    cout << endl ;
    BOOST_CHECK( complete ) ;

    // Tests foreach for linear
    seq_linear line( -5.0, 5.0, N ) ;
    cout << "seq_linear: " << line << endl ;
    cout << "using foreach: " ;
    BOOST_FOREACH( double i, line )
        complete = false, cout << i << " ", complete = true ;
    cout << endl ;
    BOOST_CHECK( complete ) ;

    // Tests foreach for log
    seq_log log( 1.0, 5.0, N ) ;
    cout << "seq_log: " << log << endl ;
    cout << "using foreach: " ;
    BOOST_FOREACH( double i, log )
        complete = false, cout << i << " ", complete = true ;
    cout << endl ;
    BOOST_CHECK( complete ) ;

    // Tests foreach for data
    std::srand( 1 ) ;
    double* sample = new double[N] ;
    for(size_t i=0; i<N; ++i)
        sample[i] = ((double)std::rand() / RAND_MAX) * 100.0 ;
    std::sort( sample, sample+N ) ;
    seq_data data( sample, N ) ;
    cout << "seq_data: " << data << endl ;
    cout << "using foreach: " ;
    BOOST_FOREACH( double i, data )
        complete = false, cout << i << " ", complete = true ;
    cout << endl ;
    BOOST_CHECK( complete ) ;
    delete[] sample ;
}

/**
 * Tests the uBLas capabilities of seq_vectors
 */
BOOST_AUTO_TEST_CASE( seq_ublas_test ) {
    cout << "=== sequence_test/seq_ublas_test ===" << endl ;

    // Tests the use of seq_linear
    seq_linear line( -5.0, 1.0, 5.0 ) ;
    size_t n( line.size() ) ;
    vector<double> result(n) ;
    noalias(result) = 6.0 * line ;
    size_t count = 0 ;
    BOOST_FOREACH( double i, result )
        BOOST_CHECK_EQUAL( i, 6.0*line(count++) ) ;
    cout << "result_linear: " << result << endl ;

    // Tests the use of seq_log
    seq_log log( 1.0, 10.0, n ) ;
    result = 2.0 * log10( log ) ;
    count = 0 ;
    BOOST_FOREACH( double i, result )
        BOOST_CHECK_EQUAL( i, 2.0*log10(log(count++)) ) ;
    cout << "result_log: " << result << endl ;

    // Tests the use of seq_rayfan
    seq_rayfan fan( -90.0, 90.0, n ) ;
    result = atan( fan ) / 2.0 ;
    count = 0 ;
    BOOST_FOREACH( double i, result )
        BOOST_CHECK_EQUAL( i, (atan( fan(count++) ) / 2.0) ) ;
    cout << "result_rayfan: " << result << endl ;

    // Tests the use of seq_data
    std::srand(1) ;
    double* d = new double[n] ;
    for(size_t i=0; i<n; ++i)
        d[i] = ((double)std::rand() / RAND_MAX ) * 100.0 ;
    std::sort( d, d+n ) ;
    seq_data sdata( d, n ) ;
    result = element_div(element_prod(exp(sdata), line), log) ;
    count = 0 ;
    double tmp = 0 ;
    BOOST_FOREACH( double i, result )
    {
        tmp = exp(sdata(count)) * line(count) / log(count) ;
        ++count ;
        BOOST_CHECK_EQUAL( i, tmp ) ;
    }
    cout << "result_data: " << result << endl ;
    delete[] d ;
}

/**
 * Tests the seq_vector::clip method
 * Test fails if any the clipped values are
 * not equal to the predetermined correct values.
 */
BOOST_AUTO_TEST_CASE( seq_vector_clip_test ) {
    cout << "=== sequence_test/seq_vector_clip_test ===" << endl ;

     // 6.5K, 7.5K, 8.5K, 9.5K
    seq_linear values(6500.0, 1000.0, 4);

    cout << "original values: " << values << endl ;

    // Max Clip
    seq_vector* clipped_values_one = values.clip(5000.0, 9000.0);

    cout << "after max clip :  " <<  *clipped_values_one << endl ;
    BOOST_CHECK_EQUAL( clipped_values_one->size(), 3 ) ;

    double test_value = (*clipped_values_one)(0);
    BOOST_CHECK_EQUAL( test_value, 6500 ) ;
    test_value = (*clipped_values_one)(1);
    BOOST_CHECK_EQUAL( test_value, 7500 ) ;
    test_value = (*clipped_values_one)(2);
    BOOST_CHECK_EQUAL( test_value, 8500 ) ;
    delete clipped_values_one;

    // Min clip
    seq_vector* clipped_values_two = values.clip(7000.0, 11000.0);

    cout << "after min clip:  " << *clipped_values_two << endl ;
    BOOST_CHECK_EQUAL( clipped_values_two->size(), 3 ) ;

    test_value = (*clipped_values_two)(0);
    BOOST_CHECK_EQUAL( test_value, 7500 ) ;
    test_value = (*clipped_values_two)(1);
    BOOST_CHECK_EQUAL( test_value, 8500 ) ;
    test_value = (*clipped_values_two)(2);
    BOOST_CHECK_EQUAL( test_value, 9500 ) ;
    delete clipped_values_two;

    // Max and Min clip
    seq_vector* clipped_values_three = values.clip(7000.0, 9000.0);

    cout << "after max and min clip:  " << *clipped_values_three << endl ;
    BOOST_CHECK_EQUAL( clipped_values_three->size(), 2 ) ;

    test_value = (*clipped_values_three)(0);
    BOOST_CHECK_EQUAL( test_value, 7500 ) ;
    test_value = (*clipped_values_three)(1);
    BOOST_CHECK_EQUAL( test_value, 8500 ) ;
    delete clipped_values_three;
}

/**
 * Tests the implementation of seq_augment
 * Test fails if any values are unequal to the
 * predetermined truth vector.
 */
BOOST_AUTO_TEST_CASE( seq_augment_test ) {
    cout << "=== sequence_test/seq_augment_test ===" << endl ;

    //seq_linear origin( 6.0, 1.0, 5 ) ;
    seq_rayfan origin(6.0, 10.0, 5);
    size_t N = 6 ;

    //double tmp[] = { 6.0, 6.25, 6.5, 6.75, 7.0, 8.0, 9.0, 9.25, 9.5, 9.75, 10.0 } ;
    double tmp[]= { 6.0,6.19598,6.39196,6.58795,6.78393,7.68466,8.73893,8.93492,9.1309,9.32688,10.0 };
    size_t size( sizeof(tmp)/sizeof(double) ) ;
    vector<double> data( size, 0.0 ) ;
    size_t index = 0 ;
    BOOST_FOREACH( double& i, data )
        i = tmp[index++] ;
    seq_data truth( data ) ;

    seq_augment aug( &origin, N) ;
    cout << std::setprecision(8);
    cout << "origin: " << origin << endl ;
    cout << "augment: " << aug << endl ;
    index = 0 ;
    BOOST_FOREACH( double i, aug )
        BOOST_CHECK_CLOSE( i, tmp[index++], 0.0001 ) ;
}

/**
 * Tests the implementation of seq_vector operator==
 * Test fails if equal seq_vector's are not found false, or
 * unequal seq_vector's are found true.
 */
BOOST_AUTO_TEST_CASE( seq_vector_equal_test ) {
    cout << "=== sequence_test/seq_vector_equal_test ===" << endl ;

    seq_linear linear( 6.0, 1.0, 5 ) ;

    seq_rayfan rayfan_five( 6.0, 10.0, 5);

    if (linear == rayfan_five) {
        BOOST_FAIL("seq_vector_compare_test are not equal ");
    }

    seq_rayfan rayfan_six( 6.0, 10.0, 6);

    if (rayfan_five == rayfan_six) {
        BOOST_FAIL("seq_vector_compare_test are not equal ");
    }

    // Same should be equal
    seq_rayfan rayfan( 6.0, 10.0, 6);

    if (rayfan != rayfan_six) {
        BOOST_FAIL("seq_vector_compare_test are equal ");
    }
}

BOOST_AUTO_TEST_SUITE_END()


/*=============================================================================
    Copyright (c) 2001-2010 Joel de Guzman

    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)
=============================================================================*/
#include <boost/detail/lightweight_test.hpp>
#include <boost/config/warning_disable.hpp>

#include <input/parse_sexpr_impl.hpp>
#include <scheme/compiler.hpp>
#include <iostream>
#include <fstream>

///////////////////////////////////////////////////////////////////////////////
//  Main program
///////////////////////////////////////////////////////////////////////////////
int main()
{
    using scheme::utree;

    { // testing the c++ side

        using scheme::if_;
        using scheme::plus;
        using scheme::times;
        using scheme::minus;
        using scheme::lte;
        using scheme::_1;
        using scheme::_2;
        using scheme::lambda;

        BOOST_TEST(plus(11, 22, 33)         ()          == utree(66));
        BOOST_TEST(plus(11, 22, _1)         (33)        == utree(66));
        BOOST_TEST(plus(11, _1, _2)         (22, 33)    == utree(66));
        BOOST_TEST(plus(11, plus(_1, _2))   (22, 33)    == utree(66));

        lambda factorial;
        factorial = if_(lte(_1, 0), 1, times(_1, factorial(minus(_1, 1))));

        BOOST_TEST(factorial(_1)            (10)        == utree(3628800));
    }

    return boost::report_errors();
}


// Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "AES tools" project.
// For details, see https://github.com/egor-tensin/aes-tools.
// Distributed under the MIT License.

#include "file_cmd_parser.hpp"
#include "helpers/file.hpp"

#include <aesxx/all.hpp>

#include <boost/program_options.hpp>

#include <exception>
#include <iostream>
#include <string>
#include <vector>

namespace
{
    void decrypt_file(
        aes::Box& box,
        const std::string& ciphertext_path,
        const std::string& plaintext_path)
    {
        const auto ciphertext_buf = file::read_file(ciphertext_path);
        const auto plaintext_buf = box.decrypt_buffer(
            ciphertext_buf.data(),
            ciphertext_buf.size());
        file::write_file(plaintext_path, plaintext_buf);
    }

    void decrypt_file(const FileSettings& settings)
    {
        const auto& algorithm = settings.algorithm;
        const auto& mode = settings.mode;

        aes::Box::Key key;
        aes::Box::parse_key(key, algorithm, settings.key);

        if (aes::mode_requires_init_vector(mode))
        {
            aes::Box::Block iv;
            aes::Box::parse_block(iv, algorithm, settings.iv);

            aes::Box box{algorithm, key, mode, iv};
            decrypt_file(box, settings.input_path, settings.output_path);
        }
        else
        {
            aes::Box box{algorithm, key};
            decrypt_file(box, settings.input_path, settings.output_path);
        }
    }
}

int main(int argc, char** argv)
{
    try
    {
        FileSettings settings{argv[0]};

        try
        {
            settings.parse(argc, argv);
        }
        catch (const boost::program_options::error& e)
        {
            settings.usage_error(e);
            return 1;
        }

        if (settings.exit_with_usage)
        {
            settings.usage();
            return 0;
        }

        decrypt_file(settings);
    }
    catch (const aes::Error& e)
    {
        std::cerr << e;
        return 1;
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << "\n";
        return 1;
    }
    return 0;
}


/*! \file vhartree.h
    \brief Hartreeポテンシャルを求めるクラスの実装

    Copyright © 2015 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include "vhartree.h"
#include <array>                // for std::array
#include <boost/assert.hpp>     // for BOOST_ASSERT

namespace schrac {
    // #region コンストラクタ

    Vhartree::Vhartree(std::vector<double> const & r_mesh) :
        Vhart([this]{ return std::cref(vhart_); }, [this](std::vector<double> const & v) { return vhart_ = v; }),
        acc_(gsl_interp_accel_alloc(), gsl_interp_accel_free),
        r_mesh_(r_mesh),
        spline_(gsl_spline_alloc(gsl_interp_cspline, r_mesh.size()), gsl_spline_free)
    {
        vhart_.reserve(r_mesh.size());
    }

    Vhartree::Vhartree(Vhartree const & rhs) :
        Vhartree(rhs.r_mesh_)
    {
        vhart_ = rhs.vhart_;
        gsl_spline_init(spline_.get(), r_mesh_.data(), vhart_.data(), r_mesh_.size());
    }

    // #endregion コンストラクタ

    // #region publicメンバ関数

    double Vhartree::dvhartree_dr(double r) const
    {
        return gsl_spline_eval_deriv(spline_.get(), r, acc_.get());
    }

    void Vhartree::set_vhartree_boundary_condition(double Z)
    {
        for (auto && v : vhart_) {
            v += (Z / r_mesh_.back() - vhart_.back());
        }
    }

    void Vhartree::vhart_init()
    {
        gsl_spline_init(spline_.get(), r_mesh_.data(), vhart_.data(), r_mesh_.size());
    }

    double Vhartree::vhartree(double r) const
    {
        return gsl_spline_eval(spline_.get(), r, acc_.get());
    }

    // #endregion publicメンバ関数
}


/*
 * dom_sort_no_duplicates.hpp
 *
 *  Created on: Aug 18, 2014
 *      Author: joost
 */

#ifndef MODULES_NSGAEXT_NO_DUPLICATES_HPP_
#define MODULES_NSGAEXT_NO_DUPLICATES_HPP_

#include <set>
#include <map>

#include <boost/foreach.hpp>

#include <sferes/ea/dom_sort_basic.hpp>
#include <sferes/ea/dom_sort.hpp>
#include <sferes/dbg/dbg.hpp>

#include "dom_sort_fast.hpp"

namespace sferes
{
namespace ea
{

template<typename Indiv, typename ND>
inline void dom_sort_no_duplicates(const std::vector<Indiv>& pop,
        std::vector<std::vector<Indiv> >& fronts,
        const ND& nd,
        std::vector<size_t>& ranks)
{
    dbg::trace trace("dom-sort", DBG_HERE);
    //p[i] is set to i for all i in range(pop.size())
    std::vector<size_t> p(pop.size());
    for (size_t i = 0; i < p.size(); ++i)
        p[i] = i;

    ranks.resize(pop.size());
    int rank = 0;
    while (!p.empty())
    {
        //Create a temporay population
        std::vector<size_t> non_dominated;
        std::vector<Indiv> non_dominated_ind;
        std::set<std::vector<float> > unique_points;
        std::vector<Indiv> tmp_pop;
        for (size_t i = 0; i < p.size(); ++i){
            tmp_pop.push_back(pop[p[i]]);
        }
        assert(tmp_pop.size());

        //Find all individuals in the population who are not dominated in the temp population
        //Those individuals will get the current rank and they are added to non-dominated
        for (size_t i = 0; i < p.size(); ++i){
            if (nd(pop[p[i]], tmp_pop))
            {
                if(unique_points.insert(pop[p[i]]->fit().objs()).second){
                    non_dominated.push_back(p[i]);
                    ranks[p[i]] = rank;
                    non_dominated_ind.push_back(pop[p[i]]);
                }
            }
        }
        assert(non_dominated.size());

        //Put all individuals that were dominated into the np vector
        //And set this np vector as the new p vector
        std::vector<size_t> np;
        std::set_difference(p.begin(), p.end(),
                non_dominated.begin(), non_dominated.end(),
                std::back_insert_iterator<std::vector<size_t> >(np));
        assert(np.size() < p.size());
        p.swap(np);

        //Add all non-dominated individuals to the current front and fill the next rank
        fronts.push_back(non_dominated_ind);
        ++rank;
    }
}

template<typename Indiv>
inline void dom_sort_no_duplicates(const std::vector<Indiv>& pop,
        std::vector<std::vector<Indiv> >& fronts,
        std::vector<size_t>& ranks)
{
    dom_sort_no_duplicates(pop, fronts, _dom_sort_basic::non_dominated_f(), ranks);
}

namespace deb{
typedef std::vector<std::vector<size_t> > r_front_t;
}

//Find the rank of each individual according to the `max 1 unique point per layer' rule
template<typename Indiv>
inline size_t assign_ranks(const std::vector<Indiv>& pop, deb::r_front_t& raw_fronts, std::vector<size_t>& ranks){
    std::map<std::vector<float>, unsigned> unique_points;
    size_t rank;
    size_t proposed_rank;
    size_t max_rank = 0;
    size_t pop_index;

    for (unsigned i = 0; i < raw_fronts.size(); ++i){
        for (unsigned j = 0; j < raw_fronts[i].size(); ++j){
            pop_index = raw_fronts[i][j];
            if(unique_points.count(pop[pop_index]->fit().objs()) == 0){
                rank = (unique_points[pop[pop_index]->fit().objs()] = i);
            } else{
                rank = (unique_points[pop[pop_index]->fit().objs()] += 1);
                if(i > rank){
                    rank = (unique_points[pop[pop_index]->fit().objs()] = i);
                }
            }
            ranks[pop_index] = rank;
            if(rank > max_rank) max_rank = rank;
        }
    }
    return max_rank;
}

template<typename SizeType>
inline deb::r_front_t make_layers(deb::r_front_t& s, std::vector<SizeType>& n){
    deb::r_front_t f(1);
    for (size_t i = 0; i < n.size(); ++i){
        if (n[i] == 0){
            f[0].push_back(i);
        }
    }
    dbg::assertion(DBG_ASSERTION(!f[0].empty()));

    // second step : make layers
    size_t i = 0;
    while (!f[i].empty()){
        f.push_back(std::vector<size_t>());
        for (size_t pp = 0; pp < f[i].size(); ++pp){
            size_t p = f[i][pp];
            for (size_t k = 0; k < s[p].size(); ++k){
                size_t q = s[p][k];
                dbg::assertion(DBG_ASSERTION(q != p));
                dbg::assertion(DBG_ASSERTION(n[q] != 0));
                --n[q];
                if (n[q] == 0){
                    f.back().push_back(q);
                }
            }
        }
        ++i;
        dbg::assertion(DBG_ASSERTION(i < f.size()));
    }

    return f;
}

// cf deb's paper on NSGA-2 :
/// @article{deb2002nsga,
// title={{NSGA-II}},
//   author={Deb, K. and Pratap, A. and Agarwal, S. and Meyarivan, T. and Fast, A. and Algorithm, E.M.G.},
//  journal={IEEE transactions on evolutionary computation},
//   volume={6},
//  number={2},
//  year={2002}
// }
// this algorithm is in O(n^2)
template<typename Indiv, typename DC>
inline void dom_sort_no_duplicates_deb(const std::vector<Indiv>& pop,
        std::vector<std::vector<Indiv> >& fronts,
        const DC& dc,
        std::vector<size_t>& ranks)
{
    dbg::trace trace("dom_sort_trace" , DBG_HERE);
    typedef typename _dom_sort::count_dom_custom_comparator<Indiv, DC>::dom_by_t dom_by_t;
    //Ascertain that the population is good
    dbg::assertion(DBG_ASSERTION(!pop.empty()));
#ifndef NDEBUG
    BOOST_FOREACH(const Indiv& ind, pop)
    for (size_t i = 0; i < ind->fit().objs().size(); ++i)
    { dbg::assertion(DBG_ASSERTION(!std::isnan(ind->fit().objs()[i]))); }
#endif

    //Determine the number of individuals that each individual is dominated by.
    dbg::out(dbg::info, "dom_sort") << "Counting dominated..." << std::endl;
    deb::r_front_t s(pop.size());
    dom_by_t n(pop.size());
    std::fill(n.begin(), n.end(), 0);
    ranks.resize(pop.size());
    std::fill(ranks.begin(), ranks.end(), pop.size());
    parallel::p_for(parallel::range_t(0, pop.size()), _dom_sort::count_dom_custom_comparator<Indiv, DC>(pop, n, s, dc));
    dbg::out(dbg::info, "dom_sort") << "Counting dominated... done" << std::endl;

    //Ascertain that no individual is dominated by more than than the population size of individuals.
#ifndef NDEBUG
    BOOST_FOREACH(size_t k, n){ dbg::assertion(DBG_ASSERTION(k < pop.size())); }
#endif

    //Make layers
    dbg::out(dbg::info, "dom_sort") << "Making layers..." << std::endl;
    deb::r_front_t f = make_layers(s, n);
    dbg::out(dbg::info, "dom_sort") << "Making layers... done" << std::endl;

    //Ascertain that every individual in the population has been assigned a rank.
#ifndef NDEBUG
    size_t size = 0;
    BOOST_FOREACH(std::vector<size_t>& v, f)
    size += v.size();
    assert(size == pop.size());
#endif

    //Assign ranks
    dbg::out(dbg::info, "dom_sort") << "Assigning ranks..." << std::endl;
    size_t max_rank = assign_ranks(pop, f, ranks);
    dbg::out(dbg::info, "dom_sort") << "Assigning ranks... done" << std::endl;

    //Fill the fronts with individuals
    dbg::out(dbg::info, "dom_sort") << "Filling fronts..." << std::endl;
    fronts.clear();
    dbg::out(dbg::info, "dom_sort") << "Resizing fronts to: " << max_rank + 1 << std::endl;
    fronts.resize(max_rank + 1);
    dbg::out(dbg::info, "dom_sort") << "Pushing back individuals" << std::endl;
    for (size_t i = 0; i < pop.size(); ++i){
        dbg::assertion(DBG_ASSERTION(i < ranks.size()));
        dbg::assertion(DBG_ASSERTION(i < pop.size()));
        dbg::assertion(DBG_ASSERTION(ranks[i] < fronts.size()));
        fronts[ranks[i]].push_back(pop[i]);
    }
    dbg::out(dbg::info, "dom_sort") << "Filling fronts... done" << std::endl;

    dbg::assertion(DBG_ASSERTION(!fronts.empty()));
    dbg::assertion(DBG_ASSERTION(!fronts[0].empty()));
}

struct dom_sort_basic_f{
    template<typename Indiv, typename ND>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            const ND& nd,
            std::vector<size_t>& ranks) const
    {
        dom_sort_basic(pop, fronts, nd, ranks);
    }

    template<typename Indiv>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            std::vector<size_t>& ranks) const
    {
        dom_sort_basic(pop, fronts, _dom_sort_basic::non_dominated_f(), ranks);
    }
};

struct dom_sort_no_duplicates_f{
    template<typename Indiv, typename ND>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            const ND& nd,
            std::vector<size_t>& ranks) const
    {
        dom_sort_no_duplicates(pop, fronts, nd, ranks);
    }

    template<typename Indiv>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            std::vector<size_t>& ranks) const
    {
        dom_sort_no_duplicates(pop, fronts, _dom_sort_basic::non_dominated_f(), ranks);
    }
};

struct dom_sort_no_duplicates_fast_f{
    template<typename Indiv, typename ND>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            const ND& nd,
            std::vector<size_t>& ranks) const
    {
        dom_sort_no_duplicates_deb(pop, fronts, nd, ranks);
    }

    template<typename Indiv>
    inline void operator() (const std::vector<Indiv>& pop,
            std::vector<std::vector<Indiv> >& fronts,
            std::vector<size_t>& ranks) const
    {
        dom_sort_no_duplicates_deb(pop, fronts, _dom_sort_basic::non_dominated_f(), ranks);
    }
};
}
}

#endif /* DOM_SORT_NO_DUPLICATES_HPP_ */


#include "cloudproc.hpp"
#include <pcl/io/pcd_io.h>
#include "boost/format.hpp"
#include <pcl/filters/voxel_grid.h>
#include <pcl/surface/mls.h>
#include <pcl/surface/gp3.h>
#include <pcl/io/vtk_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/obj_io.h>
#include <pcl/surface/organized_fast_mesh.h>
#include "pcl/io/vtk_lib_io.h"
#include <pcl/point_types.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/convex_hull.h>
#include "pcl/impl/instantiate.hpp"
#include <boost/filesystem.hpp>
#if 0
#include <pcl/filters/median_filter.h>
#endif
#include <pcl/filters/fast_bilateral.h>

using namespace std;
using namespace pcl;
namespace fs = boost::filesystem;

#define FILE_OPEN_ERROR(fname) throw runtime_error( (boost::format("couldn't open %s")%fname).str() )

namespace cloudproc {

template <class CloudT>
int cloudSize(const CloudT& cloud) {
  return cloud.width * cloud.height;
}

template <class CloudT>
void setWidthToSize(const CloudT& cloud) {
  cloud->width = cloud->points.size();
  cloud->height = 1;
}

template <class T>
typename pcl::PointCloud<T>::Ptr readPCD(const std::string& pcdfile) {
  sensor_msgs::PointCloud2 cloud_blob;
  typename pcl::PointCloud<T>::Ptr cloud (new typename pcl::PointCloud<T>);
  if (pcl::io::loadPCDFile (pcdfile, cloud_blob) != 0) FILE_OPEN_ERROR(pcdfile);
  pcl::fromROSMsg (cloud_blob, *cloud);
  return cloud;
}

template<class T>
void saveCloud(const typename pcl::PointCloud<T>& cloud, const std::string& fname) {
  std::string ext = fs::extension(fname);
  if (ext == ".pcd")   pcl::io::savePCDFileBinary(fname, cloud);
  else if (ext == ".ply") pcl::io::savePLYFile(fname, cloud, true);
  else throw std::runtime_error( (boost::format("%s has unrecognized extension")%fname).str() );
}

///////////////////////////

template<class T>
typename pcl::PointCloud<T>::Ptr downsampleCloud(typename pcl::PointCloud<T>::ConstPtr in, float vsize) {
  typename pcl::PointCloud<T>::Ptr out (new typename pcl::PointCloud<T>);
  pcl::VoxelGrid< T > sor;
  sor.setInputCloud (in);
  sor.setLeafSize (vsize, vsize, vsize);
  sor.filter (*out);
  return out;
}

//////////////////////////

void findConvexHull(PointCloud<pcl::PointXYZ>::ConstPtr in, pcl::PointCloud<pcl::PointXYZ>& out, std::vector<Vertices>& polygons) {
  pcl::ConvexHull<PointXYZ> chull;
  chull.setInputCloud (in);
  chull.reconstruct (out, polygons);
}

PointCloud<pcl::PointNormal>::Ptr mlsAddNormals(PointCloud<pcl::PointXYZ>::ConstPtr in, float searchRadius) {
  pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals (new pcl::PointCloud<pcl::PointNormal>);

  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
  tree->setInputCloud (in);

  pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;
  mls.setComputeNormals (true);
  mls.setInputCloud (in);
  mls.setPolynomialFit (true);
  mls.setSearchMethod (tree);
  mls.setSearchRadius (0.04);
  mls.process (*cloud_with_normals);
  return cloud_with_normals;
}
#if 0
pcl::PolygonMesh::Ptr createMesh_MarchingCubes(PointCloud<pcl::PointNormal>::ConstPtr cloud_with_normals) {
  pcl::PolygonMesh::Ptr triangles(new PolygonMesh());
    // Create search tree*
    pcl::search::KdTree<pcl::PointNormal>::Ptr tree2 (new pcl::search::KdTree<pcl::PointNormal>);
    tree2->setInputCloud (cloud_with_normals);

    pcl::MarchingCubesGreedy<pcl::PointNormal> mcg;
    mcg.setInputCloud(cloud_with_normals);
    mcg.setSearchMethod(tree2);
    mcg.setIsoLevel(.1);
    mcg.setGridResolution(100,100,100);
    mcg.reconstruct (*triangles);

    return triangles;
}
#endif

pcl::PolygonMesh::Ptr meshGP3(PointCloud<pcl::PointNormal>::ConstPtr cloud_with_normals, float mu, int maxnn, float searchRadius) {
  pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(
      new pcl::search::KdTree<pcl::PointNormal>);
  tree2->setInputCloud(cloud_with_normals);

  // Initialize objects
  pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
  pcl::PolygonMesh::Ptr triangles(new pcl::PolygonMesh);

  // Set the maximum distance between connected points (maximum edge length)
  gp3.setSearchRadius(searchRadius);

  // Set typical values for the parameters
  gp3.setMu(mu);
  gp3.setMaximumNearestNeighbors(maxnn);
  gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
  gp3.setMinimumAngle(M_PI / 18); // 10 degrees
  gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
  gp3.setNormalConsistency(false);

  gp3.setInputCloud(cloud_with_normals);
  gp3.setSearchMethod(tree2);
  gp3.reconstruct(*triangles);
  return triangles;
}

pcl::PolygonMesh::Ptr meshOFM(PointCloud<pcl::PointXYZ>::ConstPtr cloud, int edgeLengthPixels, float maxEdgeLength) {
  pcl::OrganizedFastMesh<pcl::PointXYZ> ofm;
  ofm.setInputCloud(cloud);
  ofm.setTrianglePixelSize (edgeLengthPixels);
  ofm.setMaxEdgeLength(maxEdgeLength);
  ofm.setTriangulationType (pcl::OrganizedFastMesh<PointXYZ>::TRIANGLE_ADAPTIVE_CUT);
  pcl::PolygonMeshPtr mesh(new pcl::PolygonMesh());
  ofm.reconstruct(mesh->polygons);
  pcl::toROSMsg(*cloud, mesh->cloud);
  mesh->header = cloud->header;
  return mesh;
}

#if 0
pcl::PolygonMesh::Ptr createMesh(PointCloud<pcl::PointNormal>::ConstPtr cloud, MeshMethod method) {
  switch (method) {
  case MarchingCubes:
    return createMesh_MarchingCubes(cloud);
  case GP3:
    return createMesh_GP3(cloud);
  default:
    throw std::runtime_error("invalid meshing method");
  }
}
#endif

#if 0
void saveTrimeshCustomFmt(pcl::PolygonMesh::ConstPtr mesh, const std::string& fname) {
  ofstream o(fname.c_str());
  if (o.bad()) FILE_OPEN_ERROR(fname);
  pcl::PointCloud<PointXYZ>::Ptr cloud(new pcl::PointCloud<PointXYZ>());
  pcl::fromROSMsg(mesh->cloud, *cloud);
  o << cloud->size() << endl;
  BOOST_FOREACH(const PointXYZ& pt, cloud->points) {
    o << pt.x << " " << pt.y << " " << pt.z;
  }
  o << mesh->polygons.size() << endl;
  BOOST_FOREACH(const pcl::Vertices& verts, mesh->polygons) {
    if (verts.vertices.size() != 3) throw runtime_error("only triangles are supported");
    o << verts.vertices[0] << " " << verts.vertices[1] << " " << verts.vertices[2] << endl;
  }
}
#endif

template <typename T>
pcl::PointCloud<pcl::PointXYZ>::Ptr toXYZ(typename pcl::PointCloud<T>::ConstPtr in) {
  pcl::PointCloud<pcl::PointXYZ>::Ptr out(new pcl::PointCloud<PointXYZ>());
  out->reserve(in->size());
  out->width = in->width;
  out->height = in->height;
  BOOST_FOREACH(const T& pt, in->points) {
    out->points.push_back(PointXYZ(pt.x, pt.y, pt.z));
  }
  return out;
}

template <class T>
VectorXb boxMask(typename pcl::PointCloud<T>::ConstPtr in, float xmin, float ymin, float zmin, float xmax, float ymax, float zmax) {
  int i=0;
  VectorXb out(in->size());
  BOOST_FOREACH(const T& pt, in->points) {
    out[i] = (pt.x >= xmin && pt.x <= xmax && pt.y >= ymin && pt.y <= ymax && pt.z >= zmin && pt.z <= zmax);
    ++i;
  }
  return out;
}

template <class T>
typename pcl::PointCloud<T>::Ptr maskFilterDisorganized(typename pcl::PointCloud<T>::ConstPtr in, const VectorXb& mask) {
  int n = mask.sum();
  typename pcl::PointCloud<T>::Ptr out(new typename pcl::PointCloud<T>());
  out->points.reserve(n);
  for (int i=0; i < mask.size(); ++i) {
    if (mask[i]) out->points.push_back(in->points[i]);
  }
  setWidthToSize(out);
  return out;
}

template <class T>
typename pcl::PointCloud<T>::Ptr maskFilterOrganized(typename pcl::PointCloud<T>::ConstPtr in, const VectorXb& mask) {
  typename pcl::PointCloud<T>::Ptr out(new typename pcl::PointCloud<T>(*in));
  for (int i=0; i < mask.size(); ++i) {
    if (!mask[i]) {
      T& pt = out->points[i];
      pt.x = NAN;
      pt.y = NAN;
      pt.z = NAN;
    }
  }
  return out;
}

template <class T>
typename pcl::PointCloud<T>::Ptr maskFilter(typename pcl::PointCloud<T>::ConstPtr in, const VectorXb& mask, bool keep_organized) {
  if (keep_organized) return maskFilterOrganized<T>(in, mask);
  else return maskFilterDisorganized<T>(in, mask);
}

template <class T>
typename pcl::PointCloud<T>::Ptr medianFilter(typename pcl::PointCloud<T>::ConstPtr in, int windowSize, float maxAllowedMovement) {
#if 0
  pcl::MedianFilter<T> mf;
  mf.setWindowSize(windowSize);
  mf.setMaxAllowedMovement(maxAllowedMovement);
  typename PointCloud<T>::Ptr out(new PointCloud<T>());
  mf.setInputCloud(in);
  mf.filter(*out);
  return out;
#else PRINT_AND_THROW("not implemented");
#endif
}

template <class T>
typename pcl::PointCloud<T>::Ptr fastBilateralFilter(typename pcl::PointCloud<T>::ConstPtr in, float sigmaS, float sigmaR) {
  pcl::FastBilateralFilter<T> mf;
  mf.setSigmaS(sigmaS);
  mf.setSigmaR(sigmaR);
  typename PointCloud<T>::Ptr out(new PointCloud<T>());
  mf.setInputCloud(in);
  mf.applyFilter(*out);
  return out;
}

void removenans(sensor_msgs::PointCloud2& cloud, float fillval=0);
void removenans(sensor_msgs::PointCloud2& cloud, float fillval) {
  int npts = cloud.width * cloud.height;
  for (int i=0; i < npts; ++i) {
    float* ptdata = (float*)(cloud.data.data() + cloud.point_step * i);
    for (int j=0; j < 3; ++j) {
      if (!isfinite(ptdata[j])) ptdata[j] = fillval;
    }
  }
}

void saveMesh(const pcl::PolygonMesh& origmesh, const std::string& fname) {
  pcl::PolygonMesh mesh = origmesh;
  removenans(mesh.cloud);

  string ext = fs::extension(fname);
  int errcode;
  if (ext == ".ply") {
    errcode = pcl::io::savePLYFileBinary (fname, mesh);
  }
  else if (ext == ".obj") {
    bool success = pcl::io::saveOBJFile (fname, mesh);
  }
  else if (ext == ".vtk") {
    bool success = pcl::io::saveVTKFile (fname, mesh);
  }
  else PRINT_AND_THROW(boost::format("filename %s had unrecognized extension")%fname);
  if (errcode) PRINT_AND_THROW(boost::format("saving mesh to file %s failed")%fname);
}

pcl::PolygonMesh::Ptr loadMesh(const std::string& fname) {
  pcl::PolygonMesh::Ptr out(new PolygonMesh());

  string ext = fs::extension(fname);

  if (ext == ".ply") {
    pcl::io::loadPolygonFilePLY(fname, *out);
  }
  else if (ext == ".obj") {
    pcl::io::loadPolygonFileOBJ (fname, *out);
  }
  else if (ext == ".vtk") {
    pcl::io::loadPolygonFileVTK (fname, *out);
  }
  else PRINT_AND_THROW(boost::format("filename %s had unrecognized extension")%fname);
  return out;
}

#include "autogen_instantiations.cpp"
}