// Copyright 2014-2015 SDL plc
// 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

    semirings used for Arc for Hypergraph.
*/

#ifndef HYP__HYPERGRAPH_WEIGHT_HPP
#define HYP__HYPERGRAPH_WEIGHT_HPP
#pragma once

/**
   Weight: semiring (zero, one, plus, times, less,==).

   note: some more expensive semirings' zero and one will not be usable until
   main() (static globals). singleton lazy-init pattern not worth the overhead.
*/

#include <sdl/Hypergraph/Exception.hpp>
#include <sdl/Hypergraph/Types.hpp>
#include <sdl/Hypergraph/WeightBase.hpp>
#include <sdl/Util/Constants.hpp>
#include <sdl/Util/Hash.hpp>
#include <sdl/Util/LogHelper.hpp>
#include <sdl/Util/LogMath.hpp>
#include <sdl/Util/Math.hpp>
#include <sdl/LexicalCast.hpp>
#include <boost/math/special_functions/log1p.hpp>

#if HAVE_OPENFST
// OpenFst weight compatability - needed for ToReplaceFst
#define DEFINE_OPENFST_COMPAT_FUNCTIONS(name)            \
  static std::string const& Type() {                     \
    static std::string const r(#name);                   \
    return r;                                            \
  }                                                      \
  static const Self One() { return Self::one(); }        \
  static const Self Zero() { return Self::zero(); }      \
  void Write(std::ostream& o) const { o << *this; }      \
  static inline std::size_t Properties() { return 0x3; } \
  typedef Self ReverseWeight;                            \
  Self& Reverse() { return *this; }                      \
  Self const& Reverse() const { return *this; }
#else
#define DEFINE_OPENFST_COMPAT_FUNCTIONS(name)
#endif

#define SDL_DEFINE_FLOATWT_CMP(c, cmp)                \
  template <class T>                                  \
  bool operator cmp(c<T> const& w1, c<T> const& w2) { \
    return w1.value_ cmp w2.value_;                   \
  }

#define SDL_DEFINE_FLOATWT_CMPS(c) \
  SDL_DEFINE_FLOATWT_CMP(c, ==)    \
  SDL_DEFINE_FLOATWT_CMP(c, >=)    \
  SDL_DEFINE_FLOATWT_CMP(c, <=)    \
  SDL_DEFINE_FLOATWT_CMP(c, !=)    \
  SDL_DEFINE_FLOATWT_CMP(c, <)     \
  SDL_DEFINE_FLOATWT_CMP(c, >)

// TODO: #if HAVE_OPENFST and use constants appropriate for particular semiring - this is just to compile
// HgFsmDraw

// TODO: OpenFst Divide. use sdl::Util::logPlus?

namespace sdl {
namespace Hypergraph {

/**
   Simple wrapper weight around a float value. Base class for
   Viterbi weight, Log weight, etc.
 */
template <class T>
class FloatWeightTpl : public WeightBase {
 protected:
  typedef typename Util::OtherFloat<T>::type DoubleT;

 public:
  typedef T FloatT;

  static inline constexpr FloatT kOneValue() { return 0; }
  // TODO: C++11 constexpr
  static inline constexpr FloatT kZeroValue() { return std::numeric_limits<T>::infinity(); }

  typedef void HasIsZero;
  bool isZero() const { return value_ == std::numeric_limits<T>::infinity(); }
  friend inline void setZero(FloatWeightTpl& x) { x.value_ = std::numeric_limits<T>::infinity(); }

  typedef void HasIsOne;
  bool isOne() const { return !value_; }
  friend inline void setOne(FloatWeightTpl& x) { x.value_ = 0; }

  /// uninitialized - we want users to explicitly init to zero or one as
  /// appropriate e.g. vector<ViterbiWeight>(N, ViterbiWeight::zero())
  constexpr FloatWeightTpl() = default;  // uninitialized but an explicit weight() init would then 0-init?
  constexpr FloatWeightTpl(T v) : value_(v) {}
  constexpr FloatWeightTpl(DoubleT v) : value_((T)v) {}
  constexpr FloatWeightTpl(int v) : value_((T)v) {}
  constexpr FloatWeightTpl(std::size_t v) : value_((T)v) {}

  FloatWeightTpl(FloatWeightTpl const& o) = default;
  FloatWeightTpl& operator=(FloatWeightTpl const& o) = default;
  FloatWeightTpl(FloatWeightTpl&& o) = default;
  FloatWeightTpl& operator=(FloatWeightTpl&& o) = default;

  T& value() { return value_; }

  T getValue() const { return value_; }

  void set(std::string const& s) { value_ = sdl::lexical_cast<T>(s); }

  T value_;
};

SDL_DEFINE_FLOATWT_CMPS(FloatWeightTpl)

template <class T>
std::istream& operator>>(std::istream& i, FloatWeightTpl<T>& x) {
  return i >> x.value();
}
template <class T>
std::ostream& operator<<(std::ostream& o, FloatWeightTpl<T> const& x) {
  return o << x.value_;
}

template <class T>
inline std::size_t hashWeight(FloatWeightTpl<T> const& w) {
  return Util::hashFloat(w.value_);
}

template <class T>
inline bool approxEqual(FloatWeightTpl<T> const& w1, FloatWeightTpl<T> const& w2,
                        T epsilon = FloatConstants<T>::epsilon) {
  return Util::floatEqual(w1.value_, w2.value_, epsilon);
}

template <class T>
inline bool approxGreaterOrEqual(FloatWeightTpl<T> const& w1, FloatWeightTpl<T> const& w2,
                                 T epsilon = FloatConstants<T>::epsilon) {
  return Util::approxGreaterOrEqual(w1.value_, w2.value_, epsilon);
}

template <class T>
inline bool approxLessOrEqual(FloatWeightTpl<T> const& w1, FloatWeightTpl<T> const& w2,
                              T epsilon = FloatConstants<T>::epsilon) {
  return Util::approxLessOrEqual(w1.value_, w2.value_, epsilon);
}

template <class T>
inline bool definitelyGreater(FloatWeightTpl<T> const& w1, FloatWeightTpl<T> const& w2,
                              T epsilon = FloatConstants<T>::epsilon) {
  return Util::definitelyGreater(w1.value_, w2.value_, epsilon);
}

template <class T>
inline bool definitelyLess(FloatWeightTpl<T> const& w1, FloatWeightTpl<T> const& w2,
                           T epsilon = FloatConstants<T>::epsilon) {
  return Util::definitelyLess(w1.value_, w2.value_, epsilon);
}

// all 3 of approx equal, greater, less may be true simultaneously (if same eps, then approxEqual <=>
// approxGreater and approxLess

class BooleanWeight : public FloatWeightTpl<bool> {
 public:
  typedef bool FloatT;

  BooleanWeight() : FloatWeightTpl<bool>() {}
  static inline constexpr bool kOneValue() { return 1; }
  static inline constexpr bool kZeroValue() { return 0; }

  // explicit only because somebody was wrongly activating this implicitly (they should use safe_bool idiom?)
  explicit constexpr BooleanWeight(bool v) : FloatWeightTpl<bool>(v) {}

  typedef BooleanWeight Self;
  DEFINE_OPENFST_COMPAT_FUNCTIONS(Boolean)

  typedef void HasIsOne;
  bool isOne() const { return value_ == true; }
  friend inline void setOne(BooleanWeight& x) { x.value_ = true; }
  static inline constexpr BooleanWeight one() { return BooleanWeight(true); }

  typedef void HasIsZero;
  bool isZero() const { return value_ == false; }
  friend inline void setZero(BooleanWeight& x) { x.value_ = false; }
  static inline constexpr BooleanWeight zero() { return BooleanWeight(false); }
};

template <class T>
class ViterbiWeightTpl : public FloatWeightTpl<T> {
  typedef ViterbiWeightTpl<T> Self;
  typedef FloatWeightTpl<T> Base;

 public:
  typedef void HasIsZero;
  bool isZero() const { return this->value_ == std::numeric_limits<T>::infinity(); }
  typedef void HasIsOne;
  bool isOne() const { return !this->value_; }

  typedef T FloatT;

  constexpr ViterbiWeightTpl() : Base() {}

  constexpr ViterbiWeightTpl(T v) : Base(v) {}
  constexpr ViterbiWeightTpl(int v) : Base(v) {}
  constexpr ViterbiWeightTpl(std::size_t v) : Base(v) {}
  constexpr ViterbiWeightTpl(typename Base::DoubleT v) : Base(v) {}

  ViterbiWeightTpl(ViterbiWeightTpl const& o) = default;
  ViterbiWeightTpl& operator=(ViterbiWeightTpl const& o) = default;
  ViterbiWeightTpl(ViterbiWeightTpl&& o) = default;
  ViterbiWeightTpl& operator=(ViterbiWeightTpl&& o) = default;

  Self& operator=(Base const& other) {
    this->value_ = other.value_;
    return *this;
  }

  static inline constexpr ViterbiWeightTpl<T> one() { return ViterbiWeightTpl<T>(0.0f); }

  static inline constexpr ViterbiWeightTpl<T> zero() {
    return ViterbiWeightTpl<T>(std::numeric_limits<T>::infinity());
  }

  void plusBy(Self const& b) {
    if (b.value_ < this->value_) this->value_ = b.value_;
  }
  typedef void HasPlusBy;

  void timesBy(Self const& b) { this->value_ += b.value_; }
  typedef void HasTimesBy;

  DEFINE_OPENFST_COMPAT_FUNCTIONS(Viterbi)
};

template <class T>
ViterbiWeightTpl<T> plus(ViterbiWeightTpl<T> const& w1, ViterbiWeightTpl<T> const& w2) {
  return w1.value_ < w2.value_ ? w1 : w2;
}

template <class T>
ViterbiWeightTpl<T> minus(ViterbiWeightTpl<T> const& w1, ViterbiWeightTpl<T> const& w2) {
  SDL_THROW_LOG(Hypergraph, UnimplementedException, "Viterbi minus is not supported");
  return w1;  // make compiler happy
}

template <class T>
inline ViterbiWeightTpl<T> times(ViterbiWeightTpl<T> const& w1, ViterbiWeightTpl<T> const& w2) {
  if (w1 == ViterbiWeightTpl<T>::zero() || w2 == ViterbiWeightTpl<T>::zero())
    return ViterbiWeightTpl<T>::zero();
  return ViterbiWeightTpl<T>(w1.value_ + w2.value_);
}

template <class T>
inline bool less(ViterbiWeightTpl<T> const& w1, ViterbiWeightTpl<T> const& w2) {
  return w1.value_ < w2.value_;
}

template <class T>
inline ViterbiWeightTpl<T> divide(ViterbiWeightTpl<T> const& w1, ViterbiWeightTpl<T> const& w2) {
  if (w1 == ViterbiWeightTpl<T>::zero() || w2 == ViterbiWeightTpl<T>::zero())
    // Technically can't divide by 0. but practically ok.
    return ViterbiWeightTpl<T>::zero();
  return ViterbiWeightTpl<T>(w1.value_ - w2.value_);
}

template <class T>
inline ViterbiWeightTpl<T> invert(ViterbiWeightTpl<T> const& w) {
  return ViterbiWeightTpl<T>(-w.value_);
}

template <class T>
inline ViterbiWeightTpl<T> pow(ViterbiWeightTpl<T> const& w, T k) {
  return ViterbiWeightTpl<T>(k * w.value_);
}

template <class T>
class LogWeightTpl : public FloatWeightTpl<T> {

  typedef FloatWeightTpl<T> Base;
  typedef LogWeightTpl Self;

 public:
  typedef void HasIsZero;
  bool isZero() const { return this->value_ == std::numeric_limits<T>::infinity(); }

  typedef void HasIsOne;
  bool isOne() const { return !this->value_; }

  typedef T FloatT;

  constexpr LogWeightTpl() : Base() {}

  constexpr LogWeightTpl(T v) : Base(v) {}
  constexpr LogWeightTpl(typename Base::DoubleT v) : Base(v) {}
  constexpr LogWeightTpl(int v) : Base(v) {}
  constexpr LogWeightTpl(std::size_t v) : Base(v) {}
  LogWeightTpl(LogWeightTpl const& o) = default;
  LogWeightTpl& operator=(LogWeightTpl const& o) = default;
  LogWeightTpl(LogWeightTpl&& o) = default;
  LogWeightTpl& operator=(LogWeightTpl&& o) = default;

  static inline constexpr LogWeightTpl<T> one() { return LogWeightTpl<T>(0.0f); }

  static inline constexpr LogWeightTpl<T> zero() {
    return LogWeightTpl<T>(std::numeric_limits<T>::infinity());
  }

  Self& operator=(Base const& other) {
    this->value_ = other.value_;
    return *this;
  }

  void timesBy(LogWeightTpl const& b) { this->value_ += b.value_; }
  typedef void HasTimesBy;

  DEFINE_OPENFST_COMPAT_FUNCTIONS(Log)
};

template <class T>
LogWeightTpl<T> plus(LogWeightTpl<T> const& w1, LogWeightTpl<T> const& w2) {
  return {Util::neglogPlus(w1.value_, w2.value_)};
}

template <class T>
LogWeightTpl<T> minus(LogWeightTpl<T> const& w1, LogWeightTpl<T> const& w2) {
  return {Util::neglogMinus(w1.value_, w2.value_)};
}

template <class T>
inline LogWeightTpl<T> times(LogWeightTpl<T> const& w1, LogWeightTpl<T> const& w2) {
  if (w1 == LogWeightTpl<T>::zero() || w2 == LogWeightTpl<T>::zero()) return LogWeightTpl<T>::zero();
  return LogWeightTpl<T>(w1.value_ + w2.value_);
}

template <class T>
inline bool less(LogWeightTpl<T> const& w1, LogWeightTpl<T> const& w2) {
  return w1.value_ < w2.value_;
}

template <class T>
inline LogWeightTpl<T> divide(LogWeightTpl<T> const& w1, LogWeightTpl<T> const& w2) {
  if (w1 == LogWeightTpl<T>::zero() || w2 == LogWeightTpl<T>::zero()) {
    // Technically can't divide by zero. but practically ok.
    return LogWeightTpl<T>::zero();
  }
  return LogWeightTpl<T>(w1.value_ - w2.value_);
}

template <class T>
inline LogWeightTpl<T> invert(LogWeightTpl<T> const& w) {
  return LogWeightTpl<T>(-w.value_);
}

template <class T>
inline LogWeightTpl<T> pow(LogWeightTpl<T> const& w, T k) {
  return LogWeightTpl<T>(k * w.value_);
}

typedef LogWeightTpl<float> LogWeight;
typedef ViterbiWeightTpl<float> ViterbiWeight;

inline BooleanWeight plus(BooleanWeight const& w1, BooleanWeight const& w2) {
  return BooleanWeight(w1.value_ || w2.value_);
}

inline BooleanWeight times(BooleanWeight const& w1, BooleanWeight const& w2) {
  return BooleanWeight(w1.value_ && w2.value_);
}

inline bool less(BooleanWeight const& w1, BooleanWeight const& w2) {
  return w1.value_ < w2.value_;
}

inline BooleanWeight divide(BooleanWeight const& w1, BooleanWeight const& w2) {
  SDL_THROW_LOG(Hypergraph, UnimplementedException, "Boolean divide is not supported");
  return w1;  // make compiler happy
}

inline BooleanWeight invert(BooleanWeight const& w1, BooleanWeight const& w2) {
  SDL_THROW_LOG(Hypergraph, UnimplementedException, "Boolean invert is not supported");
  return w1;  // make compiler happy
}

inline BooleanWeight pow(BooleanWeight const& w, bool p) {
  return p ? w : BooleanWeight(true);
}

template <class Weight>
char const* weightName(Weight*) {
  return "Weight";
}

template <class Weight>
char const* weightName() {
  return weightName((Weight*)0);
}

template <class T>
inline char const* weightName(ViterbiWeightTpl<T>*) {
  return "Viterbi";
}

template <class T>
inline char const* weightName(LogWeightTpl<T>*) {
  return "Log";
}

inline char const* weightName(BooleanWeight*) {
  return "Boolean";
}

}}

#endif


#include "pcraster_multicore/python/local/cover.h"

#include <boost/python.hpp>

// PCRaster
#include "calc_spatial.h"
#include "calc_nonspatial.h"
#include "calc_vs.h"

// Field wrapper
#include "pcraster_multicore/wrapper/datatype_customization_points/multicore_spatial.h"
#include "pcraster_multicore/wrapper/datatype_traits/multicore_spatial.h"
#include "pcraster_multicore/wrapper/argument_traits/multicore_spatial.h"

#include "pcraster_multicore/wrapper/datatype_customization_points/multicore_nonspatial.h"
#include "pcraster_multicore/wrapper/datatype_traits/multicore_nonspatial.h"
#include "pcraster_multicore/wrapper/argument_traits/multicore_nonspatial.h"

#include "pcraster_multicore/python/execution_policy.h"
#include "pcraster_multicore/python/local/utils.h"
#include "pcraster_multicore/python/type_conversion/boolean.h"

// Fern
#include "fern/algorithm/policy/policies.h"
#include "fern/algorithm/core/cover.h"

namespace fa = fern::algorithm;

namespace pcraster_multicore {
namespace python {
namespace detail {

template<class T>
void cover_nonspatial(
         fa::ExecutionPolicy& epol,
         multicore_field::Spatial<T>& res,
         multicore_field::Nonspatial<T> const& arg2) {

  using InputNoDataPolicy = fa::InputNoDataPolicies<SpatialDetectNoData<T>,
        NonspatialDetectNoData<T>>;
  InputNoDataPolicy input_no_data_policy{{res}, {arg2}};

  SpatialSetNoData<T> output_no_data_policy(res);

  fa::core::cover(input_no_data_policy,
    output_no_data_policy, epol, res, arg2, res);
}

template<class T>
void cover_spatial(
         fa::ExecutionPolicy& epol,
         multicore_field::Spatial<T>& res,
         multicore_field::Spatial<T> const& arg2){

  using InputNoDataPolicy = fa::InputNoDataPolicies<SpatialDetectNoData<T>,
        SpatialDetectNoData<T>>;

  InputNoDataPolicy input_no_data_policy{{res}, {arg2}};

  SpatialSetNoData<T> output_no_data_policy(res);

  fa::core::cover(input_no_data_policy,
    output_no_data_policy, epol, res, arg2, res);
}

template<class T>
calc::Field* cover(std::vector<calc::Field*> const&  field_arguments){

  calc::Field* res_field = (field_arguments.at(0))->createClone();
  multicore_field::Spatial<T> res(res_field);

  fa::ExecutionPolicy epol = execution_policy();

  for(size_t idx = 1; idx < field_arguments.size(); ++idx){
    // we assume that the first field is always spatial
    // i.e. spatial argument, or spatial result of previous cover
    if(field_arguments.at(idx)->isSpatial() == true){
      const multicore_field::Spatial<T> arg2(field_arguments.at(idx));
      cover_spatial<T>(epol, res, arg2);
    }
    else{
      const multicore_field::Nonspatial<T> arg2(field_arguments.at(idx));
      cover_nonspatial<T>(epol, res, arg2);
    }
  }
  return res.getField();
}

calc::Field* spatial_safe_bool(calc::Field* argument){
  double value = 0;
  argument->getCell(value, 0);

  INT4 nominal_val = static_cast<INT4>(value);

  if(!(0 <= nominal_val && nominal_val <= 1)){
    throw std::runtime_error("value '" + std::to_string(nominal_val) + "' is of type nominal or ordinal, must be boolean");

  }

  UINT1 cellvalue = nominal_val != static_cast<UINT1>(0) ? 1 : 0;

  calc::Field* res_field = new calc::Spatial(VS_B, calc::CRI_1, nr_cells());

  UINT1* cells = static_cast<UINT1*>(res_field->dest());
  std::memset(cells, cellvalue, nr_cells());

  return res_field;

}

calc::Field* spatial_int4(calc::Field* argument){
  double value = 0;
  argument->getCell(value, 0);

  INT4 cellvalue = static_cast<INT4>(value);

  calc::Field* res_field = new calc::Spatial(VS_O, calc::CRI_4, nr_cells());

  for(size_t idx = 0; idx < nr_cells(); ++idx){
    res_field->setCell(cellvalue, idx);
  }

  return res_field;

}

} // namespace detail

calc::Field* cover(boost::python::list const& arguments){

  size_t nr_args = boost::python::len(arguments);

  if(nr_args == 0){
    throw std::runtime_error("at least 1 argument required, 0 given\n");
  }

  if(nr_args == 1){
    return (static_cast<calc::Field*>(boost::python::extract<calc::Field*>(arguments[0])))->createClone();
  }

  std::vector<calc::Field *> field_arguments;

  for(size_t idx = 0; idx < nr_args; ++idx){
    field_arguments.push_back(boost::python::extract<calc::Field*>(arguments[idx]));
    // arguments must have same extent as clone
    assert_equal_location_attributes(*field_arguments.at(idx));
  }

  bool is_boolean = false;
  bool is_nominal = false;
  bool is_ordinal = false;
  bool is_scalar = false;

  // First spatial argument will determine the return datatype
  for(size_t idx = 0; idx < nr_args; ++idx){
    if(field_arguments.at(idx)->isSpatial() == true && boolean_valuescale(*field_arguments.at(idx))){
      is_boolean = true;
      break;
    }
    if(field_arguments.at(idx)->isSpatial() == true && nominal_valuescale(*field_arguments.at(idx))){
      is_nominal = true;
      break;
    }
    if(field_arguments.at(idx)->isSpatial() == true && ordinal_valuescale(*field_arguments.at(idx))){
      is_ordinal = true;
      break;
    }
    if(field_arguments.at(idx)->isSpatial() == true && scalar_valuescale(*field_arguments.at(idx))){
      is_scalar = true;
      break;
    }
  }

  if(is_scalar) {
    // cast nonspatial nominals (integers) to scalar
    for(size_t idx = 0; idx < nr_args; ++idx){
      if(field_arguments.at(idx)->isSpatial() == false){
        if(nominal_valuescale(*field_arguments.at(idx))){
          field_arguments.at(idx) = to_scalar(field_arguments.at(idx));
        }
      }
      assert_scalar_valuescale(*field_arguments.at(idx), "argument nr " + std::to_string(idx + 1));
    }
    return detail::cover<REAL4>(field_arguments);
  }
  else if(is_nominal){
    if(field_arguments.at(0)->isSpatial() == false){
      field_arguments.at(0) = detail::spatial_int4(field_arguments.at(0));
    }
    for(size_t idx = 0; idx < nr_args; ++idx){
      assert_nominal_valuescale(*field_arguments.at(idx), "argument nr " + std::to_string(idx + 1));
    }
    return detail::cover<INT4>(field_arguments);
  }
  else if(is_ordinal) {
    // If first argument is nonspatial nominal: cast to spatial ordinal
    if(field_arguments.at(0)->isSpatial() == false){
      if(nominal_valuescale(*field_arguments.at(0))){
          field_arguments.at(0) = detail::spatial_int4(field_arguments.at(0));
      }
    }
    for(size_t idx = 0; idx < nr_args; ++idx){
      // Nominal nonspatials (integers) can be casted to ordinals
      if(field_arguments.at(idx)->isSpatial() == false){
        if(nominal_valuescale(*field_arguments.at(idx))){
          field_arguments.at(idx) = to_ordinal(field_arguments.at(idx));
        }
      }
      assert_ordinal_valuescale(*field_arguments.at(idx), "argument nr " + std::to_string(idx + 1));
    }
    return detail::cover<INT4>(field_arguments);
  }
  else if(is_boolean) {
    // If first argument is nonspatial nominal (either 0 or 1): cast to boolean spatial
    if(field_arguments.at(0)->isSpatial() == false){
      if(nominal_valuescale(*field_arguments.at(0))){
          field_arguments.at(0) = detail::spatial_safe_bool(field_arguments.at(0));
      }
    }
    // Cast nonspatial nominal arguments to boolean where possible
    for(size_t idx = 0; idx < nr_args; ++idx){
      if(field_arguments.at(idx)->isSpatial() == false){
        // only 0 and 1 may be casted to boolean
        if(nominal_valuescale(*field_arguments.at(idx))){
          field_arguments.at(idx) = safe_boolean(field_arguments.at(idx));
        }
      }
      assert_boolean_valuescale(*field_arguments.at(idx), "argument nr " + std::to_string(idx + 1));
    }
    return detail::cover<UINT1>(field_arguments);
  }
  else if(directional_valuescale(*field_arguments.at(0))){
    // directional should be covered by wrapper
    // just bail out to be sure
    throw std::runtime_error("internal error: unable to perform operation");
  }

  assert(false);
  return nullptr;
}

} // namespace python
} // namespace pcraster_multicore


#ifndef CPPJIEBA_DICT_TRIE_HPP
#define CPPJIEBA_DICT_TRIE_HPP

#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <cstring>
#include <cstdlib>
#include <stdint.h>
#include <cmath>
#include <limits>
#include <boost/algorithm/string.hpp>

//#include "limonp/StringUtil.hpp"
//#include "limonp/Logging.hpp"
#include "Unicode.hpp"
#include "Trie.hpp"
extern "C" {
	void _log(const char *fmt, ...);
}

namespace cppjieba {

	//using namespace limonp;

	const double MIN_DOUBLE = -3.14e+100;// numeric_limits<double>::min();//
	const double MAX_DOUBLE = 3.14e+100; //numeric_limits<double>::max();//
	const size_t DICT_COLUMN_NUM = 3;
	const char* const UNKNOWN_TAG = "";

	class DictTrie {
	public:
		enum UserWordWeightOption {
			WordWeightMin,
			WordWeightMedian,
			WordWeightMax,
		}; // enum UserWordWeightOption

		DictTrie(bool fromShm,const string& dict_path, const string& user_dict_paths = "", UserWordWeightOption user_word_weight_opt = WordWeightMedian):static_node_infos_s(nullptr){
			Init(fromShm,dict_path, user_dict_paths, user_word_weight_opt);
		}

		~DictTrie() {
			delete trie_;
		}

		bool Find(RuneStrArray::const_iterator begin, RuneStrArray::const_iterator end, DictUnit* du=nullptr) const {
			return trie_->Find(begin, end, du);
		}

		void Find(RuneStrArray::const_iterator begin,
			RuneStrArray::const_iterator end,
			vector<struct Dag>&res,
			size_t max_word_len = MAX_WORD_LENGTH) const {
			trie_->Find(begin, end, res, max_word_len);
		}

		bool Find(const string& word)
		{
			const DictUnit *tmp = NULL;
			RuneStrArray runes;
			if (!DecodeRunesInString(word, runes))
			{
				//XLOG(ERROR) << "Decode failed.";
				return false;
			}
			return Find(runes.begin(), runes.end());
		}

		bool IsUserDictSingleChineseWord(const Rune& word) const {
			return user_dict_single_chinese_word_.find(word) != user_dict_single_chinese_word_.end();
		}

		double GetMinWeight() const {
			return min_weight_;
		}
		void InserUserDictNode(const string& line) {
			vector<string> buf;
			boost::algorithm::split(buf, line, boost::algorithm::is_any_of(" "));
			const int sz = buf.size();
			if (sz < 1 || sz>3)
				return;
			DictUnit& node_info = static_node_infos_.emplace_back();
			switch (sz)
			{
			case 1:
			{
				MakeNodeInfo(node_info,
					buf[0],
					user_word_default_weight_,
					UNKNOWN_TAG);
				break;
			}
			case 2:
			{
				MakeNodeInfo(node_info,
					buf[0],
					user_word_default_weight_,
					buf[1]);
				break;
			}
			case 3:
			{
				int freq = atoi(buf[1].c_str());
				assert(freq_sum_ > 0.0);
				double weight = log(1.0 * freq / freq_sum_);
				MakeNodeInfo(node_info, buf[0], weight, buf[2]);
				break;
			}
			default:
				break;
			}
			if (node_info.word.size() == 1) {
				user_dict_single_chinese_word_.insert(node_info.word[0]);
			}
		}

		void LoadUserDict(const vector<string>& buf) {
			for (auto& s: buf) {
				InserUserDictNode(s);
			}
		}

		void LoadUserDict(const set<string>& buf) {
			std::set<string>::const_iterator iter;
			for (iter = buf.begin(); iter != buf.end(); iter++) {
				InserUserDictNode(*iter);
			}
		}

		void LoadUserDict(const string& filePaths) {
			vector<string> files;
			boost::algorithm::split(files, filePaths, boost::algorithm::is_any_of("|;"));
			//vector<string> files = limonp::Split(filePaths, "|;");
			size_t lineno = 0;
			for (size_t i = 0; i < files.size(); i++) {
				ifstream ifs(files[i].c_str());
				if(!ifs.is_open())
					continue;
				string line;

				for (; getline(ifs, line); lineno++) {
					if (line.size() == 0) {
						continue;
					}
					InserUserDictNode(line);
				}
			}
		}
		DictUnitVect static_node_infos_;
		SharedDictUnitVect* static_node_infos_s;

	private:
		void Init(bool fromShm,const string& dict_path, const string& user_dict_paths, UserWordWeightOption user_word_weight_opt) {
			if (fromShm)
			{
				static_node_infos_s = _loadFromShm(min_weight_, max_weight_, median_weight_);
			}
			if (static_node_infos_s == nullptr)
			{
				LoadDict(dict_path);
				freq_sum_ = CalcFreqSum(static_node_infos_);
				CalculateWeight(static_node_infos_, freq_sum_);
				SetStaticWordWeights(user_word_weight_opt);
				if (user_dict_paths.size()) {
					LoadUserDict(user_dict_paths);
				}
				static_node_infos_s = _saveToShm(static_node_infos_,min_weight_, max_weight_, median_weight_);
				static_node_infos_.clear();
				static_node_infos_.shrink_to_fit();
			}
			CreateTrie(static_node_infos_s);
		}

		void CreateTrie(SharedDictUnitVect* dictUnits) {
			trie_ = new Trie(dictUnits);
			trie_->tnodes.shrink_to_fit();
			trie_->nxtMaps.shrink_to_fit();
		}

		inline bool MakeNodeInfo(DictUnit& node_info,
			const string& word,
			double weight,
			const string& tag) {
			if (!DecodeRunesInString(word, node_info.word)) {
				//XLOG(ERROR) << "Decode " << word << " failed.";
				return false;
			}
			node_info.weight = weight;
			node_info.word.shrink_to_fit();
			memset(node_info.tag, 0, sizeof(node_info.tag));
			strncpy(node_info.tag, tag.c_str(), 4);
			return true;
		}

		void LoadDict(const string& filePath) {
			static_node_infos_.clear();
			static_node_infos_.reserve(600*1000);
			ifstream ifs(filePath.c_str());
			if (!ifs.is_open())
				return;
			//XCHECK(ifs.is_open()) << "open " << filePath << " failed.";
			string line;
			boost::container::vector<string> buf;
			//DictUnit node_info;
			for (size_t lineno = 0; getline(ifs, line); lineno++) {
				boost::algorithm::split(buf, line, boost::algorithm::is_any_of(" "));
				//Split(line, buf, " ");
				assert(buf.size() == DICT_COLUMN_NUM);// << "split result illegal, line:" << line;
				MakeNodeInfo(static_node_infos_.emplace_back(DictUnit()),
					buf[0],
					atof(buf[1].c_str()),
					buf[2]);
			}
		}

		static bool WeightCompare(const DictUnit& lhs, const DictUnit& rhs) {
			return lhs.weight < rhs.weight;
		}

		void SetStaticWordWeights(UserWordWeightOption option) {
			assert(!static_node_infos_.empty());
			if (static_node_infos_.empty())
				return;
			double wt = 0.0;
			min_weight_ = MAX_DOUBLE;
			max_weight_ = MIN_DOUBLE;
			median_weight_ = 0.0;
			for (auto& nd : static_node_infos_)
			{
				if (nd.weight > max_weight_)
					max_weight_ = nd.weight;
				else if (nd.weight < min_weight_)
					min_weight_ = nd.weight;
				wt += nd.weight;
			}
			median_weight_ = wt / static_node_infos_.size();// x[x.size() / 2].weight;
			switch (option) {
			case WordWeightMin:
				user_word_default_weight_ = min_weight_;
				break;
			case WordWeightMedian:
				user_word_default_weight_ = median_weight_;
				break;
			default:
				user_word_default_weight_ = max_weight_;
				break;
			}
		}

		double CalcFreqSum(const DictUnitVect& node_infos) const {
			double sum = 0.0;
			for (const DictUnit& du: node_infos) {
				sum += du.weight;
			}
			return sum;
		}

		void CalculateWeight(DictUnitVect& node_infos, double sum) const {
			assert(sum > 0.0);
			//int sz = node_infos.size();
			for (DictUnit& du : node_infos) {
				assert(du.weight > 0.0);
				du.weight = log(double(du.weight) / sum);
			}
		}

		void Shrink(DictUnitVect& units) const {
			DictUnitVect(units.begin(), units.end()).swap(units);
		}

		Trie * trie_;

		double freq_sum_;
		double min_weight_;
		double max_weight_;
		double median_weight_;
		double user_word_default_weight_;
		flat_set<Rune> user_dict_single_chinese_word_;
	};
}

#endif


/*! \file AMR3D.hpp
\brief Abstract class for amr in 3D
\author Elad Steinberg
*/

#ifndef AMR3D_HPP
#define AMR3D_HPP 1

#include "computational_cell.hpp"
#include "conserved_3d.hpp"
#include "../common/equation_of_state.hpp"
#include "../../3D/GeometryCommon/Tessellation3D.hpp"
#include <boost/scoped_ptr.hpp>
#include "LinearGauss3D.hpp"
#include "hdsim_3d.hpp"

//! \brief Abstract class for cell update scheme in amr
class AMRCellUpdater3D
{
public:

	/*! \brief Calculates the computational cell
	\param extensive Extensive conserved variables
	\param eos Equation of state
	\param volume Cell volume
	\param old_cell Old computational cell
	\param tracerstickernames The names of the tracers and stickers
	\return Computational cell
	*/
	virtual ComputationalCell3D ConvertExtensiveToPrimitve3D(const Conserved3D& extensive, const EquationOfState& eos,
		double volume, ComputationalCell3D const& old_cell, TracerStickerNames const& tracerstickernames) const = 0;

	//! \brief Class destructor
	virtual ~AMRCellUpdater3D(void);
};

//! \brief Abstract class for extensive update scheme in amr
class AMRExtensiveUpdater3D
{
public:

	/*! \brief Calculates the computational cell
	\param cell Computational cell
	\param eos Equation of state
	\param volume Cell volume
	\param tracerstickernames The names of the tracers and stickers
	\return Extensive
	*/
	virtual Conserved3D ConvertPrimitveToExtensive3D(const ComputationalCell3D& cell, const EquationOfState& eos,
		double volume, TracerStickerNames const& tracerstickernames, Slope3D const& slope,Vector3D const& CMold,Vector3D const& CMnew) const = 0;

	//! \brief Class destructor
	virtual ~AMRExtensiveUpdater3D(void);
};

//! \brief Simple class for extensive update scheme in amr
class SimpleAMRExtensiveUpdater3D : public AMRExtensiveUpdater3D
{
public:
	SimpleAMRExtensiveUpdater3D(void);

	Conserved3D ConvertPrimitveToExtensive3D(const ComputationalCell3D& cell, const EquationOfState& eos,
		double volume, TracerStickerNames const& tracerstickernames, Slope3D const& slope, Vector3D const& CMold, Vector3D const& CMnew) const;
};

//! \brief Simple class for cell update scheme in amr
class SimpleAMRCellUpdater3D : public AMRCellUpdater3D
{
private:
	const vector<string>  toskip_;
public:
	/*!
	\brief class constructor
	\param toskip A list of sticker names to skip their cell update
	*/
	SimpleAMRCellUpdater3D(vector<string> toskip = vector<string>());

	ComputationalCell3D ConvertExtensiveToPrimitve3D(const Conserved3D& extensive, const EquationOfState& eos,
		double volume, ComputationalCell3D const& old_cell, TracerStickerNames const& tracerstickernames) const;
};

//! \brief Simple class for extensive update scheme in amr for SR
class SimpleAMRExtensiveUpdaterSR3D : public AMRExtensiveUpdater3D
{
public:
	Conserved3D ConvertPrimitveToExtensive3D(const ComputationalCell3D& cell, const EquationOfState& eos,
		double volume, TracerStickerNames const& tracerstickernames, Slope3D const& slope, Vector3D const& CMold, Vector3D const& CMnew) const;
};

//! \brief Simple class for cell update scheme in amr for SR
class SimpleAMRCellUpdaterSR3D : public AMRCellUpdater3D
{
private:
	const double G_;
	const vector<string>  toskip_;
public:
	/*!
	\brief class constructor
	\param G The adiabatic index
	\param toskip A list of sticker names to skip their cell update
	*/
	explicit SimpleAMRCellUpdaterSR3D(double G, vector<string> toskip);

	ComputationalCell3D ConvertExtensiveToPrimitve3D(const Conserved3D& extensive, const EquationOfState& eos,
		double volume, ComputationalCell3D const& old_cell, TracerStickerNames const& tracerstickernames) const;
};

//! \brief Chooses which cells should be remove
class CellsToRemove3D
{
public:
	/*!
	\brief Finds the cells to remove
	\param tess The tesselation
	\param cells The computational cells
	\param time The sim time
	\param tracerstickernames The names of the tracers and stickers
	\return The indeces of cells to remove with a corresponding merit which decides if there are neighboring cells which one to choose to remove
	*/
	virtual std::pair<vector<size_t>, vector<double> > ToRemove(Tessellation3D const& tess,
		vector<ComputationalCell3D> const& cells, double time,
		TracerStickerNames const& tracerstickernames)const = 0;

	//! \brief Virtual destructor
	virtual ~CellsToRemove3D(void);
};

//! \brief Chooses which cells should be refined
class CellsToRefine3D
{
public:
	/*!
	\brief Finds the cells to refine
	\param tess The tesselation
	\param cells The computational cells
	\param time The sim time
	\param tracerstickernames The names of the tracers and stickers
	\return The indeces of cells to remove and the direction to split (can be given empty)
	*/
	virtual std::pair<vector<size_t>,vector<Vector3D> > ToRefine(Tessellation3D const& tess,
		vector<ComputationalCell3D> const& cells, double time, TracerStickerNames const& tracerstickernames)const = 0;

	//! \brief Virtual destructor
	virtual ~CellsToRefine3D(void);
};

//! \brief Base class for amr
class AMR3D
{
private:
	EquationOfState const& eos_;
	CellsToRefine3D const& refine_;
	CellsToRemove3D  const& remove_;
	SimpleAMRCellUpdater3D scu_;
	SimpleAMRExtensiveUpdater3D seu_;
	SpatialReconstruction3D &interp_;
	AMRCellUpdater3D* cu_;
	AMRExtensiveUpdater3D* eu_;
	AMR3D(AMR3D const& amr);
	AMR3D& operator=(AMR3D const&);

public:
	/*!
	\brief Runs the AMR
	\param sim The sim object
	*/
	void operator() (HDSim3D &sim);

	/*! \brief Class constructor
	\param refine Refinement scheme
	\param remove Removal scheme
	\param cu Cell updater
	\param eu Extensive updater
	\param eos Equation of state
	*/
	AMR3D(EquationOfState const& eos, CellsToRefine3D const& refine, CellsToRemove3D const& remove,SpatialReconstruction3D &interp, AMRCellUpdater3D* cu = 0,
		AMRExtensiveUpdater3D* eu = 0);
	//! Class destructor
	~AMR3D();
};

#endif // AMR3D_HPP


/**
 * @file methods/ann/visitor/loss_visitor.hpp
 * @author Atharva Khandait
 *
 * This file provides an abstraction for the Loss() function for different
 * layers and automatically directs any parameter to the right layer type.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_HPP
#define MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_HPP

#include <mlpack/methods/ann/layer/layer_traits.hpp>

#include <boost/variant.hpp>

namespace mlpack {
namespace ann {

/**
 * LossVisitor exposes the Loss() method of the given module.
 */
class LossVisitor : public boost::static_visitor<double>
{
 public:
  //! Return the Loss.
  template<typename LayerType>
  double operator()(LayerType* layer) const;

  double operator()(MoreTypes layer) const;

 private:
  //! Return 0 if the module doesn't implement the Loss() or Model() function.
  template<typename T>
  typename std::enable_if<
      !HasLoss<T, double(T::*)()>::value &&
      !HasModelCheck<T>::value, double>::type
  LayerLoss(T* layer) const;

  //! Return the output height if the module implements the Loss() function.
  template<typename T>
  typename std::enable_if<
      HasLoss<T, double(T::*)()>::value &&
      !HasModelCheck<T>::value, double>::type
  LayerLoss(T* layer) const;

  //! Return the loss if the module implements the Model() function.
  template<typename T>
  typename std::enable_if<
      !HasLoss<T, double(T::*)()>::value &&
      HasModelCheck<T>::value, double>::type
  LayerLoss(T* layer) const;

  //! Return the loss if the module implements the Model() or loss() function.
  template<typename T>
  typename std::enable_if<
      HasLoss<T, double(T::*)()>::value &&
      HasModelCheck<T>::value, double>::type
  LayerLoss(T* layer) const;
};

} // namespace ann
} // namespace mlpack

// Include implementation.
#include "loss_visitor_impl.hpp"

#endif


/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2020 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Datatypes/Color.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/GeometryPrimitives/BBox.h>
#include <Graphics/Widgets/Widget.h>
#include <Graphics/Widgets/WidgetFactory.h>
#include <Modules/Fields/EditMeshBoundingBox.h>
#include <boost/format.hpp>

using namespace SCIRun;
using namespace Modules::Fields;
using namespace Core::Datatypes;
using namespace Dataflow::Networks;
using namespace Core::Algorithms;
using namespace Core::Geometry;
using namespace Graphics;
using namespace Datatypes;

MODULE_INFO_DEF(EditMeshBoundingBox, ChangeMesh, SCIRun)

namespace SCIRun
{
  namespace Modules
  {
    namespace Fields
    {
      class EditMeshBoundingBoxImpl
      {
      public:
        Transform userWidgetTransform_;
        Transform field_initial_transform_;
        BBox box_initial_bounds_;
        BBox bbox_;
        BoxPosition position_;
      };
    }
  }
}

EditMeshBoundingBox::EditMeshBoundingBox()
: GeometryGeneratingModule(staticInfo_),
impl_(new EditMeshBoundingBoxImpl), widgetMoved_(false)
{
  INITIALIZE_PORT(InputField);
  INITIALIZE_PORT(OutputField);
  INITIALIZE_PORT(Transformation_Widget);
  INITIALIZE_PORT(Transformation_Matrix);
}

void EditMeshBoundingBox::processWidgetFeedback(const ModuleFeedback& var)
{
  try
  {
    auto vsf = dynamic_cast<const ViewSceneFeedback&>(var);
    if (vsf.matchesWithModuleId(id()) && impl_->userWidgetTransform_ != vsf.transform)
    {
      widgetMoved_ = true;
      adjustGeometryFromTransform(vsf.transform);
      enqueueExecuteAgain(false);
    }
  }
  catch (std::bad_cast&)
  {
    //ignore
  }
}

void EditMeshBoundingBox::adjustGeometryFromTransform(const Transform& transformMatrix)
{
  Point currCenter, right, down, in;
  impl_->position_.getPosition(currCenter, right, down, in);

  DenseMatrix center(4, 1);
  center << currCenter.x(), currCenter.y(), currCenter.z(), 1.0;
  DenseMatrix newTransform(DenseMatrix(transformMatrix) * center);

  Point newLocation(newTransform(0, 0) / newTransform(3, 0),
                    newTransform(1, 0) / newTransform(3, 0),
                    newTransform(2, 0) / newTransform(3, 0));

  auto state = get_state();
  state->setValue(OutputCenterX, newLocation.x());
  state->setValue(OutputCenterY, newLocation.y());
  state->setValue(OutputCenterZ, newLocation.z());

  impl_->userWidgetTransform_ = transformMatrix;
}

void EditMeshBoundingBox::setStateDefaults()
{
  clear_vals();
  auto state = get_state();
  state->setValue(RestrictX, false);
  state->setValue(RestrictY, false);
  state->setValue(RestrictZ, false);
  state->setValue(RestrictR, false);
  state->setValue(RestrictD, false);
  state->setValue(RestrictI, false);
  state->setValue(OutputCenterX, 0.0);
  state->setValue(OutputCenterY, 0.0);
  state->setValue(OutputCenterZ, 0.0);
  state->setValue(OutputSizeX, 1.0);
  state->setValue(OutputSizeY, 1.0);
  state->setValue(OutputSizeZ, 1.0);
  state->setValue(Scale, 0.1);
  state->setValue(NoTranslation, true);
  state->setValue(XYZTranslation, false);
  state->setValue(RDITranslation, false);
  state->setValue(BoxRealScale, 0.0);
  state->setValue(BoxMode, 0);

  getOutputPort(Transformation_Widget)->connectConnectionFeedbackListener([this](const ModuleFeedback& var) { processWidgetFeedback(var); });
}

void EditMeshBoundingBox::execute()
{
  auto field = getRequiredInput(InputField);

  if (needToExecute())
  {
    clear_vals();
    update_input_attributes(field);
    executeImpl(field);
  }
}

void EditMeshBoundingBox::clear_vals()
{
  auto state = get_state();
  const std::string cleared("---");
  state->setValue(InputCenterX, cleared);
  state->setValue(InputCenterY, cleared);
  state->setValue(InputCenterZ, cleared);
  state->setValue(InputSizeX, cleared);
  state->setValue(InputSizeY, cleared);
  state->setValue(InputSizeZ, cleared);
}

namespace
{
  std::string convertForLabel(double coord)
  {
    return str(boost::format("%8.4f") % coord);
  }
}

void EditMeshBoundingBox::update_input_attributes(FieldHandle f)
{
  impl_->bbox_ = f->vmesh()->get_bounding_box();

  if (!impl_->bbox_.valid())
  {
    warning("Input field is empty -- using unit cube.");
    impl_->bbox_.extend(Point(0, 0, 0));
    impl_->bbox_.extend(Point(1, 1, 1));
  }
  auto size = impl_->bbox_.diagonal();
  auto center = impl_->bbox_.center();

  auto state = get_state();
  state->setValue(InputCenterX, convertForLabel(center.x()));
  state->setValue(InputCenterY, convertForLabel(center.y()));
  state->setValue(InputCenterZ, convertForLabel(center.z()));
  state->setValue(InputSizeX, convertForLabel(size.x()));
  state->setValue(InputSizeY, convertForLabel(size.y()));
  state->setValue(InputSizeZ, convertForLabel(size.z()));
}

GeometryHandle EditMeshBoundingBox::buildGeometryObject()
{
  return WidgetFactory::createBox({*this, ""}, {{get_state()->getValue(Scale).toDouble(), "", impl_->position_.center_, impl_->bbox_, 0},
                                  impl_->position_ });
}

void EditMeshBoundingBox::computeWidgetBox(const BBox& box) const
{
  auto bbox(box);
  if (!bbox.valid())
  {
    warning("Input field is empty -- using unit cube.");
    bbox.extend(Point(0, 0, 0));
    bbox.extend(Point(1, 1, 1));
  }

  // build a widget identical to the BBox
  auto size = Vector(bbox.get_max() - bbox.get_min());
  const auto SMALL = 1e-4;
  if (fabs(size.x())<SMALL)
  {
    size.x(2 * SMALL);
    bbox.extend(bbox.get_min() - Vector(SMALL, 0.0, 0.0));
    bbox.extend(bbox.get_max() + Vector(SMALL, 0.0, 0.0));
  }
  if (fabs(size.y())<SMALL)
  {
    size.y(2 * SMALL);
    bbox.extend(bbox.get_min() - Vector(0.0, SMALL, 0.0));
    bbox.extend(bbox.get_max() + Vector(0.0, SMALL, 0.0));
  }
  if (fabs(size.z())<SMALL)
  {
    size.z(2 * SMALL);
    bbox.extend(bbox.get_min() - Vector(0.0, 0.0, SMALL));
    bbox.extend(bbox.get_max() + Vector(0.0, 0.0, SMALL));
  }
  auto center = Point(bbox.get_min() + size / 2.);

  Vector sizex(size.x(), 0, 0);
  Vector sizey(0, size.y(), 0);
  Vector sizez(0, 0, size.z());

  auto right(center + sizex / 2.);
  auto down(center + sizey / 2.);
  auto in(center + sizez / 2.);

  impl_->position_.setPosition(center, right, down, in);
}

namespace
{
  void checkForVerySmall(double& size)
  {
    const auto VERY_SMALL = 1e-12;
    if (size < VERY_SMALL) size = 1.0;
  }
}

void EditMeshBoundingBox::executeImpl(FieldHandle inputField)
{
  auto state = get_state();

  if (!transient_value_cast<bool>(state->getTransientValue(ScaleChanged)))
  {
    computeWidgetBox(inputField->vmesh()->get_bounding_box());

    Point initialWidgetCenter, initialWidgetRight, initialWidgetDown, initialWidgetIn;
    impl_->position_.getPosition(initialWidgetCenter, initialWidgetRight, initialWidgetDown, initialWidgetIn);

    auto initialXSize = (initialWidgetRight - initialWidgetCenter).length();
    auto initialYSize = (initialWidgetDown - initialWidgetCenter).length();
    auto initialZSize = (initialWidgetIn - initialWidgetCenter).length();

    checkForVerySmall(initialXSize);
    checkForVerySmall(initialYSize);
    checkForVerySmall(initialZSize);

    Transform r_transformThatIsAppliedSomewhere;
    {
      impl_->field_initial_transform_.load_identity();
      impl_->field_initial_transform_.pre_scale(Vector(initialXSize, initialYSize, initialZSize));
      r_transformThatIsAppliedSomewhere.load_frame((initialWidgetRight - initialWidgetCenter).safe_normal(),
        (initialWidgetDown - initialWidgetCenter).safe_normal(),
        (initialWidgetIn - initialWidgetCenter).safe_normal());

      impl_->field_initial_transform_.pre_trans(r_transformThatIsAppliedSomewhere);
      impl_->field_initial_transform_.pre_translate(Vector(initialWidgetCenter));
    }

    Vector outputFieldSizeX, outputFieldSizeY, outputFieldSizeZ;
    if (transient_value_cast<bool>(state->getTransientValue(ResetSize)))
    {
      outputFieldSizeX = Vector(initialXSize * 2, 0, 0);
      outputFieldSizeY = Vector(0, initialYSize * 2, 0);
      outputFieldSizeZ = Vector(0, 0, initialZSize * 2);
    }
    else
    {
      state->setValue(OutputSizeX, std::fabs(state->getValue(OutputSizeX).toDouble()));
      state->setValue(OutputSizeY, std::fabs(state->getValue(OutputSizeY).toDouble()));
      state->setValue(OutputSizeZ, std::fabs(state->getValue(OutputSizeZ).toDouble()));

      outputFieldSizeX = Vector(state->getValue(OutputSizeX).toDouble(), 0, 0);
      outputFieldSizeY = Vector(0, state->getValue(OutputSizeY).toDouble(), 0);
      outputFieldSizeZ = Vector(0, 0, state->getValue(OutputSizeZ).toDouble());
    }

    auto newWidgetCenter = initialWidgetCenter;
    if (!transient_value_cast<bool>(state->getTransientValue(ResetCenter)))
    {
      newWidgetCenter = Point(state->getValue(OutputCenterX).toDouble(),
                              state->getValue(OutputCenterY).toDouble(),
                              state->getValue(OutputCenterZ).toDouble());
    }

    auto newWidgetRight(newWidgetCenter + outputFieldSizeX / 2.);
    auto newWidgetDown(newWidgetCenter + outputFieldSizeY / 2.);
    auto newWidgetIn(newWidgetCenter + outputFieldSizeZ / 2.);

    impl_->position_.setPosition(newWidgetCenter, newWidgetRight, newWidgetDown, newWidgetIn);

    r_transformThatIsAppliedSomewhere.load_frame((newWidgetRight - newWidgetCenter).safe_normal(),
      (newWidgetDown - newWidgetCenter).safe_normal(),
      (newWidgetIn - newWidgetCenter).safe_normal());

    // Change the input field handle here.
    FieldHandle output(inputField->deep_clone());

    Transform transformAppliedToOutputMesh;
    transformAppliedToOutputMesh.load_identity();
    Vector sizeHalf((newWidgetRight - newWidgetCenter).length(),
      (newWidgetDown - newWidgetCenter).length(),
      (newWidgetIn - newWidgetCenter).length());
    transformAppliedToOutputMesh.pre_scale(sizeHalf);

    transformAppliedToOutputMesh.pre_trans(r_transformThatIsAppliedSomewhere);
    transformAppliedToOutputMesh.pre_translate(Vector(newWidgetCenter));

    auto inv(impl_->field_initial_transform_);
    inv.invert();
    transformAppliedToOutputMesh.post_trans(inv);

    // Change the input field handle here.
    output->vmesh()->transform(transformAppliedToOutputMesh);

    state->setValue(OutputSizeX, sizeHalf.x() * 2);
    state->setValue(OutputSizeY, sizeHalf.y() * 2);
    state->setValue(OutputSizeZ, sizeHalf.z() * 2);
    state->setValue(OutputCenterX, newWidgetCenter.x());
    state->setValue(OutputCenterY, newWidgetCenter.y());
    state->setValue(OutputCenterZ, newWidgetCenter.z());

    // Convert the transform into a matrix and send it out.
    MatrixHandle mh(new DenseMatrix(transformAppliedToOutputMesh));
    sendOutput(Transformation_Matrix, mh);

    //state->setValue(Resetting, false);
    widgetMoved_ = false;

    sendOutput(OutputField, output);
  }
  state->setTransientValue(ScaleChanged, false);
  state->setTransientValue(SetOutputCenter, false);
  state->setTransientValue(ResetCenter, false);
  state->setTransientValue(SetOutputSize, false);
  state->setTransientValue(ResetSize, false);

  sendOutput(Transformation_Widget, buildGeometryObject());
}

const AlgorithmParameterName EditMeshBoundingBox::ResetCenter("ResetCenter");
const AlgorithmParameterName EditMeshBoundingBox::ResetSize("ResetSize");

const AlgorithmParameterName EditMeshBoundingBox::InputCenterX("InputCenterX");
const AlgorithmParameterName EditMeshBoundingBox::InputCenterY("InputCenterY");
const AlgorithmParameterName EditMeshBoundingBox::InputCenterZ("InputCenterZ");
const AlgorithmParameterName EditMeshBoundingBox::InputSizeX("InputSizeX");
const AlgorithmParameterName EditMeshBoundingBox::InputSizeY("InputSizeY");
const AlgorithmParameterName EditMeshBoundingBox::InputSizeZ("InputSizeZ");
 //Output Field Atributes
const AlgorithmParameterName EditMeshBoundingBox::SetOutputCenter("SetOutputCenter");
const AlgorithmParameterName EditMeshBoundingBox::SetOutputSize("SetOutputSize");
const AlgorithmParameterName EditMeshBoundingBox::OutputCenterX("OutputCenterX");
const AlgorithmParameterName EditMeshBoundingBox::OutputCenterY("OutputCenterY");
const AlgorithmParameterName EditMeshBoundingBox::OutputCenterZ("OutputCenterZ");
const AlgorithmParameterName EditMeshBoundingBox::OutputSizeX("OutputSizeX");
const AlgorithmParameterName EditMeshBoundingBox::OutputSizeY("OutputSizeY");
const AlgorithmParameterName EditMeshBoundingBox::OutputSizeZ("OutputSizeZ");
//Widget Scale/Mode
const AlgorithmParameterName EditMeshBoundingBox::Scale("Scale");
const AlgorithmParameterName EditMeshBoundingBox::ScaleChanged("ScaleChanged");
const AlgorithmParameterName EditMeshBoundingBox::NoTranslation("NoTranslation");
const AlgorithmParameterName EditMeshBoundingBox::XYZTranslation("XYZTranslation");
const AlgorithmParameterName EditMeshBoundingBox::RDITranslation("RDITranslation");
const AlgorithmParameterName EditMeshBoundingBox::RestrictX("RestrictX");
const AlgorithmParameterName EditMeshBoundingBox::RestrictY("RestrictY");
const AlgorithmParameterName EditMeshBoundingBox::RestrictZ("RestrictZ");
const AlgorithmParameterName EditMeshBoundingBox::RestrictR("RestrictR");
const AlgorithmParameterName EditMeshBoundingBox::RestrictD("RestrictD");
const AlgorithmParameterName EditMeshBoundingBox::RestrictI("RestrictI");

const AlgorithmParameterName EditMeshBoundingBox::BoxMode("BoxMode");
const AlgorithmParameterName EditMeshBoundingBox::BoxRealScale("BoxRealScale");


#include <boost/program_options.hpp>
#include <chrono>
#include <cstdlib>
#include <thread>

#include "EncryptedCoordinator.hpp"
#include "LatticeEncryption.hpp"
#include "Ligero.hpp"

#include "GatheringCoordinator.hpp"
#include "SecretSharingNTT.hpp"
#include "Transport.hpp"
#include "ZkArgument.hpp"

namespace po = boost::program_options;

using namespace ligero;
using namespace ligero::lattice;
using namespace std::chrono_literals;

/** Parameters RSA Ceremony */
/** ======================= */

constexpr auto degree = 1 << 16;
constexpr auto p = 9;
constexpr auto q = 21;
constexpr auto sigma = 8;
constexpr auto lambda = 128;
constexpr auto tau_limit_bit = 175;
constexpr auto tau = 1000;
constexpr auto numCandidates = 2048;

/** throughput parties */
constexpr auto wait_timeout = 300 * 1000; /* wait 5 min when start */
constexpr auto timeout = 25 * 1000;       /* a little longer than test */
constexpr auto data_size = 32 * 1024;     /* 32 KB */
constexpr auto nb_max_send = 1024;
constexpr auto parties = 1;

/** throughtput coordinator */
constexpr auto pbs = 1000;
constexpr auto duration = 20s;
constexpr auto wait_time = 120s;
constexpr auto cleanup_time = 5s;
constexpr auto throughput_cutoff = 100 * 1024; /* 200 KB/s */

/** Number of restart */
int maxRestart = 10;

/** Parameters Zero-Knowledge */
/** ========================= */

Statements localStatement = Statements::RSACeremony;

/** Settings Zero-Knowledge */
RegularInteger p_zk(10514644122014433281ULL);
typedef Fpp_Fixed<p_zk> FieldT;

zksnark::InterleavedRSCode irs;        /* Reed-Solomon Interleaved Code */
ligero::MTParameters<FieldT> mtParams; /* Merkle Tree Parameters */
constexpr size_t l = 4096;             /* Bloc size */
constexpr size_t k = 8192;             /* Degree */
constexpr size_t n = 16384;            /* # of Shares */

constexpr size_t t = 256;  // Columns to Open Up (By default half the columns)
constexpr auto ptNumberEvaluation = 3;

/** Random Generator */
const hash::HashIntegerSeedGeneratorF<FieldT>& randomGenerator =
    hash::blake2bIntegerSeedGenerator<FieldT>;
const hash::HashStringSeedGeneratorF& stringGenerator = hash::blake2bStringHash;

/** Threading */
constexpr size_t maxNumberThreads = 90;
/** [target machine m5.metal has 96 threads over 48 cores] */

/** This function handles the ceremony itself, whether it is performed with
 * passive or active security.
 * @param coordinator the encrypted coordinator handling the ceremony with
 * passive security
 * @param transport transport object to communicate with parties and verifiers
 * @param ipAddr ip address of the socket used to communicate with parties and
 * verifiers
 * @param passive boolean flag indicating whether to generate a ZK proof or not
 * @param speedtest boolean flag indicating whether to run a throughput test
 * ahead of the ceremony
 * @return Unit if successful
 */
expected<Unit> coordinateHelper(
    EncryptedCoordinator<uint64_t, degree, p, q>& coordinator,
    ZeroMQCoordinatorTransport& transport, std::string ipAddr, bool passive,
    bool speedtest) {
  // base case
  if (maxRestart < 0) {
    return Error::TOO_MANY_RESTART;
  }

  auto result = coordinator.host(transport, &maxRestart, speedtest);
  if (hasError(result)) {
    LOG(INFO) << showError(getError(result));
    exit(EXIT_FAILURE);
  }

  if (!passive) {
    printAtXY(1, 3,
              std::string("Active Security: Proof Verification Started."));
    auto response = GatheringCoordinator::gatherAndVerifyArgumentsOfKnowledge<
        FieldT, uint64_t, degree, p, q>(
        coordinator, transport, std::string("Prod"),
        "ligero-simulations/Proofs", 21, false, ipAddr.c_str(), parties);

    transport.myTimers.end(2, "1.a. Overall speed", transport.communicationCost,
                           true);

    /** Verify failed and need to restart the protocol */
    if (hasError(response)) {
      if (getError(response) == Error::RESTART) {
        transport.myTimers.reset();
        coordinator.clearPublicData();
        printAtXY(1, 1, std::string("Restarting RSA Ceremony."));
        return coordinateHelper(coordinator, transport, ipAddr, passive,
                                speedtest);
      } else {
        return getError(response);
      }
    }
    LOG(INFO) << "Verified all proofs; ceremony successful.";
    printAtXY(1, 7 + firstline,
              std::string("Verified all proofs; ceremony successful."));

    return Unit{};
  }
  return Unit{};
}

/** A helper function for the coordinator side of things.
 * @param parties number of parties expected to join the ceremony
 * @param ipAddr ip address of the socket used to communicate with parties and
 * verifiers
 * @param numCandidates number of candidates generated for each attempt
 * @param passive boolean flag indicating whether to generate a ZK proof or not
 * @param speedtest boolean flag indicating whether to run a throughput test
 * ahead of the ceremony
 * @param protocolMode allows various modes of operation including live, replay
 * @return Unit if successful
 */
void coordinate(int parties, std::string ipAddr, int numCandidates,
                bool passive, bool speedtest, ProtocolMode protocolMode) {
  try {

    AutoSeededRandomPool prng;
    ECDSA<ECP, SHA256>::PublicKey publicKey;
    ECDSA<ECP, SHA256>::PrivateKey privateKey;
    privateKey.Initialize( prng, ASN1::secp256r1() );
    privateKey.MakePublicKey( publicKey );

    ProtocolConfig<uint64_t> config(parties, p, q, degree, sigma, lambda,
                                    tau_limit_bit, pbs, protocolMode, publicKey);

    system("clear");

    /** Generate all proofs */
    /** =================== */
    auto coordinator = EncryptedCoordinator<uint64_t, degree, p, q>(config);
    ZeroMQCoordinatorTransport transport(
        std::string("tcp://") + ipAddr + std::string(":5555"),
        config.numParties(), privateKey);
    printAtXY(1, 1, std::string("Hosting RSA Ceremony."));

    if (speedtest) {
      auto throughput = coordinator.hostThroughputTest(
          transport, {duration, wait_time, cleanup_time, data_size},
          throughput_cutoff);
      if (hasError(throughput)) {
        if (getError(throughput) == Error::TOO_FEW_PARTIES) {
          LOG(INFO) << "Too few parties survive throughput test. Aborting";
        } else {
          LOG(INFO) << showError(getError(throughput));
        }
        exit(EXIT_FAILURE);
      }
    } else {
      transport.parties() = parties;
    }

    auto result =
        coordinateHelper(coordinator, transport, ipAddr, passive, speedtest);
    if (hasError(result)) {
      LOG(INFO) << "Caught an error trying to coordinate... "
                << showError(getError(result));
      exit(EXIT_FAILURE);
    }
  } catch (zmq::error_t& e) {
    LOG(ERROR) << "Caught an error trying to coordinate... " << e.what();
  }
}

/** ============================================================== */
int main(int argc, char** argv) {
  std::string ipAddr("127.0.0.1");
  std::string logfile("default");

  int numBits = 100;
  int numParties = 2;
  int numCandidates = 2048;
  double chiStd = 3.2;

  bool passive = false;
  bool speedtest = false;

  /** Set Zero-Knowledge Parameters */
  irs.k = k; /** Degree of the Polynomial */
  irs.n = n; /** Number of Shares */

  mtParams.digestLength = 32;
  mtParams.zkByteSize = 8;
  mtParams.hashInnerNodeF = hash::blake2bTwoToOneHash;
  mtParams.hashLeafContentF = hash::blake2bFieldElementHash<FieldT>;
  mtParams.hashZKRandomnessF = hash::blake2bZKElementHash;
  mtParams.leavesNumber = irs.n;

  try {
    po::options_description desc("Allowed options");
    desc.add_options()("help", "produce help message")(
        "version", "show version")("parties", po::value<int>(),
                                   "set the number of participating clients")(
        "logfile", po::value<std::string>(), "sets the logfile name")(
        "ip", po::value<std::string>(),
        "set the ip address for the coordinator")("bitsize", po::value<int>(),
                                                  "sets the bit size")(
        "chiStd", po::value<double>(), "sets the std distribution randomness")(
        "numcandidates", po::value<int>(),
        "sets the number of shares attempted per participant")(
        "mode", po::value<std::string>(),
        "sets the protocol mode from normal, replay, record, normal by "
        "default")("passive", "do not include a zero-knowledge argument")(
        "speedtest", "perform throughput test before kick off the ceremony");

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    if (vm.count("help")) {
      LOG(TRACE) << desc << "\n";
      return 0;
    }

    if (vm.count("version")) {
      std::cout << "coordinator version " << std::hex << PROTOCOL_VERSION_NUMBER
                << std::endl;
      return 0;
    }

    if (vm.count("passive")) {
      passive = true;
    }

    if (vm.count("speedtest")) {
      speedtest = true;
      LOG(TRACE) << "Running with throughput test, make sure running parties "
                    "with `--speedtest` also";
    }

    if (vm.count("parties")) {
      numParties = vm["parties"].as<int>();
      LOG(TRACE) << "nb parties = " << numParties;
    }

    if (vm.count("logfile")) {
      logfile = vm["logfile"].as<std::string>();
      LOG(TRACE) << "logfile = " << logfile;
    }

    ProtocolMode protocolMode = ProtocolMode::NORMAL;
    if (vm.count("mode")) {
      std::string mode = vm["mode"].as<std::string>();
      if (mode == "replay") {
        protocolMode = ProtocolMode::REPLAY;
      } else if (mode == "record") {
        protocolMode = ProtocolMode::RECORD;
      } else {
        protocolMode = ProtocolMode::NORMAL;
      }
      LOG(TRACE) << "mode = " << mode;
    }

    if (vm.count("ip")) {
      ipAddr = vm["ip"].as<std::string>();
      LOG(TRACE) << "Coordinator resides at " << ipAddr;
    }

    if (vm.count("numcandidates")) {
      numCandidates = (int)vm["numcandidates"].as<int>();
      LOG(TRACE) << "numCandidates = " << numCandidates;
    }

    if (vm.count("chiStd")) {
      chiStd = (double)vm["chiStd"].as<double>();
      LOG(TRACE) << "chiStd = " << chiStd;
      std::cout << chiStd;
    }

    const int packingFactor = numCandidates;

    /** Configure easylogging */
    configureEasyLogging(logfile.c_str());

    LOG(TRACE) << std::string(120, '=');
    LOG(TRACE) << "Coordinator started.";

    coordinate(numParties, ipAddr, numCandidates, passive, speedtest,
               protocolMode);
  } catch (std::exception& e) {
    LOG(FATAL) << "Error: " << e.what();
  } catch (...) {
    LOG(FATAL) << "Caught an exception of unknown type.";
  }

  return 0;
}


#include <jni.h>
#include <string>
#include <iostream>
#include <random>
#include <math.h>
#include <android/log.h>
//#include <qspower/power.hh>
#include <sys/time.h>
#include <sched.h>
#include <unistd.h>
#include <sys/syscall.h> // syscall
#include <future>
#include  <omp.h>
#include "Eigen/Core"
#include <Eigen/Dense>
#include <vector>
#include <thread>
#include <pthread.h>
#include <assert.h>
//#include "cpu.h"
#include "cpu_tune.h"

#define gettid() syscall(__NR_gettid)
//#define gettidv2() syscall(SYS_gettid)

using namespace Eigen;
using namespace std;

#define  LOG_TAG    "nativeprint"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

extern "C" JNIEXPORT jstring

JNICALL
Java_com_example_frank_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

/*
bool static_test(){
    LOGI("frank static test");

    //powerSDK相关
    struct timeval t1, t2, t3, t4;
    double time = 0.0;
    gettimeofday(&t1, NULL);

    const size_t N = 10000000;
    std::vector<float> a(N), b(N), c(N);
    std::random_device random_device;
    std::mt19937 generator(random_device());
    const float min_value = -1024.0, max_value = 1024.0;
    std::uniform_real_distribution<float> dist(min_value, max_value);

    const float alpha = 0.2f; // multiplicative factor

    qspower::init();

    bool isSupported = false;
    isSupported = qspower::is_supported();

    LOGD("frank isSupported : %d\n", isSupported);

    // request window mode of 40% to 50% frequency on cpu big cores for an indefinite period of time
    qspower::request_mode(qspower::mode::perf_burst, qspower::device_set{ qspower::device_type::cpu_big});

    // Initialize the source arrays with random numbers
//    for (size_t i = 0; i < N; i++) {
//        a[i] = dist(generator);
//        b[i] = dist(generator);
//    }
    volatile double d = 0.0;
    // computation, vector addition
    for (size_t i = 0; i < N; i++) {
//        c[i] = alpha * a[i] + b[i];
        d += cos(sin(tan(cosh(1.234))));
        d += sin(sin(tan(cosh(1.234))));
        d += tan(sin(tan(cosh(1.234))));
    }

    // return the system to the normal power state
    qspower::request_mode(qspower::mode::normal);

    qspower::terminate();

    gettimeofday(&t2, NULL);
    time = 1000*(t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000;
    LOGD("frank time = %lf,  d= %lf\n", time, d);

    if (isSupported){
        return true;
    } else{
        return false;
    }
}

bool dynamic_test(){
    const size_t N            = 1000000;                       // Number of elements in the vector
    const size_t iterations   = 10;                             // Number of times, entire vector operation will be performed
    const float  min_value    = -1024.0, max_value = 1024.0;    // Min and Max value for random number generator
    const float  alpha        = 0.2f;                           // multiplicative factor
    const int    desired_goal = 7800;                           // desired number of elements to be processed per millisecond

    std::vector<float>               a(N), b(N), c(N);
    std::random_device               random_device;
    std::mt19937                     generator(random_device());
    std::uniform_real_distribution<> dist(min_value, max_value);
    auto                             gen_dist = std::bind(dist, std::ref(generator));

    std::chrono::time_point<std::chrono::system_clock> begin;
    using float_millisecs = std::chrono::duration<float, std::chrono::milliseconds::period>;

    qspower::init();

    bool isSupported = qspower::is_supported();

    // Set the desired goal of how many elements are to be processed
    // with tolerance of 100 elements.
    int tolerance =

    qspower::set_goal(desired_goal, 100, qspower::cpu_big);

    for (size_t j = 0; j < iterations; j++)
    {
        begin = std::chrono::system_clock::now();

        // Initialize the source arrays with random numbers
        std::generate(a.begin(), a.end(), gen_dist);
        std::generate(b.begin(), b.end(), gen_dist);

        // add both arrays
        for (size_t i = 0; i < N; i++)
        {
            c[i] = sin(alpha * a[i] + b[i]);
        }

        // measure the time
        auto measured = float_millisecs(std::chrono::system_clock::now() - begin);

        // feed the SDK runtime with how many elements are processed per millisecond
        // so it can regulate the cores and frequencies
        qspower::regulate(N / measured.count());
    }

    // return the system to the normal power state
    qspower::clear_goal();

//    qspower::terminate();

    if (isSupported){
        return true;
    } else{
        return false;
    }
}
*/

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_frank_myapplication_MainActivity_test(JNIEnv *env, jobject instance) {

//#pragma omp parallel for num_threads(6)
//    for (int i = 0; i < 12; i++)
//    {
//        LOGI("frank num = %d", omp_get_thread_num());
//    }

    int row = 3000;
    int col = 3000;

    Eigen::MatrixXf a = Eigen::MatrixXf::Random(row, col);
    Eigen::MatrixXf b = Eigen::MatrixXf::Random(col, row);
    MatrixXf c = Eigen::MatrixXf::Random(row, col);
    MatrixXf d = Eigen::MatrixXf::Random(row, col);

    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            a(i,j) = i+j*2;
        }
    }
    for (int i = 0; i < col; i++) {
        for (int j = 0; j < row; j++) {
            b(i,j) = i*2+j;
        }
    }

    cpuCores cores = BIG;
    cpuMode mode = SAVER;
    set_cpuStatus(cores, mode);
    struct timeval t1, t2, t3, t4;
    double time = 0.0;
    gettimeofday(&t1, NULL);

//    c = a * b;
//    d = a * b;

    gettimeofday(&t2, NULL);
    time = 1000*(t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000;
    LOGI("frank time1 =  %lf\n", time);

    terminateQspower();
    std::string hello = "tune test\n";
    return env->NewStringUTF(hello.c_str());
}


#pragma once

#include "glm/glm.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>

#include <algorithm>
#include <numeric>

//#include "ska_sort.hpp"
//#include <boost/sort/sort.hpp>
//#include <boost/sort/spreadsort/integer_sort.hpp>

#include <cctype>
#include <chrono> // current time
#include <climits>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <omp.h>
#include <pystring.h>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

struct vertex3D {
    glm::vec3 position;
    glm::vec3 normal;
    glm::vec2 texCoord;

    // for unordered_map
    bool operator==(const vertex3D& other) const {
        return position == other.position && normal == other.normal && texCoord == other.texCoord;
    }
};

inline uint64_t spread_bits_uint64(uint64_t x) {
    x = (x | (x << 32)) & 0x7fff00000000ffff;
    x = (x | (x << 16)) & 0x00ff0000ff0000ff;
    x = (x | (x << 8)) & 0x700f00f00f00f00f;
    x = (x | (x << 4)) & 0x30c30c30c30c30c3;
    x = (x | (x << 2)) & 0x1249249249249249;
    return x;
}

inline uint64_t mortonIndex64(uint32_t x, uint32_t y, uint32_t z) {
    return (spread_bits_uint64(x) | (spread_bits_uint64(y) << 1) | (spread_bits_uint64(z) << 2));
}

// for unordered_map
namespace std {
template <> struct hash<vertex3D> {
    size_t operator()(vertex3D const& vertex) const {
        return ((hash<glm::vec3>()(vertex.position) ^ (hash<glm::vec3>()(vertex.normal) << 1)) >> 1) ^
               (hash<glm::vec2>()(vertex.texCoord) << 1);
    }
};
} // namespace std

namespace std {

inline bool operator<(const glm::ivec3& a, const glm::ivec3& b) {
    return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z)));
    // return mortonIndex64(a.x, a.y, a.z) < mortonIndex64(b.x, b.y, b.z);
}

template <> struct less<glm::ivec3> {
    bool operator()(const glm::ivec3& a, const glm::ivec3& b) {
        return a < b;
    }
};
} // namespace std

namespace objLoader {

using namespace std;

constexpr uint32_t packCharsToIntKey(char a, char b) {
    return (static_cast<uint32_t>(a) << 8) | static_cast<uint32_t>(b);
}

constexpr uint32_t v = packCharsToIntKey('v', ' ');
constexpr uint32_t vn = packCharsToIntKey('v', 'n');
constexpr uint32_t vt = packCharsToIntKey('v', 't');
constexpr uint32_t vp = packCharsToIntKey('v', 'p');
constexpr uint32_t f = packCharsToIntKey('f', ' ');
constexpr uint32_t comment = packCharsToIntKey('#', ' ');

// add groups
constexpr uint32_t g = packCharsToIntKey('g', ' ');

// add groups
struct groupInfo {
    std::string name;
    int startOffset;
    int count;
};

struct RawMeshData {
    std::vector<glm::vec3> positions;
    std::vector<glm::vec3> normals;
    std::vector<glm::vec2> textureCoords;
    // store the obj face info interleaved for now. makes finding unique verts easier
    std::vector<glm::ivec3> faceIndices;

    // add groups.
    std::vector<groupInfo> groupInfos;
};

struct MeshDataSplit {
    std::vector<vertex3D> vertices;
    // add groups.
    // an int which is the start index into the faceIndices of where groups start
    // to get the range, use the last(-1) offset
    std::vector<groupInfo> groupInfos;
};

struct MeshDataElements : MeshDataSplit {
    std::vector<int> indices;
};

RawMeshData readObjRaw(const std::string& filePath) {

    std::ios_base::sync_with_stdio(false);
    using namespace std::chrono;
    auto startTime = system_clock::now();

    fmt::print(stderr, "starting obj loader\n");
    RawMeshData meshData;

    std::ifstream inputFileStream(filePath);
    std::string line;
    std::string face;
    std::stringstream ss;

    std::vector<std::string> tokens;
    std::vector<std::string> faceTokensA;
    std::vector<std::string> faceTokensB;
    std::vector<std::string> faceTokensC;

    // if quads
    std::vector<std::string> faceTokensD;

    //for storing where spaces and slashes go
    std::vector<int> spacePositions(8);
    std::vector<int> slashPositionsA(6);
    std::vector<int> slashPositionsB(6);
    std::vector<int> slashPositionsC(6);

    bool hasTextureCoords = false;
    bool hasNormals = false;
    bool startGroupTracking = false;
    int groupCount = 0;

    while (std::getline(inputFileStream, line)) {
        spacePositions.clear();
        //spaces after the first will always be after 3
        for (auto i = 3u; i < line.size(); ++i){
            if(line[i] == " "){
                spacePositions.push_back(i);
            }
        }
        //pystring::split(line, tokens, " ");
        uint32_t key = packCharsToIntKey(line[0], line[1]);

        switch (key) {

        case v: {
            meshData.positions.emplace_back(std::stof(tokens[1]), std::stof(tokens[2]), std::stof(tokens[3]));

            break;
        }
        case vn: {
            meshData.normals.emplace_back(std::stof(tokens[1]), std::stof(tokens[2]), std::stof(tokens[3]));
            if (!startGroupTracking) {
                startGroupTracking = true;
            }
            break;
        }
        case vt: {
            meshData.textureCoords.emplace_back(std::stof(tokens[1]), std::stof(tokens[2]));
            break;
        }
        case vp: {
            break;
        }
        // add groups
        case g: {
            if (startGroupTracking) {
                if (tokens.size() > 1 && !tokens[1].empty()) { // its a face group with a name
                    meshData.groupInfos.push_back({tokens[1], static_cast<int>(meshData.faceIndices.size())});
                } else {
                    meshData.groupInfos.push_back(
                        {fmt::format("group{}", ++groupCount), static_cast<int>(meshData.faceIndices.size())});
                }
            }
            break;
        }
        case f: {
            // is face

            pystring::split(tokens[1], faceTokensA, "/");
            pystring::split(tokens[2], faceTokensB, "/");
            pystring::split(tokens[3], faceTokensC, "/");

            // quad
            if (tokens.size() == 5) {
                pystring::split(tokens[4], faceTokensD, "/");
            }
            auto stoiIfNotEmpty = [](const std::string& str) -> int {
                if (!str.empty()) {
                    return std::stoi(str);
                }
                return 1;
            };

            meshData.faceIndices.emplace_back(std::stoi(faceTokensA[0]) - 1, stoiIfNotEmpty(faceTokensA[1]) - 1,
                                              stoiIfNotEmpty(faceTokensA[2]) - 1);

            meshData.faceIndices.emplace_back(std::stoi(faceTokensB[0]) - 1, stoiIfNotEmpty(faceTokensB[1]) - 1,
                                              stoiIfNotEmpty(faceTokensB[2]) - 1);

            meshData.faceIndices.emplace_back(std::stoi(faceTokensC[0]) - 1, stoiIfNotEmpty(faceTokensC[1]) - 1,
                                              stoiIfNotEmpty(faceTokensC[2]) - 1);

            // quad
            if (tokens.size() == 5) {
                meshData.faceIndices.emplace_back(std::stoi(faceTokensA[0]) - 1, stoiIfNotEmpty(faceTokensA[1]) - 1,
                                                  stoiIfNotEmpty(faceTokensA[2]) - 1);

                meshData.faceIndices.emplace_back(std::stoi(faceTokensC[0]) - 1, stoiIfNotEmpty(faceTokensC[1]) - 1,
                                                  stoiIfNotEmpty(faceTokensC[2]) - 1);

                meshData.faceIndices.emplace_back(std::stoi(faceTokensD[0]) - 1, stoiIfNotEmpty(faceTokensD[1]) - 1,
                                                  stoiIfNotEmpty(faceTokensD[2]) - 1);
            }

            break;
        }
        case comment: {
            break;
        }
        default: {
        }
        }
    }

    // fix up groups
    for (auto it = meshData.groupInfos.begin(); it != meshData.groupInfos.end() - 1; ++it) {
        (*it).count = (*std::next(it)).startOffset - (*it).startOffset;
    }
    meshData.groupInfos.back().count =
        static_cast<int>(meshData.faceIndices.size() - meshData.groupInfos.back().startOffset);

    fmt::print(stderr, "finished mesh read\n");

    auto timeTaken = duration<float>(system_clock::now() - startTime).count();
    fmt::print(stderr, "time taken {}\n", timeTaken);

    return meshData;
}

// for feeding into drawArrays as seperate triangles. hard to misuses as the type indicates the usage
MeshDataSplit readObjSplit(const std::string& filePath) {
    auto rawMeshData = readObjRaw(filePath);

    MeshDataSplit meshData;
    meshData.groupInfos = std::move(rawMeshData.groupInfos);

    meshData.vertices.resize(rawMeshData.faceIndices.size());
    if (rawMeshData.textureCoords.size() == 0) {
        rawMeshData.textureCoords.resize(rawMeshData.faceIndices.size());
    }

    if (rawMeshData.normals.size() == 0) {
        rawMeshData.normals.resize(rawMeshData.faceIndices.size());
    }

#pragma omp parallel for
    for (int i = 0u; i < rawMeshData.faceIndices.size(); ++i) {
        meshData.vertices[i] = {rawMeshData.positions[rawMeshData.faceIndices[i].x],
                                rawMeshData.normals[rawMeshData.faceIndices[i].z],
                                rawMeshData.textureCoords[rawMeshData.faceIndices[i].y]};
    }
    return meshData;
}

// for feeding into drawArrayElements
MeshDataElements readObjElements(const std::string& filePath) {
    using namespace std::chrono;

    auto rawMeshData = readObjRaw(filePath);
    auto startTime = system_clock::now();

    MeshDataElements meshData;

    // add groups
    meshData.groupInfos = std::move(rawMeshData.groupInfos);
    meshData.indices.resize(rawMeshData.faceIndices.size());
    std::vector<int> trackingIds(rawMeshData.faceIndices.size());

    // for building offsets of unique ranges
    std::vector<int> trackingUniqueIds(rawMeshData.faceIndices.size());
    std::iota(trackingIds.begin(), trackingIds.end(), 0);
    std::iota(trackingUniqueIds.begin(), trackingUniqueIds.end(), 0);

    auto rightshift_func = [&rawMeshData](const int& x) {
        return mortonIndex64(rawMeshData.faceIndices[x].x, rawMeshData.faceIndices[x].y, rawMeshData.faceIndices[x].z);
    };

    //    auto rightshift_func = [&rawMeshData](const uint64_t& x, const uint64_t offset) {
    //        return x >>
    //               offset;
    //    };

    auto compare_func = [&rawMeshData](const int a, const int b) {
        return rawMeshData.faceIndices[a] < rawMeshData.faceIndices[b];
    };
    //    boost::sort::spreadsort::integer_sort(trackingIds.begin(), trackingIds.end(), rightshift_func, compare_func);

    // ska_sort(trackingIds.begin(), trackingIds.end(), rightshift_func);
    // find unique verts. we have already copied face index so order isn't so important any more
    // std::sort(rawMeshData.faceIndices.begin(), rawMeshData.faceIndices.end());

    std::sort(trackingIds.begin(), trackingIds.end(),
              [&rawMeshData](int a, int b) { return rawMeshData.faceIndices[a] < rawMeshData.faceIndices[b]; });
    //boost::sort::parallel_stable_sort(trackingIds.begin(), trackingIds.end(), compare_func);
    auto uniqueEndIt =
        std::unique(trackingUniqueIds.begin(), trackingUniqueIds.end(), [&trackingIds, &rawMeshData](int a, int b) {
            return rawMeshData.faceIndices[trackingIds[a]] == rawMeshData.faceIndices[trackingIds[b]];
        });

    // trackingUniqueIds.erase(uniqueEndIt, trackingUniqueIds.end());

    // how many unique vertices
    auto count = std::distance(trackingUniqueIds.begin(), uniqueEndIt);

    meshData.vertices.resize(count);
    fmt::print(stderr, "pos size {}\n", rawMeshData.positions.size());
    fmt::print(stderr, "norm size {}\n", rawMeshData.normals.size());
    fmt::print(stderr, "tc size {}\n", rawMeshData.textureCoords.size());

    fmt::print(stderr, "unique point count is {}\n", count);

    if (rawMeshData.textureCoords.size() == 0) {
        rawMeshData.textureCoords.resize(rawMeshData.faceIndices.size());
    }

    if (rawMeshData.normals.size() == 0) {
        rawMeshData.normals.resize(rawMeshData.faceIndices.size());
    }

    std::atomic<int> k = 0;
#pragma omp parallel for
    for (auto i = 1ll; i <= count; ++i) {

        meshData.vertices[i - 1] = {
            rawMeshData.positions[rawMeshData.faceIndices[trackingIds[trackingUniqueIds[i - 1]]].x],
            rawMeshData.normals[rawMeshData.faceIndices[trackingIds[trackingUniqueIds[i - 1]]].z],
            rawMeshData.textureCoords[rawMeshData.faceIndices[trackingIds[trackingUniqueIds[i - 1]]].y]};

        // all the indices that reference this vertex
        for (auto j = trackingUniqueIds[i - 1]; j < trackingUniqueIds[i]; ++j, k++) {
            meshData.indices[trackingIds[j]] = i - 1;
        }
    }
    for (auto j = trackingUniqueIds[count - 1]; j < rawMeshData.faceIndices.size(); ++j) {
        // meshData.indices[trackingIds[j]] = i - 1;
        // fmt::print(stderr, "first index is  {}\n", trackingIds[j]);
        meshData.indices[trackingIds[j]] = count - 1;
    }
    // fmt::print(stderr, "first index is  {}\n", trackingIds[k]);

    fmt::print(stderr, "back j {}\n", trackingUniqueIds[count - 1]);

    fmt::print(stderr, "max k {}\n", k);

    auto timeTaken = duration<float>(system_clock::now() - startTime).count();
    fmt::print(stderr, "indexing time taken {}\n", timeTaken);

    return meshData;
}

MeshDataElements readObjElementsMap(const std::string& filePath) {
    using namespace std::chrono;

    auto rawMeshData = readObjRaw(filePath);
    auto startTime = system_clock::now();

    MeshDataElements meshData;
    std::unordered_map<vertex3D, uint32_t> uniqueVertices;
    // add groups
    meshData.groupInfos = std::move(rawMeshData.groupInfos);

    //#pragma omp parallel for
    for (auto i = 0u; i < rawMeshData.faceIndices.size(); ++i) {

        vertex3D vertex = {rawMeshData.positions[rawMeshData.faceIndices[i].x],
                           rawMeshData.normals[rawMeshData.faceIndices[i].z],
                           rawMeshData.textureCoords[rawMeshData.faceIndices[i].y]};

        if (uniqueVertices.count(vertex) == 0) {
            uniqueVertices[vertex] = static_cast<uint32_t>(meshData.vertices.size());
            meshData.vertices.push_back(vertex);
        }

        meshData.indices.push_back(uniqueVertices[vertex]);
    }

    fmt::print(stderr, "total unique count {}\n", uniqueVertices.size());

    auto timeTaken = duration<float>(system_clock::now() - startTime).count();
    fmt::print(stderr, "indexing map time taken {}\n", timeTaken);

    return meshData;
}

} // namespace objLoader


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<int, 5, 5> test_int_5_5(const Eigen::Matrix<int, 5, 5>& M) { return M; }
void export_int_5_5() { boost::python::def("test_int_5_5", test_int_5_5); }


#include <iostream>
#include <vector>
#include <array>
#include <algorithm>
#include <fstream>

#include <boost/filesystem.hpp>

#include "fann.h"
#include "floatfann.h"
#include "fann_data.h"
#include "parallel_fann.h"

#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/vfh.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/visualization/histogram_visualizer.h>

int FANN_API test_callback(struct fann *ann, struct fann_train_data *train,
	unsigned int max_epochs, unsigned int epochs_between_reports,
	float desired_error, unsigned int epochs)
{
	printf("Epochs     %8d. MSE: %.5f. Desired-MSE: %.5f\n", epochs, fann_get_MSE(ann), desired_error);
	return 0;
}

//  fann_callback_type callbackTrainning(fann_train_data ann)
int FANN_API callbackTrainning(
    struct fann *ann,
    struct fann_train_data *train,
    unsigned int max_epochs,
    unsigned int epochs_between_reports,
    float desired_error,
    unsigned int epochs
)
{
    ofstream MyFileData("ccccccc_error.txt", ios::app);
    MyFileData << epochs << ", "
               << fann_get_MSE(ann) << ", "
               << fann_get_bit_fail(ann) << ", "
               << desired_error << ", "
               << std::endl;
    std::cout  << epochs << ", "
               << fann_get_MSE(ann) << ", "
               << fann_get_bit_fail(ann) << ", "
               << desired_error << ", "
               << std::endl;
    MyFileData.close();
}

int main()
{
	fann_type *calc_out;
	const unsigned int num_input = 308;
	const unsigned int num_output = 8;
	const unsigned int num_layers = 3;
	const unsigned int num_neurons_hidden = 308;
	const float desired_error = (const float) 0;
	const unsigned int max_epochs = 2000;
	const unsigned int epochs_between_reports = 1;
	struct fann *ann;
	struct fann_train_data *data;

	unsigned int i = 0;
	unsigned int decimal_point;

	printf("Creating network.\n");
	// ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_output);
    // ann = fann_create_standard(5, 308, 616, 308, 64, 8); // bbbbbbb
	ann = fann_create_standard(5, 308, 616, 308, 64, 8); // ccccccc

	data = fann_read_train_from_file("listDataSetv2.1.data");

	fann_set_activation_steepness_hidden(ann, 0.01); //deafault 0.5 //bbbbbb = 0.01
	fann_set_activation_steepness_output(ann, 0.01); //deafault 0.5

    // fann_scale_input_train_data(data, (fann_type) (0), (fann_type) 100);
    // fann_scale_output_train_data(data, (fann_type) (0), (fann_type) 100);

	fann_set_activation_function_hidden(ann, FANN_SIGMOID_STEPWISE);
	fann_set_activation_function_output(ann, FANN_SIGMOID_STEPWISE);

	fann_set_train_stop_function(ann, FANN_STOPFUNC_BIT);
	// fann_set_bit_fail_limit(ann, 0.01f); //default 0.35

	fann_set_training_algorithm(ann, FANN_TRAIN_RPROP); //defailt FANN_TRAIN_RPROP

	fann_init_weights(ann, data);

    fann_set_callback(ann, (fann_callback_type)&callbackTrainning); //callback to save error

	printf("Training network.\n");
	fann_train_on_data(ann, data, max_epochs, epochs_between_reports, desired_error);

	// float error;
    // ofstream MyFileData("ccccccc_error.txt", ios::app);
    // for(i = 1; i <= max_epochs; i++)
	// {
		// error = fann_train_epoch_irpropm_parallel(ann, data, 4);
	// 	printf("Epochs     %8d. Current error: %.10f. Bit Fail: %5d\n", i, error, fann_get_bit_fail(ann));
    //     MyFileData << i << "#"
    //             << error << "#"
    //             << fann_get_bit_fail(ann) << "#"
    //             << std::endl;
	// }
    // MyFileData.close();

	printf("Testing network. %f\n", fann_test_data(ann, data));

	for(i = 0; i < fann_length_train_data(data); i++)
	{
		calc_out = fann_run(ann, data->input[i]);
		printf("test (%f,%f) -> %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f \n, should be %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f , difference=%f\n",
			   data->input[i][0], data->input[i][1],
               calc_out[0], calc_out[1], calc_out[2], calc_out[3], calc_out[4], calc_out[5], calc_out[6], calc_out[7],
               data->output[i][0], data->output[i][1], data->output[i][2], data->output[i][3], data->output[i][4], data->output[i][5], data->output[i][6], data->output[i][7],
			   fann_abs(calc_out[0] - data->output[i][0]));

	}

	printf("Saving network.\n");

	fann_save(ann, "ccccccc.net");

	decimal_point = fann_save_to_fixed(ann, "ccccccc_fixed.net");
	fann_save_train_to_fixed(data, "cccccccfixed.data", decimal_point);

	printf("Cleaning up.\n");
	fann_destroy_train(data);
	fann_destroy(ann);

	return 0;
}


//==================================================================================================
/*!
  @file

  Copyright 2015 NumScale SAS
  Copyright 2015 J.T.Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_SCALAR_IS_GT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SCALAR_IS_GT_HPP_INCLUDED

#include <boost/simd/function/definition/is_gt.hpp>
#include <boost/simd/arch/common/scalar/function/is_greater.hpp>

#endif


// Copyright Oleg Maximenko 2016.
// 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)
//
// See http://github.com/svgpp/svgpp for library home page.

#pragma once

#include <svgpp/definitions.hpp>
#include <boost/spirit/include/qi.hpp>

namespace svgpp { namespace detail
{

template<class Direction, class PropertySource, class LengthFactory, class Iterator>
bool parse_length(
  LengthFactory const & length_factory,
  Iterator & it, Iterator end,
  typename LengthFactory::length_type & out_length);

template<class LengthFactory, class Iterator>
bool parse_percentage_or_length(
  LengthFactory const & length_factory,
  Iterator & it, Iterator end,
  typename LengthFactory::length_type & out_length);

struct length_grammar_tag {};
struct percentage_or_length_grammar_tag {};

template<class Direction, class Iterator, class PropertySource, class LengthFactory>
boost::spirit::qi::grammar<
  Iterator,
  typename LengthFactory::length_type(LengthFactory const &),
  boost::spirit::qi::locals<typename LengthFactory::number_type>
> const & get_length_rule(length_grammar_tag, LengthFactory const &);

template<class Direction, class Iterator, class PropertySource, class LengthFactory>
boost::spirit::qi::grammar<
  Iterator,
  typename LengthFactory::length_type(LengthFactory const &),
  boost::spirit::qi::locals<typename LengthFactory::number_type>
> const & get_length_rule(percentage_or_length_grammar_tag, LengthFactory const &);

}}


// Copyright (c) 2017 Graphcore Ltd. All rights reserved.

#ifndef poplibs_test_NonLinearity_hpp
#define poplibs_test_NonLinearity_hpp

#include "poplibs_support/Compiler.hpp"
#include "poplibs_test/exceptions.hpp"
#include "popnn/NonLinearity.hpp"
#include <boost/multi_array.hpp>

namespace poplibs_test {

// input/output can be pointers to same memory
void nonLinearity(popnn::NonLinearityType nonLinearityType,
                  const double *inputData, double *outputData, std::size_t n);

void nonLinearity(popnn::NonLinearityType nonLinearityType,
                  boost::multi_array_ref<double, 2> array);

void nonLinearity(popnn::NonLinearityType nonLinearityType,
                  boost::multi_array<double, 4> &array);

// For computing the gradient of non-linearity types SWISH and GELU
// `activations` refers to the the input to the activation function rather than
// the output.
void bwdNonLinearity(popnn::NonLinearityType nonLinearityType,
                     const double *activations, double *deltas, std::size_t n);
void bwdNonLinearity(popnn::NonLinearityType nonLinearityType,
                     const boost::multi_array<double, 4> &activations,
                     boost::multi_array<double, 4> &deltas);
void bwdNonLinearity(popnn::NonLinearityType nonLinearityType,
                     const boost::multi_array<double, 2> &activations,
                     boost::multi_array<double, 2> &deltas);

} // End namespace poplibs_test.

#endif // poplibs_test_NonLinearity_hpp