#include <spatialops/SpatialOpsTools.h>
#include <spatialops/OperatorDatabase.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 <spatialops/util/TimeLogger.h>

//#define PROFILING
#ifdef PROFILING
#include <valgrind/callgrind.h>
#endif /* PROFILING */

#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>

#define USING_VARYING_EDGE_STENCIL

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

template<typename FieldT, typename DirT>
struct StencilsT
{
  typedef typename UnitTriplet<DirT>::type DirTripletT;
  typedef typename Multiply<IndexTriplet<2, 2, 2>, DirTripletT>::result TwoDirTripletT;
  typedef typename Multiply<IndexTriplet<3, 3, 3>, DirTripletT>::result ThreeDirTripletT;
  typedef typename Multiply<IndexTriplet<4, 4, 4>, DirTripletT>::result FourDirTripletT;
  //Main Point
  public:
    typedef typename 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  ( typename DirTripletT::Negate )
                   ::NEBO_ADD_POINT  ( typename TwoDirTripletT::Negate )
                   ::NEBO_ADD_POINT  ( typename ThreeDirTripletT::Negate )
                   ::NEBO_ADD_POINT  ( typename FourDirTripletT::Negate )
      MainPointStencilCollectionT;
    typedef NeboStencilBuilder<Gradient,
                               MainPointStencilCollectionT,
                               FieldT,
                               FieldT>
            MainPointStencilT;
    static const NeboStencilCoefCollection<MainPointStencilCollectionT::length> mainCoefs;

#ifdef USING_VARYING_EDGE_STENCIL
  //One Point
  public:
    //Positive
    public:
      typedef NEBO_FIRST_IJK( 0, 0, 0)
        OnePointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   OnePointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicOneT;
        static const NeboStencilCoefCollection<OnePointPositiveStencilCollectionT::length> pCoefOne;

    //Negative
    public:
      typedef OnePointPositiveStencilCollectionT OnePointNegativeStencilCollectionT;
      typedef PBasicOneT NBasicOneT;
      static const NeboStencilCoefCollection<OnePointPositiveStencilCollectionT::length> nCoefOne;

  //Two Point One Sided
  public:
    //Two Point Positive
    public:
      typedef typename NEBO_FIRST_IJK( 0, 0, 0)
                     ::NEBO_ADD_POINT  ( DirTripletT )
        TwoPointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   TwoPointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicTwoT;
        static const NeboStencilCoefCollection<TwoPointPositiveStencilCollectionT::length> pCoefTwo;

    //Two Point Negative
    public:
      typedef typename NEBO_FIRST_IJK( 0, 0, 0)
                     ::NEBO_ADD_POINT  ( typename DirTripletT::Negate )
        TwoPointNegativeStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   TwoPointNegativeStencilCollectionT,
                                   FieldT,
                                   FieldT>
                NBasicTwoT;
        static const NeboStencilCoefCollection<TwoPointPositiveStencilCollectionT::length> nCoefTwo;

  //Three Point Centered
  public:
    //Point Positive
    public:
      typedef typename NEBO_FIRST_IJK( 0, 0, 0)
                     ::NEBO_ADD_POINT  ( DirTripletT )
                     ::NEBO_ADD_POINT  ( typename DirTripletT::Negate )
        ThreePointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   ThreePointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicThreeT;
        static const NeboStencilCoefCollection<ThreePointPositiveStencilCollectionT::length> pCoefThree;

    //Negative
    public:
      typedef ThreePointPositiveStencilCollectionT ThreePointNegativeStencilCollectionT;
      typedef PBasicThreeT NBasicThreeT;
        static const NeboStencilCoefCollection<ThreePointPositiveStencilCollectionT::length> nCoefThree;

  //Four Point Slightly Centered
  public:
    //Point Positive
    public:
      typedef typename NEBO_FIRST_IJK( 0, 0, 0)
                     ::NEBO_ADD_POINT  ( DirTripletT )
                     ::NEBO_ADD_POINT  ( TwoDirTripletT )
                     ::NEBO_ADD_POINT  ( typename DirTripletT::Negate )
        FourPointPositiveStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   FourPointPositiveStencilCollectionT,
                                   FieldT,
                                   FieldT>
                PBasicFourT;
        static const NeboStencilCoefCollection<FourPointPositiveStencilCollectionT::length> pCoefFour;

    //Negative
    public:
      typedef typename NEBO_FIRST_IJK( 0, 0, 0)
                     ::NEBO_ADD_POINT  ( typename DirTripletT::Negate )
                     ::NEBO_ADD_POINT  ( typename TwoDirTripletT::Negate )
                     ::NEBO_ADD_POINT  ( DirTripletT )
        FourPointNegativeStencilCollectionT;
        typedef NeboStencilBuilder<Gradient,
                                   FourPointNegativeStencilCollectionT,
                                   FieldT,
                                   FieldT>
                NBasicFourT;
        static const NeboStencilCoefCollection<FourPointPositiveStencilCollectionT::length> nCoefFour;

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

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

  typedef NeboVaryingEdgeStencilBuilder<DirT,
                                        FieldT,
                                        FieldT,
                                        MainPointStencilT,
                                        NegativeStencilListT,
                                        PositiveStencilListT>
          VaryEdgeOpT;
#endif /* USING_VARYING_EDGE_STENCIL */

  static 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 USING_VARYING_EDGE_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 /* USING_VARYING_EDGE_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 );
  }
};

template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::OnePointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::pCoefOne
= build_coef_collection(1.0);
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::OnePointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::nCoefOne
= pCoefOne;
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::TwoPointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::pCoefTwo
= build_coef_collection(1.0)(1.0);
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::TwoPointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::nCoefTwo
= pCoefTwo;
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::ThreePointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::pCoefThree
= build_coef_collection(1.0)(1.0)(1.0);
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::ThreePointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::nCoefThree
= pCoefThree;
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::FourPointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::pCoefFour
= build_coef_collection(1.0)(1.0)(1.0)(1.0);
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::FourPointPositiveStencilCollectionT::length>  StencilsT<FieldT, DirT>::nCoefFour
= pCoefFour;
template<typename FieldT, typename DirT>
const NeboStencilCoefCollection<StencilsT<FieldT, DirT>::MainPointStencilCollectionT::length> StencilsT<FieldT, DirT>::mainCoefs
= build_coef_collection(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0)(1.0);

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

enum RunMode
{
  CORRECTNESS,
  TIMING,
};

struct TimerPack
{
  Timer& i_timer;
  double o_time;

  TimerPack(Timer& i_timer)
    : i_timer(i_timer)
  {}
};

double ReturnMedian(std::function<double(Timer&)>& i_timedFunc,
                    Timer& i_timer,
                    const size_t i_iterations)
{
  std::vector<double> times;
  times.reserve(i_iterations);
  for(size_t i = 0; i < i_iterations; i++)
  {
    times.push_back(i_timedFunc(i_timer));
  }

  std::sort(times.begin(), times.end());

  return times[i_iterations/2];
}

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

template<typename DirT, typename DOMAIN_SIDET, typename RHSType>
GhostData CalculateRHSGhost(const RHSType& i_rhs)
{
  GhostData const mainStencilGhost(i_rhs.ghosts_without_bc());

  GhostData const mainBCCells(i_rhs.ghosts_with_bc() - mainStencilGhost);

  if(DOMAIN_SIDET::value == static_cast<int>(DomainEdgeSide::BOTH_SIDE::value)) {
    /* Both sides means we do not fill in directional ghost data nor need it
    */
    IntVec plus = mainStencilGhost.get_plus();
    plus[DirT::value] = 0;
    IntVec minus = mainStencilGhost.get_minus();
    minus[DirT::value] = 0;
    return GhostData(minus, plus) + mainBCCells;
  }
  else {
    if(DOMAIN_SIDET::value == static_cast<int>(DomainEdgeSide::MINUS_SIDE::value)) {
      IntVec minus = mainStencilGhost.get_minus();

      minus[DirT::value] = 0;

      return GhostData(minus, mainStencilGhost.get_plus()) + mainBCCells;
    }
    else if(DOMAIN_SIDET::value == static_cast<int>(DomainEdgeSide:: PLUS_SIDE::value)) {
      IntVec plus = mainStencilGhost.get_plus();

      plus[DirT::value] = 0;

      return GhostData(mainStencilGhost.get_minus(), plus) + mainBCCells;
    }
    else
    {
      return mainStencilGhost + mainBCCells;
    }
  };
}

#ifdef USING_VARYING_EDGE_STENCIL
namespace NinePointVaryingStencilTest
{
  namespace
  {
    template<typename FieldT, typename PointCollection, typename Arg>
     struct EvalExpr {
       NeboStencilCoefCollection<PointCollection::length> typedef Coefs;

       typename PointCollection::Point typedef Point;

       typename PointCollection::Collection typedef Collection;

#ifdef __CUDACC__
      __host__ __device__
#endif /* __CUDACC__ */
       static inline typename FieldT::value_type eval(Arg const & arg,
                                                      Coefs const & coefs,
                                                      int const x,
                                                      int const y,
                                                      int const z) {
#ifdef __CUDA_ARCH__
          return EvalExpr<FieldT, Collection, Arg>::eval(arg, coefs.others(), x, y, z)
                 + arg.eval(x + Point::value_gpu(0),
                            y + Point::value_gpu(1),
                            z + Point::value_gpu(2)) * coefs.coef();
#else
          return EvalExpr<FieldT, Collection, Arg>::eval(arg, coefs.others(), x, y, z)
                 + arg.eval(x + Point::value(0),
                            y + Point::value(1),
                            z + Point::value(2)) * coefs.coef();
#endif /* __CUDA_ARCH__ */
       }
    };

    template<typename FieldT, typename Point, typename Arg>
     struct EvalExpr<FieldT, NeboStencilPointCollection<Point, NeboNil>, Arg > {
       NeboStencilCoefCollection<1> typedef Coefs;

#ifdef __CUDACC__
      __host__ __device__
#endif /* __CUDACC__ */
       static inline typename FieldT::value_type eval(Arg const & arg,
                                                      Coefs const & coefs,
                                                      int const x,
                                                      int const y,
                                                      int const z) {
#ifdef __CUDA_ARCH__
          return arg.eval(x + Point::value_gpu(0),
                          y + Point::value_gpu(1),
                          z + Point::value_gpu(2)) * coefs.coef();
#else
          return arg.eval(x + Point::value(0),
                          y + Point::value(1),
                          z + Point::value(2)) * coefs.coef();
#endif /* __CUDA_ARCH__ */
       }
    };
  }

  template<typename FieldT>
  struct FieldArgWrapper
  {
    public:
      FieldArgWrapper(FieldT i_f, const int i_deviceIndex)
      : xGlob_(i_f.window_with_ghost().glob_dim(0)),
        yGlob_(i_f.window_with_ghost().glob_dim(1)),
        base_(i_f.field_values(i_deviceIndex) + (i_f.window_with_ghost().offset(0) +
                                           i_f.get_valid_ghost_data().get_minus(0))
              + (i_f.window_with_ghost().glob_dim(0) * ((i_f.window_with_ghost().offset(1)
                                                       + i_f.get_valid_ghost_data().get_minus(1))
                                                      + (i_f.window_with_ghost().glob_dim(1)
                                                         * (i_f.window_with_ghost().offset(2)
                                                            + i_f.get_valid_ghost_data().get_minus(2))))))
    {}

#ifdef __CUDACC__
      __host__ __device__
#endif /* __CUDACC__ */
    inline typename FieldT::value_type eval(int const x, int const y, int const z) const {
      return base_[x + xGlob_ * (y + (yGlob_ * z))];
    }

#ifdef __CUDACC__
      __host__ __device__
#endif /* __CUDACC__ */
    inline typename FieldT::value_type& ref(int const x, int const y, int const z) {
      return base_[x + xGlob_ * (y + (yGlob_ * z))];
    }

    private:
      int const xGlob_;
      int const yGlob_;
      typename FieldT::value_type * base_;
  };

  namespace
  {
    template<typename DirT, typename DOMAIN_SIDET>
    struct IncrementEdge;

    template<typename DOMAIN_SIDET>
    struct IncrementEdge<XDIR, DOMAIN_SIDET>
    {
      static inline void IncrX(int& v) {++v;}
      static inline void IncrY(int& v) {++v;}
      static inline void IncrZ(int& v) {++v;}

      static inline void EdgeIncrX(int& v) {++v;}
      static inline void EdgeIncrY(int& v) {}
      static inline void EdgeIncrZ(int& v) {}

      static inline bool ContinueX(const int startV, const int v, const int vLimit) { return startV == v; }
      static inline bool ContinueY(const int startV, const int v, const int vLimit) { return v < vLimit; }
      static inline bool ContinueZ(const int startV, const int v, const int vLimit) { return v < vLimit; }

      template<typename FieldT>
      static inline int MainStartX(const int start, const int negativeExtent){ return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::MINUS_SIDE::value ? StencilsT<FieldT, XDIR>::NegativeStencilListT::length
                                                                                : negativeExtent; }
      template<typename FieldT>
      static inline int MainEndX(const int extent)                           { return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::PLUS_SIDE::value ? extent - StencilsT<FieldT, XDIR>::PositiveStencilListT::length
                                                                                : extent; }
      template<typename FieldT>
      static inline int MainStartY(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndY(const int extent)                           { return extent; }
      template<typename FieldT>
      static inline int MainStartZ(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndZ(const int extent)                           { return extent; }

      static inline int StartNegativeX(const int start) { return 0; }
      static inline int StartNegativeY(const int start) { return start; }
      static inline int StartNegativeZ(const int start) { return start; }

      template<typename FieldT>
      static inline int StartPositiveX(const int start, const int extent) { return extent - StencilsT<FieldT, XDIR>::PositiveStencilListT::length; }
      template<typename FieldT>
      static inline int StartPositiveY(const int start, const int extent) { return start; }
      template<typename FieldT>
      static inline int StartPositiveZ(const int start, const int extent) { return start; }

      static inline int GetStaticIndex(const int x, const int y, const int z) { return x; }
      static inline int GetRapidIncr  (const int x, const int y, const int z) { return y; }
      static inline int GetSlowIncr   (const int x, const int y, const int z) { return z; }
    };
    template<typename DOMAIN_SIDET>
    struct IncrementEdge<YDIR, DOMAIN_SIDET>
    {
      static inline void IncrX(int& v) {++v;}
      static inline void IncrY(int& v) {++v;}
      static inline void IncrZ(int& v) {++v;}

      static inline void EdgeIncrX(int& v) {}
      static inline void EdgeIncrY(int& v) {++v;}
      static inline void EdgeIncrZ(int& v) {}

      static inline bool ContinueX(const int startV, const int v, const int vLimit) { return v < vLimit; }
      static inline bool ContinueY(const int startV, const int v, const int vLimit) { return startV == v; }
      static inline bool ContinueZ(const int startV, const int v, const int vLimit) { return v < vLimit; }

      template<typename FieldT>
      static inline int MainStartX(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndX(const int extent)                           { return extent; }
      template<typename FieldT>
      static inline int MainStartY(const int start, const int negativeExtent){ return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::MINUS_SIDE::value ? StencilsT<FieldT, YDIR>::NegativeStencilListT::length
                                                                                : negativeExtent; }
      template<typename FieldT>
      static inline int MainEndY(const int extent)                           { return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::PLUS_SIDE::value ? extent - StencilsT<FieldT, YDIR>::PositiveStencilListT::length
                                                                                : extent; }
      template<typename FieldT>
      static inline int MainStartZ(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndZ(const int extent)                           { return extent; }

      static inline int StartNegativeX(const int start) { return start; }
      static inline int StartNegativeY(const int start) { return 0; }
      static inline int StartNegativeZ(const int start) { return start; }

      template<typename FieldT>
      static inline int StartPositiveX(const int start, const int extent) { return start; }
      template<typename FieldT>
      static inline int StartPositiveY(const int start, const int extent) { return extent - StencilsT<FieldT, YDIR>::PositiveStencilListT::length; }
      template<typename FieldT>
      static inline int StartPositiveZ(const int start, const int extent) { return start; }

      static inline int GetStaticIndex(const int x, const int y, const int z) { return y; }
      static inline int GetRapidIncr  (const int x, const int y, const int z) { return x; }
      static inline int GetSlowIncr   (const int x, const int y, const int z) { return z; }
    };
    template<typename DOMAIN_SIDET>
    struct IncrementEdge<ZDIR, DOMAIN_SIDET>
    {
      static inline void IncrX(int& v) {++v;}
      static inline void IncrY(int& v) {++v;}
      static inline void IncrZ(int& v) {++v;}

      static inline void EdgeIncrX(int& v) {}
      static inline void EdgeIncrY(int& v) {}
      static inline void EdgeIncrZ(int& v) {++v;}

      static inline bool ContinueX(const int startV, const int v, const int vLimit) { return v < vLimit; }
      static inline bool ContinueY(const int startV, const int v, const int vLimit) { return v < vLimit; }
      static inline bool ContinueZ(const int startV, const int v, const int vLimit) { return startV == v; }

      template<typename FieldT>
      static inline int MainStartX(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndX(const int extent)                           { return extent; }
      template<typename FieldT>
      static inline int MainStartY(const int start, const int negativeExtent){ return start; }
      template<typename FieldT>
      static inline int MainEndY(const int extent)                           { return extent; }
      template<typename FieldT>
      static inline int MainStartZ(const int start, const int negativeExtent){ return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::MINUS_SIDE::value ? StencilsT<FieldT, ZDIR>::NegativeStencilListT::length
                                                                                : negativeExtent; }
      template<typename FieldT>
      static inline int MainEndZ(const int extent)                           { return DOMAIN_SIDET::value
                                                                                & DomainEdgeSide::PLUS_SIDE::value ? extent - StencilsT<FieldT, ZDIR>::PositiveStencilListT::length
                                                                                : extent; }

      static inline int StartNegativeX(const int start) { return start; }
      static inline int StartNegativeY(const int start) { return start; }
      static inline int StartNegativeZ(const int start) { return 0; }

      template<typename FieldT>
      static inline int StartPositiveX(const int start, const int extent) { return start; }
      template<typename FieldT>
      static inline int StartPositiveY(const int start, const int extent) { return start; }
      template<typename FieldT>
      static inline int StartPositiveZ(const int start, const int extent) { return extent - StencilsT<FieldT, ZDIR>::PositiveStencilListT::length; }

      static inline int GetStaticIndex(const int x, const int y, const int z) { return z; }
      static inline int GetRapidIncr  (const int x, const int y, const int z) { return x; }
      static inline int GetSlowIncr   (const int x, const int y, const int z) { return y; }
    };
  }

  namespace
  {
    template<typename DirT, typename DOMAIN_SIDET, typename FieldT>
    inline void CustomTestManualEdgeIteration_CPU( const GhostData i_ghost, const IntVec i_npts, FieldArgWrapper<FieldT>& o_lhs, const FieldArgWrapper<FieldT>& i_rhs )
    {
      typedef IncrementEdge<DirT, DOMAIN_SIDET> EdgeT;
      const IntVec properExtents = i_npts + i_ghost.get_plus();
      {
        int startX = EdgeT::StartNegativeX(-i_ghost.get_minus()[0]);
        int startY = EdgeT::StartNegativeY(-i_ghost.get_minus()[1]);
        int startZ = EdgeT::StartNegativeZ(-i_ghost.get_minus()[2]);

        if(static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::MINUS_SIDE::value
        || static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value)
        {
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicFourT,  typename StencilsT<FieldT, DirT>::NegativeStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicThreeT, typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicTwoT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicOneT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");

          //Negative 1
          {
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::OnePointNegativeStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::nCoefOne, x, y, z);
                }
              }
            }
          }
          //Negative 2
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::TwoPointNegativeStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::nCoefTwo, x, y, z);
                }
              }
            }
          }
          //Negative 3
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::ThreePointNegativeStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::nCoefThree, x, y, z);
                }
              }
            }
          }
          //Negative 4
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::FourPointNegativeStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::nCoefFour, x, y, z);
                }
              }
            }
          }
        }
        //Main
        {
          for(int z = EdgeT::template MainStartZ<FieldT>(startZ, -i_ghost.get_minus()[2]); z < EdgeT::template MainEndZ<FieldT>(properExtents[2]); ++z) {
            for(int y = EdgeT::template MainStartY<FieldT>(startY, -i_ghost.get_minus()[1]); y < EdgeT::template MainEndY<FieldT>(properExtents[1]); ++y) {
              for(int x = EdgeT::template MainStartX<FieldT>(startX, -i_ghost.get_minus()[0]); x < EdgeT::template MainEndX<FieldT>(properExtents[0]); ++x) {
                o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::MainPointStencilCollectionT,
                                          decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::mainCoefs, x, y, z);
              }
            }
          }
        }

        if(static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::PLUS_SIDE::value
        || static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value)
        {
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicFourT,  typename StencilsT<FieldT, DirT>::PositiveStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicThreeT, typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicTwoT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicOneT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");

          //Positive 4
          {
            startZ = EdgeT::template StartPositiveZ<FieldT>(startZ, properExtents[2]);
            startY = EdgeT::template StartPositiveY<FieldT>(startY, properExtents[1]);
            startX = EdgeT::template StartPositiveX<FieldT>(startX, properExtents[0]);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::FourPointPositiveStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::pCoefFour, x, y, z);
                }
              }
            }
          }
          //Positive 3
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::ThreePointPositiveStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::pCoefThree, x, y, z);
                }
              }
            }
          }
          //Positive 2
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::TwoPointPositiveStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::pCoefTwo, x, y, z);
                }
              }
            }
          }
          //Positive 1
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            for(int z = startZ; EdgeT::ContinueZ(startZ, z, properExtents[2]); EdgeT::IncrZ(z)) {
              for(int y = startY; EdgeT::ContinueY(startY, y, properExtents[1]); EdgeT::IncrY(y)) {
                for(int x = startX; EdgeT::ContinueX(startX, x, properExtents[0]); EdgeT::IncrX(x)) {
                  o_lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::OnePointPositiveStencilCollectionT,
                                            decltype(i_rhs)>::eval(i_rhs, StencilsT<FieldT, DirT>::pCoefOne, x, y, z);
                }
              }
            }
          }
        }
      }
    }
#ifdef __CUDACC__
    //GPU Helper Function Collection
    namespace
    {
      template<typename DirT, typename StencilPointCollectionT, typename FieldT>
      __global__ void CudaEvalStencilEdge( const NeboStencilCoefCollection<StencilPointCollectionT::length> i_coefs,
                                           const FieldArgWrapper<FieldT> i_rhs,
                                           const int i_staticIndex,
                                           const int i_firstLow,
                                           const int i_firstHigh,
                                           const int i_secondLow,
                                           const int i_secondHigh,
                                           FieldArgWrapper<FieldT> o_lhs)
      {
        const int first = i_firstLow + blockIdx.x * blockDim.x + threadIdx.x;
        const int second = i_secondLow + blockIdx.y * blockDim.y + threadIdx.y;

        if( first < i_firstHigh && second < i_secondHigh )
        {
          if(DirT::value == XDIR::value)
          {
            o_lhs.ref(i_staticIndex,first,second)
              = EvalExpr<FieldT, StencilPointCollectionT,
                         decltype(i_rhs)>::eval(i_rhs, i_coefs, i_staticIndex, first, second);
          }
          else if(DirT::value == YDIR::value)
          {
            o_lhs.ref(first,i_staticIndex,second)
              = EvalExpr<FieldT, StencilPointCollectionT,
                         decltype(i_rhs)>::eval(i_rhs, i_coefs, first, i_staticIndex, second);
          }
          else if(DirT::value == ZDIR::value)
          {
            o_lhs.ref(first,second,i_staticIndex)
              = EvalExpr<FieldT, StencilPointCollectionT,
                         decltype(i_rhs)>::eval(i_rhs, i_coefs, first, second, i_staticIndex);
          }
        }
      }

      template <typename StencilPointCollectionT, typename FieldT>
      __global__ void CudaEvalStencil( const NeboStencilCoefCollection<StencilPointCollectionT::length> i_coefs,
                                       const FieldArgWrapper<FieldT> i_rhs,
                                       const int i_xLow,
                                       const int i_xHigh,
                                       const int i_yLow,
                                       const int i_yHigh,
                                       const int i_zLow,
                                       const int i_zHigh,
                                       FieldArgWrapper<FieldT> o_lhs)
      {
        const int x = i_xLow + blockIdx.x * blockDim.x + threadIdx.x;
        const int y = i_yLow + blockIdx.y * blockDim.y + threadIdx.y;

        const bool valid = x < i_xHigh && y < i_yHigh;

        for(int z = i_zLow; z < i_zHigh; ++z)
        {
          if(valid)
          {
            o_lhs.ref(x,y,z) = EvalExpr<FieldT, StencilPointCollectionT, decltype(i_rhs)>::eval(i_rhs, i_coefs, x, y, z);
          }
        }
      }

    }
    template<typename DirT, typename DOMAIN_SIDET, typename FieldT>
    inline void CustomTestManualEdgeIteration_GPU( const GhostData i_ghost, const IntVec i_npts, FieldArgWrapper<FieldT>& o_lhs, const FieldArgWrapper<FieldT>& i_rhs )
    {
      typedef IncrementEdge<DirT, DOMAIN_SIDET> EdgeT;
      const IntVec properExtents = i_npts + i_ghost.get_plus();
      {
        int startX = EdgeT::StartNegativeX(-i_ghost.get_minus()[0]);
        int startY = EdgeT::StartNegativeY(-i_ghost.get_minus()[1]);
        int startZ = EdgeT::StartNegativeZ(-i_ghost.get_minus()[2]);

        const int blockDim = 16;
        const int xGDim = properExtents[0] / blockDim + ((properExtents[0] % blockDim) > 0 ? 1 : 0);
        const int yGDim = properExtents[1] / blockDim + ((properExtents[1] % blockDim) > 0 ? 1 : 0);

        const int firstEdgeGDim  = EdgeT::GetRapidIncr(properExtents[0], properExtents[1],properExtents[2]) / blockDim + ((EdgeT::GetRapidIncr(properExtents[0], properExtents[1],properExtents[2]) % blockDim) > 0 ? 1 : 0);
        const int secondEdgeGDim = EdgeT::GetSlowIncr (properExtents[0], properExtents[1],properExtents[2]) / blockDim + ((EdgeT::GetSlowIncr (properExtents[0], properExtents[1],properExtents[2]) % blockDim) > 0 ? 1 : 0);

        const dim3 dimBlock(blockDim, blockDim);
        const dim3 dimGrid(xGDim, yGDim);
        const dim3 dimEdgeGrid(firstEdgeGDim, secondEdgeGDim);

        if(static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::MINUS_SIDE::value
        || static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value)
        {
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicFourT,  typename StencilsT<FieldT, DirT>::NegativeStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicThreeT, typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicTwoT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicOneT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");

          //Negative 1
          {
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::OnePointNegativeStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::nCoefOne,
                                                                    i_rhs,
                                                                    EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                    EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    o_lhs);
          }
          //Negative 2
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::TwoPointNegativeStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::nCoefTwo,
                                                                    i_rhs,
                                                                    EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                    EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    o_lhs);
          }
          //Negative 3
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::ThreePointNegativeStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::nCoefThree,
                                                                      i_rhs,
                                                                      EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                      EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                      EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                      EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                      EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                      o_lhs);
          }
          //Negative 4
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::FourPointNegativeStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::nCoefFour,
                                                                     i_rhs,
                                                                     EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                     EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                     EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                     EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                     EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                     o_lhs);
          }
        }
        //Main
        {
          CudaEvalStencil<typename StencilsT<FieldT, DirT>::MainPointStencilCollectionT><<<dimGrid, dimBlock>>>( StencilsT<FieldT, DirT>::mainCoefs,
                                                        i_rhs,
                                                        EdgeT::template MainStartX<FieldT>(startX, -i_ghost.get_minus()[0]),
                                                        EdgeT::template MainEndX<FieldT>(properExtents[0]),
                                                        EdgeT::template MainStartY<FieldT>(startY, -i_ghost.get_minus()[1]),
                                                        EdgeT::template MainEndY<FieldT>(properExtents[1]),
                                                        EdgeT::template MainStartZ<FieldT>(startZ, -i_ghost.get_minus()[2]),
                                                        EdgeT::template MainEndZ<FieldT>(properExtents[2]),
                                                        o_lhs );
        }

        if(static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::PLUS_SIDE::value
        || static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value)
        {
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicFourT,  typename StencilsT<FieldT, DirT>::PositiveStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicThreeT, typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicTwoT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
          static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicOneT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");

          //Positive 4
          {
            startZ = EdgeT::template StartPositiveZ<FieldT>(startZ, properExtents[2]);
            startY = EdgeT::template StartPositiveY<FieldT>(startY, properExtents[1]);
            startX = EdgeT::template StartPositiveX<FieldT>(startX, properExtents[0]);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::FourPointPositiveStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::pCoefFour,
                                                                     i_rhs,
                                                                     EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                     EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                     EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                     EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                     EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                     o_lhs);

          }
          //Positive 3
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::ThreePointPositiveStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::pCoefThree,
                                                                      i_rhs,
                                                                      EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                      EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                      EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                      EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                      EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                      o_lhs);
          }
          //Positive 2
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::TwoPointPositiveStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::pCoefTwo,
                                                                    i_rhs,
                                                                    EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                    EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    o_lhs);
          }
          //Positive 1
          {
            EdgeT::EdgeIncrX(startX);
            EdgeT::EdgeIncrY(startY);
            EdgeT::EdgeIncrZ(startZ);
            CudaEvalStencilEdge<DirT, typename StencilsT<FieldT, DirT>::OnePointPositiveStencilCollectionT><<<dimEdgeGrid,dimBlock>>>(StencilsT<FieldT, DirT>::pCoefOne,
                                                                    i_rhs,
                                                                    EdgeT::GetStaticIndex(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(startX, startY, startZ),
                                                                    EdgeT::GetRapidIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    EdgeT::GetSlowIncr(startX, startY, startZ),
                                                                    EdgeT::GetSlowIncr(properExtents[0], properExtents[1], properExtents[2]),
                                                                    o_lhs);
          }
        }
      }
    }
#endif /* __CUDACC__ */

  }

  template<typename FieldT, typename DirT, typename DOMAIN_SIDET>
  bool CustomTestManualEdgeIteration( const RunMode i_mode,
                                      TimerPack* i_timerPack,
                                      const int i_deviceIndex,
                                      const IntVec i_npts,
                                      const GhostData i_fieldGhost,
                                      const BoundaryCellInfo i_bcinfo,
                                      OperatorDatabase const & i_opdb,
                                      Grid const & i_grid,
                                      const bool i_printField = false,
                                      SpatFldPtr<FieldT>* o_result = NULL )
  {
    const MemoryWindow mw = get_window_with_ghost(i_npts, i_fieldGhost, i_bcinfo);

    FieldT x     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT y     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT z     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT result( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );

    // set field values
    i_grid.set_coord<XDIR>(x);
    i_grid.set_coord<YDIR>(y);
    i_grid.set_coord<ZDIR>(z);

    x <<= 1;
    result <<= 0;

    FieldArgWrapper<FieldT> lhs(result, i_deviceIndex);
    const FieldArgWrapper<FieldT> rhs(x, i_deviceIndex);

    GhostData rhsGhostWithBC = CalculateRHSGhost<DirT, DOMAIN_SIDET>((*i_opdb.retrieve_operator<typename StencilsT<FieldT, DirT>::MainPointStencilT>())(x).expr());

    GhostData const ghosts = calculate_actual_ghost(true,
        result.get_ghost_data(),
        result.boundary_info(),
        rhsGhostWithBC);

    IntVec const extents = result.window_with_ghost().extent()
                         - result.get_valid_ghost_data().get_minus()
                         - result.get_valid_ghost_data().get_plus();

    if(i_mode == TIMING)
    {
#ifdef ENABLE_THREADS
      throw new std::runtime_error("Manual implementation of varying stencil is not threaded!");
#endif /* ENABLE_THREADS */
      i_timerPack->i_timer.reset();
    }
    //Timed code
    {
      switch(i_deviceIndex)
      {
        case CPU_INDEX:
          CustomTestManualEdgeIteration_CPU<DirT, DOMAIN_SIDET>(ghosts, extents, lhs, rhs);
          break;
#ifdef __CUDACC__
        case GPU_INDEX:
          CustomTestManualEdgeIteration_GPU<DirT, DOMAIN_SIDET>(ghosts, extents, lhs, rhs);
          break;
#endif /* __CUDACC__ */
        default:
          throw new std::runtime_error("Unkown device to execute Manual Edge Test On");
      }
    }
    if(i_mode == TIMING)
    {
#ifdef __CUDACC__
      cudaDeviceSynchronize();
#endif
      i_timerPack->o_time = i_timerPack->i_timer.stop();
    }
    if(i_printField)
    {
      print_field(result, std::cout);
    }

    if(o_result != NULL)
    {
      *o_result = SpatialFieldStore::get<FieldT>(result, i_deviceIndex);
      **o_result = result;
    }

    return true;
 }

  template<typename FieldT, typename DirT, typename DOMAIN_SIDET>
  bool CustomTestManual( const RunMode i_mode,
                         TimerPack* i_timerPack,
                         const int i_deviceIndex,
                         const IntVec i_npts,
                         const GhostData i_fieldGhost,
                         const BoundaryCellInfo i_bcinfo,
                         OperatorDatabase const & i_opdb,
                         Grid const & i_grid,
                         const bool i_printField = false,
                         SpatFldPtr<FieldT>* o_result = NULL )
  {
    const MemoryWindow mw = get_window_with_ghost(i_npts, i_fieldGhost, i_bcinfo);

    FieldT x     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT y     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT z     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT result( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );

    // set field values
    i_grid.set_coord<XDIR>(x);
    i_grid.set_coord<YDIR>(y);
    i_grid.set_coord<ZDIR>(z);

    FieldArgWrapper<FieldT> lhs(result, i_deviceIndex);
    const FieldArgWrapper<FieldT> rhs(x, i_deviceIndex);

    x <<= 1;
    result <<= 0;

    GhostData rhsGhostWithBC = CalculateRHSGhost<DirT, DOMAIN_SIDET>((*i_opdb.retrieve_operator<typename StencilsT<FieldT, DirT>::MainPointStencilT>())(x).expr());

    GhostData const ghosts = calculate_actual_ghost(true,
        result.get_ghost_data(),
        result.boundary_info(),
        rhsGhostWithBC);

    IntVec const extents = result.window_with_ghost().extent()
                         - result.get_valid_ghost_data().get_minus()
                         - result.get_valid_ghost_data().get_plus();

    IntVec const properExtents = extents + ghosts.get_plus();

    if(i_mode == TIMING)
    {
#ifdef ENABLE_THREADS
      throw new std::runtime_error("Manual implementation of varying stencil is not threaded!");
#endif /* ENABLE_THREADS */
      i_timerPack->i_timer.reset();
    }
    //Timed code
    {
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicFourT,  typename StencilsT<FieldT, DirT>::NegativeStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicThreeT, typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicTwoT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::NBasicOneT,   typename StencilsT<FieldT, DirT>::NegativeStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicFourT,  typename StencilsT<FieldT, DirT>::PositiveStencilListT::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicThreeT, typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicTwoT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");
      static_assert(std::is_same<typename StencilsT<FieldT, DirT>::PBasicOneT,   typename StencilsT<FieldT, DirT>::PositiveStencilListT::Collection::Collection::Collection::CurrentType>::value, "Unexpected Stencil in Stencil List");

      {
        for(int z = -ghosts.get_minus()[2]; z < properExtents[2]; z++) {
          for(int y = -ghosts.get_minus()[1]; y < properExtents[1]; y++) {
            for(int x = -ghosts.get_minus()[0]; x < properExtents[0]; x++) {
              int i;
              int negi;
              if(DirT::value == 0)
              {
                i = x;
                negi = properExtents[0] - i - 1;
              }
              else if (DirT::value == 1)
              {
                i = y;
                negi = properExtents[1] - i - 1;
              }
              else if (DirT::value == 2)
              {
                i = z;
                negi = properExtents[2] - i - 1;
              }

              if(i < StencilsT<FieldT, DirT>::NegativeStencilListT::length && (static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::MINUS_SIDE::value
                                                                           ||  static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value))
              {
                switch(i)
                {
                  case 0:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::OnePointNegativeStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::nCoefOne,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  case 1:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::TwoPointNegativeStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::nCoefTwo,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  case 2:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::ThreePointNegativeStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::nCoefThree,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  default:
                  case 3:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::FourPointNegativeStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::nCoefFour,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                }
              }
              else if(negi < StencilsT<FieldT, DirT>::PositiveStencilListT::length && (static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::PLUS_SIDE::value
                                                                                ||  static_cast<int>(DOMAIN_SIDET::value) == DomainEdgeSide::BOTH_SIDE::value))
              {
                switch(negi)
                {
                  case 0:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::OnePointPositiveStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::pCoefOne,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  case 1:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::TwoPointPositiveStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::pCoefTwo,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  case 2:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::ThreePointPositiveStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::pCoefThree,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                  default:
                  case 3:
                    lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::FourPointPositiveStencilCollectionT,
                                              decltype(rhs)>::eval(rhs,
                                                                   StencilsT<FieldT, DirT>::pCoefFour,
                                                                   x,
                                                                   y,
                                                                   z);
                    break;
                }
              }
              else
              {
                lhs.ref(x,y,z) = EvalExpr<FieldT, typename StencilsT<FieldT, DirT>::MainPointStencilCollectionT,
                                          decltype(rhs)>::eval(rhs,
                                                               StencilsT<FieldT, DirT>::mainCoefs,
                                                               x,
                                                               y,
                                                               z);
              }
            }
          }
        }
      }
    }
    if(i_mode == TIMING)
    {
#ifdef __CUDACC__
      cudaDeviceSynchronize();
#endif
      i_timerPack->o_time = i_timerPack->i_timer.stop();
    }
    if(i_printField)
    {
      print_field(result, std::cout);
    }

    if(o_result != NULL)
    {
      *o_result = SpatialFieldStore::get<FieldT>(result, i_deviceIndex);
      **o_result = result;
    }

    return true;
 }

  template<typename FieldT, typename DirT, typename DOMAIN_SIDET>
  bool CustomTest( const RunMode i_mode,
                   TimerPack* i_timerPack,
                   const int i_deviceIndex,
                   const IntVec i_npts,
                   const GhostData i_fieldGhost,
                   const BoundaryCellInfo i_bcinfo,
                   OperatorDatabase const & i_opdb,
                   Grid const & i_grid,
                   const bool i_printField = false,
                   SpatFldPtr<FieldT>* o_result = NULL )
  {
    const MemoryWindow mw = get_window_with_ghost(i_npts, i_fieldGhost, i_bcinfo);

    FieldT x     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT y     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT z     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT result( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );

    // set field values
    i_grid.set_coord<XDIR>(x);
    i_grid.set_coord<YDIR>(y);
    i_grid.set_coord<ZDIR>(z);

    typename StencilsT<FieldT, DirT>::VaryEdgeOpT& op = *i_opdb.retrieve_operator<typename StencilsT<FieldT, DirT>::VaryEdgeOpT>();
    x <<= 1;
    result <<= 0;
    if(i_mode == TIMING)
    {
      i_timerPack->i_timer.reset();
    }
    //Timed code
    {
#ifdef PROFILING
      CALLGRIND_START_INSTRUMENTATION;
#endif /* PROFILING */
      result <<= op.operator()(x);
#ifdef PROFILING
      CALLGRIND_STOP_INSTRUMENTATION;
#endif /* PROFILING */
    }
    if(i_mode == TIMING)
    {
#ifdef __CUDACC__
      cudaDeviceSynchronize();
#endif
      i_timerPack->o_time = i_timerPack->i_timer.stop();
    }
    if(i_printField)
    {
      print_field(result, std::cout);
    }

    if(o_result != NULL)
    {
      *o_result = SpatialFieldStore::get<FieldT>(result, i_deviceIndex);
      **o_result = result;
    }

    return true;
  }
}
#endif /* USING_VARYING_EDGE_STENCIL */

namespace MainStencilTest
{
  template<typename FieldT, typename DirT, typename DOMAIN_SIDET>
  bool CustomTest( const RunMode i_mode,
                   TimerPack* i_timerPack,
                   const int i_deviceIndex,
                   const IntVec i_npts,
                   const GhostData i_fieldGhost,
                   const BoundaryCellInfo i_bcinfo,
                   OperatorDatabase const & i_opdb,
                   Grid const & i_grid,
                   const bool i_printField = false,
                   SpatFldPtr<FieldT>* o_result = NULL )
  {
    const MemoryWindow mw = get_window_with_ghost(i_npts, i_fieldGhost, i_bcinfo);

    FieldT x     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT y     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT z     ( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );
    FieldT result( mw, i_bcinfo, i_fieldGhost, NULL, InternalStorage, i_deviceIndex );

    // set field values
    i_grid.set_coord<XDIR>(x);
    i_grid.set_coord<YDIR>(y);
    i_grid.set_coord<ZDIR>(z);

    typename StencilsT<FieldT, DirT>::MainPointStencilT& op = *i_opdb.retrieve_operator<typename StencilsT<FieldT, DirT>::MainPointStencilT>();
    x <<= 1;
    result <<= 0;
    if(i_mode == TIMING)
    {
      i_timerPack->i_timer.reset();
    }
    //Timed code
    {
      result <<= op.operator()(x);
    }
    if(i_mode == TIMING)
    {
#ifdef __CUDACC__
      cudaDeviceSynchronize();
#endif
      i_timerPack->o_time = i_timerPack->i_timer.stop();
    }
    if(i_printField)
    {
      print_field(result, std::cout);
    }

    if(o_result != NULL)
    {
      *o_result = SpatialFieldStore::get<FieldT>(result, i_deviceIndex);
      **o_result = result;
    }

    return true;
  }
}

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

template<typename FieldT, typename DirT, typename DOMAIN_SIDET>
bool RunTests( const int i_deviceIndex, const double i_length, const IntVec i_npts, const GhostData i_fieldGhost, const BoundaryCellInfo i_bcinfo, Grid const & i_grid, const bool i_printField = false )
{
  TestHelper status( true );
#ifdef USING_VARYING_EDGE_STENCIL
  OperatorDatabase opdb;
  StencilsT<FieldT, DirT>::build_varying_stencils( i_npts[0], i_npts[1], i_npts[2], i_length, i_length, i_length, opdb );

  //Get varying edge result
  SpatFldPtr<FieldT> neboResult;
  NinePointVaryingStencilTest::CustomTest<FieldT, DirT, DOMAIN_SIDET>(CORRECTNESS, NULL, i_deviceIndex, i_npts, i_fieldGhost, i_bcinfo, opdb, i_grid, i_printField, &neboResult);

  //Compare to other results
  SpatFldPtr<FieldT> other;
  //Nine Point Stencil
  if(std::is_same<DomainEdgeSide::NO_SIDE, DOMAIN_SIDET>::value)
  {
    MainStencilTest::CustomTest<FieldT, DirT, DOMAIN_SIDET>(CORRECTNESS, NULL, i_deviceIndex, i_npts, i_fieldGhost, i_bcinfo, opdb, i_grid, i_printField, &other);
    status( field_equal(*neboResult, *other), "Varying edge equal to normal Nebo nine point stencil" );
  }
  if(i_deviceIndex != GPU_INDEX)
  {
    //Manual implementation with linear iteration and if statements
    {
      NinePointVaryingStencilTest::CustomTestManual<FieldT, DirT, DOMAIN_SIDET>(CORRECTNESS, NULL, i_deviceIndex, i_npts, i_fieldGhost, i_bcinfo, opdb, i_grid, i_printField, &other);
      status( field_equal(*neboResult, *other), "Varying edge equal to manual implementation with if statements stencil" );
    }
  }
  //Manual implementation which iterates along edges
  {
    NinePointVaryingStencilTest::CustomTestManualEdgeIteration<FieldT, DirT, DOMAIN_SIDET>(CORRECTNESS, NULL, i_deviceIndex, i_npts, i_fieldGhost, i_bcinfo, opdb, i_grid, i_printField, &other);
    status( field_equal(*neboResult, *other), "Varying edge equal to manual implementation with edge iteration" );
  }
#endif /* USING_VARYING_EDGE_STENCIL */

  return status.ok();
}

template<typename FieldT>
bool Run(const double i_length, const IntVec& i_npts, const GhostData& i_ghost, const IntVec& i_bcMinus, const IntVec& i_bcPlus, const bool i_toTime, const size_t i_timingIterations)
{
  const BoundaryCellInfo bcinfo = BoundaryCellInfo::build<FieldT>(i_bcMinus, i_bcPlus);

  TestHelper status( true );

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

  typedef DomainEdgeSide::NO_SIDE DOMAIN_SIDET;
  typedef XDIR DirT;

  const Grid grid( i_npts, DoubleVec(i_length, i_length, i_length) );

# ifndef __CUDACC__
      const int deviceIndex = CPU_INDEX;
# else
      const int deviceIndex = GPU_INDEX;
#endif

  if(i_toTime)
  {
    Timer timer;
    OperatorDatabase opdb;
#ifndef USING_VARYING_EDGE_STENCIL
    build_stencils                         ( i_npts[0], i_npts[1], i_npts[2], i_length, i_length, i_length, opdb );
#else
    StencilsT<FieldT, DirT>::build_varying_stencils( i_npts[0], i_npts[1], i_npts[2], i_length, i_length, i_length, opdb );
#endif /* !USING_VARYING_EDGE_STENCIL */

    //Manual main stencil
    {
      std::function<double(Timer&)> lambda = [&](Timer& i_timer)
      {
        TimerPack pack(i_timer);
        MainStencilTest::CustomTest<FieldT, DirT, DOMAIN_SIDET>(TIMING, &pack, deviceIndex, i_npts, i_ghost, bcinfo, opdb, grid);
        return pack.o_time;
      };

      std::cout << "Time with Single Stencil (Main 9 point): "
                << ReturnMedian(lambda, timer, i_timingIterations)
                << std::endl;
    }
#ifdef USING_VARYING_EDGE_STENCIL
    //Varying edge stencil
    {
      std::function<double(Timer&)> lambda = [&](Timer& i_timer)
      {
        TimerPack pack(i_timer);
        NinePointVaryingStencilTest::CustomTest<FieldT, DirT, DOMAIN_SIDET>(TIMING, &pack, deviceIndex, i_npts, i_ghost, bcinfo, opdb, grid);
        return pack.o_time;
      };

      std::cout << "Time with Nebo: "
                << ReturnMedian(lambda, timer, i_timingIterations)
                << std::endl;
    }
    //Manual varying edge stencil
    {
      std::function<double(Timer&)> lambda = [&](Timer& i_timer)
      {
        TimerPack pack(i_timer);
        NinePointVaryingStencilTest::CustomTestManual<FieldT, DirT, DOMAIN_SIDET>(TIMING, &pack, deviceIndex, i_npts, i_ghost, bcinfo, opdb, grid);
        return pack.o_time;
      };

      std::cout << "Time via Manual: "
                << ReturnMedian(lambda, timer, i_timingIterations)
                << std::endl;
    }
    //Manual varying edge stencil edge iteration
    {
      std::function<double(Timer&)> lambda = [&](Timer& i_timer)
      {
        TimerPack pack(i_timer);
        NinePointVaryingStencilTest::CustomTestManualEdgeIteration<FieldT, DirT, DOMAIN_SIDET>(TIMING, &pack, deviceIndex, i_npts, i_ghost, bcinfo, opdb, grid);
        return pack.o_time;
      };

      std::cout << "Time with iteraing along edges: "
                << ReturnMedian(lambda, timer, i_timingIterations)
                << std::endl;
    }
#endif /* USING_VARYING_EDGE_STENCIL */
  }
  else
  {
#ifdef USING_VARYING_EDGE_STENCIL
    const bool printField = false;

    const int xSideExecute = i_bcMinus[0] != 0 ? (i_bcPlus[0] != 0 ? static_cast<int>(DomainEdgeSide::BOTH_SIDE::value) : static_cast<int>(DomainEdgeSide::MINUS_SIDE::value))
                                               : (i_bcPlus[0] != 0 ? static_cast<int>(DomainEdgeSide::PLUS_SIDE::value) : static_cast<int>(DomainEdgeSide::NO_SIDE::value));
    const int ySideExecute = i_bcMinus[1] != 0 ? (i_bcPlus[1] != 0 ? static_cast<int>(DomainEdgeSide::BOTH_SIDE::value) : static_cast<int>(DomainEdgeSide::MINUS_SIDE::value))
                                               : (i_bcPlus[1] != 0 ? static_cast<int>(DomainEdgeSide::PLUS_SIDE::value) : static_cast<int>(DomainEdgeSide::NO_SIDE::value));
    const int zSideExecute = i_bcMinus[2] != 0 ? (i_bcPlus[2] != 0 ? static_cast<int>(DomainEdgeSide::BOTH_SIDE::value) : static_cast<int>(DomainEdgeSide::MINUS_SIDE::value))
                                               : (i_bcPlus[2] != 0 ? static_cast<int>(DomainEdgeSide::PLUS_SIDE::value) : static_cast<int>(DomainEdgeSide::NO_SIDE::value));

    switch(xSideExecute)
    {
      case DomainEdgeSide::NO_SIDE::value:
        status( RunTests<FieldT, XDIR, DomainEdgeSide::NO_SIDE   >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "XDIR NO_SIDE");
        break;
      case DomainEdgeSide::MINUS_SIDE::value:
        status( RunTests<FieldT, XDIR, DomainEdgeSide::MINUS_SIDE>( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "XDIR MINUS_SIDE");
        break;
      case DomainEdgeSide::PLUS_SIDE::value:
        status( RunTests<FieldT, XDIR, DomainEdgeSide::PLUS_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "XDIR PLUS_SIDE");
        break;
      case DomainEdgeSide::BOTH_SIDE::value:
        status( RunTests<FieldT, XDIR, DomainEdgeSide::BOTH_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "XDIR BOTH_SIDE");
        break;
    }
    switch(ySideExecute)
    {
      case DomainEdgeSide::NO_SIDE::value:
        status( RunTests<FieldT, YDIR, DomainEdgeSide::NO_SIDE   >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "YDIR NO_SIDE");
        break;
      case DomainEdgeSide::MINUS_SIDE::value:
        status( RunTests<FieldT, YDIR, DomainEdgeSide::MINUS_SIDE>( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "YDIR MINUS_SIDE");
        break;
      case DomainEdgeSide::PLUS_SIDE::value:
        status( RunTests<FieldT, YDIR, DomainEdgeSide::PLUS_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "YDIR PLUS_SIDE");
        break;
      case DomainEdgeSide::BOTH_SIDE::value:
        status( RunTests<FieldT, YDIR, DomainEdgeSide::BOTH_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "YDIR BOTH_SIDE");
        break;
    }
    switch(zSideExecute)
    {
      case DomainEdgeSide::NO_SIDE::value:
        status( RunTests<FieldT, ZDIR, DomainEdgeSide::NO_SIDE   >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "ZDIR NO_SIDE");
        break;
      case DomainEdgeSide::MINUS_SIDE::value:
        status( RunTests<FieldT, ZDIR, DomainEdgeSide::MINUS_SIDE>( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "ZDIR MINUS_SIDE");
        break;
      case DomainEdgeSide::PLUS_SIDE::value:
        status( RunTests<FieldT, ZDIR, DomainEdgeSide::PLUS_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "ZDIR PLUS_SIDE");
        break;
      case DomainEdgeSide::BOTH_SIDE::value:
        status( RunTests<FieldT, ZDIR, DomainEdgeSide::BOTH_SIDE >( deviceIndex, i_length, i_npts, i_ghost, bcinfo, grid, printField ), "ZDIR BOTH_SIDE");
        break;
    }
#endif /* USING_VARYING_EDGE_STENCIL */
  }

  return status.ok();
}

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

enum FieldTypeEnum
{
  SVOL,
  XVOL,
  YVOL,
  ZVOL,
};

std::istream& operator>>(std::istream& in, FieldTypeEnum& field)
{
    std::string token;
    in >> token;
    if (token == "SVOL")
        field = SVOL;
    else if (token == "XVOL")
      field = XVOL;
    else if (token == "YVOL")
      field = YVOL;
    else if (token == "ZVOL")
      field = ZVOL;
    else
        in.setstate(std::ios_base::failbit);
    return in;
}

int main( int iarg, char* carg[] )
{
  int nx, ny, nz;
  double length;
  size_t timingIterations;
  bool toTime;
  bool pbcx = false, pbcy = false, pbcz = false;
  bool nbcx = false, nbcy = false, nbcz = false;
  int pgx, pgy, pgz;
  int ngx, ngy, ngz;
  FieldTypeEnum fieldEnum;
  {
    po::options_description desc("Supported Options");
    desc.add_options()
          ( "help", "print help message\n" )
          ( "timing",  "run timing tests instead of correctness tests" )
          ( "timing-iterations",   po::value<size_t>(&timingIterations)->default_value(100), "Number of iterations to run while timing of which the median is taken" )
          ( "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" )
          ( "pbcx", "positive boundary condition on x-dir or not" )
          ( "pbcy", "positive boundary condition on y-dir or not" )
          ( "pbcz", "positive boundary condition on z-dir or not" )
          ( "nbcx", "negative boundary condition on x-dir or not" )
          ( "nbcy", "negative boundary condition on y-dir or not" )
          ( "nbcz", "negative boundary condition on z-dir or not" )
          ( "pgx", po::value<int>   ( &pgx     )->default_value( 1   ), "number of ghosts in positive x-dir for base mesh" )
          ( "pgy", po::value<int>   ( &pgy     )->default_value( 1   ), "number of ghosts in positive y-dir for base mesh" )
          ( "pgz", po::value<int>   ( &pgz     )->default_value( 1   ), "number of ghosts in positive z-dir for base mesh" )
          ( "ngx", po::value<int>   ( &ngx     )->default_value( 1   ), "number of ghosts in negative x-dir for base mesh" )
          ( "ngy", po::value<int>   ( &ngy     )->default_value( 1   ), "number of ghosts in negative y-dir for base mesh" )
          ( "ngz", po::value<int>   ( &ngz     )->default_value( 1   ), "number of ghosts in negative z-dir for base mesh" )
          ( "Field", po::value<FieldTypeEnum>(&fieldEnum), "type of field to run tests on [SVOL, XVOL, YVOL, or ZVOL]" )
          ( "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;
    }

    toTime = ( args.count("timing") > 0 );

    if( args.count("pbcx") ) pbcx = true;
    if( args.count("pbcy") ) pbcy = true;
    if( args.count("pbcz") ) pbcz = true;
    if( args.count("nbcx") ) nbcx = true;
    if( args.count("nbcy") ) nbcy = true;
    if( args.count("nbcz") ) nbcz = true;
  }

  const IntVec bcMinus(nbcx, nbcy, nbcz);
  const IntVec bcPlus(pbcx, pbcy, pbcz);
  const GhostData ghost(ngx, pgx, ngy, pgy, ngz, pgz);
  const IntVec npts(nx,ny,nz);

  bool ret;
  {
    switch(fieldEnum)
    {
      default:
      case SVOL:
        cout << "Field: SVolField" << endl;
        ret = Run<SVolField>(length, npts, ghost, bcMinus, bcPlus, toTime, timingIterations);
        break;
      case XVOL:
        cout << "Field: XVolField" << endl;
        ret = Run<XVolField>(length, npts, ghost, bcMinus, bcPlus, toTime, timingIterations);
        break;
      case YVOL:
        cout << "Field: YVolField" << endl;
        ret = Run<YVolField>(length, npts, ghost, bcMinus, bcPlus, toTime, timingIterations);
        break;
      case ZVOL:
        cout << "Field: ZVolField" << endl;
        ret = Run<ZVolField>(length, npts, ghost, bcMinus, bcPlus, toTime, timingIterations);
        break;
    }
  }

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

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


/**
 * $Id$
 *
 * Copyright (C)
 * 2015 - $Date$
 *     Martin Wolf <ndhist@martin-wolf.org>
 *
 * This file is distributed under the BSD 2-Clause Open Source License
 * (See LICENSE file).
 *
 */
#include <boost/python.hpp>

#include <ndhist/stats/var.hpp>

namespace bp = boost::python;

namespace ndhist {
namespace stats {

void register_var()
{
    bp::def("var"
      , &py::var
      , ( bp::arg("hist")
        , bp::arg("axis")=bp::object()
        )
      , "Calculates the variance along the given axis of the given ndhist    \n"
        "object. As in statistics, the variance is defined as                \n"
        ":math:`V[x] = E[x^2] - E[x]^2`.                                     \n"
        "This function generates a projection along the given axis and then  \n"
        "calculates the variance.                                            \n"
        "If ``None`` is given as axis argument (the default), the variance   \n"
        "for all individual axes of the ndhist object is calculated and      \n"
        "returned as a tuple. But if the dimensionality of the histogram is  \n"
        "1, a scalar value is returned.                                      \n"
        "                                                                    \n"
        ".. note:: This function is only defined for ndhist objects with POD \n"
        "          type axis values AND POD type weight values.              \n"
    );
}

}// namespace stats
}// namespace ndhist


// Authors: David Alexander, Lance Hepler

#include <pbcopper/align/AffineAlignment.h>

#include <cassert>
#include <cfloat>

#include <algorithm>
#include <string>
#include <vector>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

#include <pbcopper/align/PairwiseAlignment.h>
#include <pbcopper/utility/MinMax.h>
#include <pbcopper/utility/SequenceUtils.h>

namespace PacBio {
namespace Align {

namespace {

class IupacAware;
class Standard;

inline bool IsIupacPartialMatch(char iupacCode, char b)
{
    assert(iupacCode != b);

    switch (iupacCode) {
        case 'R':
            return (b == 'A' || b == 'G');
        case 'Y':
            return (b == 'C' || b == 'T');
        case 'S':
            return (b == 'G' || b == 'C');
        case 'W':
            return (b == 'A' || b == 'T');
        case 'K':
            return (b == 'G' || b == 'T');
        case 'M':
            return (b == 'A' || b == 'C');
        default:
            return false;
    }
}

template <typename C>
float MatchScore(char t, char q, float matchScore, float mismatchScore, float partialMatchScore);

template <>
float MatchScore<Standard>(char t, char q, float matchScore, float mismatchScore, float /*unused*/)
{
    return (t == q ? matchScore : mismatchScore);
}

template <>
float MatchScore<IupacAware>(char t, char q, float matchScore, float mismatchScore,
                             float partialMatchScore)
{
    if (t == q) {
        return matchScore;
    } else if (IsIupacPartialMatch(t, q) || IsIupacPartialMatch(q, t)) {
        return partialMatchScore;
    } else {
        return mismatchScore;
    }  // NOLINT
}

template <class C>
PairwiseAlignment* AlignAffineGeneric(const std::string& target, const std::string& query,
                                      AffineAlignmentParams params)
{
    // Implementation follows the textbook "two-state" affine gap model
    // description from Durbin et. al
    using boost::numeric::ublas::matrix;

    int I = query.length();
    int J = target.length();
    matrix<float> M(I + 1, J + 1);
    matrix<float> GAP(I + 1, J + 1);

    // Initialization
    M(0, 0) = 0;
    GAP(0, 0) = -FLT_MAX;
    for (int i = 1; i <= I; ++i) {
        M(i, 0) = -FLT_MAX;
        GAP(i, 0) = params.GapOpen + (i - 1) * params.GapExtend;
    }
    for (int j = 1; j <= J; ++j) {
        M(0, j) = -FLT_MAX;
        GAP(0, j) = params.GapOpen + (j - 1) * params.GapExtend;
    }

    // Main part of the recursion
    for (int i = 1; i <= I; ++i) {
        for (int j = 1; j <= J; ++j) {
            float matchScore = MatchScore<C>(target[j - 1], query[i - 1], params.MatchScore,
                                             params.MismatchScore, params.PartialMatchScore);
            M(i, j) = std::max(M(i - 1, j - 1), GAP(i - 1, j - 1)) + matchScore;
            GAP(i, j) =
                Utility::Max(M(i, j - 1) + params.GapOpen, GAP(i, j - 1) + params.GapExtend,
                             M(i - 1, j) + params.GapOpen, GAP(i - 1, j) + params.GapExtend);
        }
    }

    // Perform the traceback
    const int MATCH_MATRIX = 1;
    const int GAP_MATRIX = 2;

    std::string raQuery;
    std::string raTarget;
    int i = I;
    int j = J;
    int mat = (M(I, J) >= GAP(I, J) ? MATCH_MATRIX : GAP_MATRIX);
    int iPrev;
    int jPrev;
    int matPrev;
    while (i > 0 || j > 0) {
        if (mat == MATCH_MATRIX) {
            matPrev = (M(i - 1, j - 1) >= GAP(i - 1, j - 1) ? MATCH_MATRIX : GAP_MATRIX);
            iPrev = i - 1;
            jPrev = j - 1;
            raQuery.push_back(query[iPrev]);
            raTarget.push_back(target[jPrev]);
        } else {
            assert(mat == GAP_MATRIX);
            float s[4];
            s[0] = (j > 0 ? M(i, j - 1) + params.GapOpen : -FLT_MAX);
            s[1] = (j > 0 ? GAP(i, j - 1) + params.GapExtend : -FLT_MAX);
            s[2] = (i > 0 ? M(i - 1, j) + params.GapOpen : -FLT_MAX);
            s[3] = (i > 0 ? GAP(i - 1, j) + params.GapExtend : -FLT_MAX);
            int argMax = std::max_element(s, s + 4) - s;

            matPrev = ((argMax == 0 || argMax == 2) ? MATCH_MATRIX : GAP_MATRIX);
            if (argMax == 0 || argMax == 1) {
                iPrev = i;
                jPrev = j - 1;
                raQuery.push_back('-');
                raTarget.push_back(target[jPrev]);
            } else {
                iPrev = i - 1;
                jPrev = j;
                raQuery.push_back(query[iPrev]);
                raTarget.push_back('-');
            }
        }

        // Go to previous square
        i = iPrev;
        j = jPrev;
        mat = matPrev;
    }

    assert(raQuery.length() == raTarget.length());
    return new PairwiseAlignment(Utility::Reversed(raTarget), Utility::Reversed(raQuery));
}

}  // anonymous namespace

AffineAlignmentParams::AffineAlignmentParams(float matchScore, float mismatchScore, float gapOpen,
                                             float gapExtend, float partialMatchScore)
    : MatchScore(matchScore)
    , MismatchScore(mismatchScore)
    , GapOpen(gapOpen)
    , GapExtend(gapExtend)
    , PartialMatchScore(partialMatchScore)
{
}

AffineAlignmentParams DefaultAffineAlignmentParams() { return {0, -1.0, -1.0, -0.5, 0}; }

AffineAlignmentParams IupacAwareAffineAlignmentParams() { return {0, -1.0, -1.0, -0.5, -0.25}; }

PairwiseAlignment* AlignAffine(const std::string& target, const std::string& query,
                               AffineAlignmentParams params)
{
    return AlignAffineGeneric<Standard>(target, query, params);
}

PairwiseAlignment* AlignAffineIupac(const std::string& target, const std::string& query,
                                    AffineAlignmentParams params)
{
    return AlignAffineGeneric<IupacAware>(target, query, params);
}

}  // namespace Align
}  // namespace PacBio