/**
 * qvrefl.cc
 * Copyright 2016 John Lawson
 *
 * 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.
 */
#include <iostream>
#include <string>
#include <unistd.h>

#include <boost/dynamic_bitset.hpp>

#include "qv/mutation_class_loader.h"
#include "qv/equiv_mutation_class_loader.h"
#include "qv/stream_iterator.h"

#include "filtered_iterator.h"
#include "cartan_equiv.h"
#include "cartan_iterator.h"
#include "cartan_mutator.h"
#include "compatible_cartan.h"
#include "mutation_star.h"
#include "semi_positive_filter.h"
#include "vector_mutator.h"
#include "unique_matrix_filter.h"
#include "util.h"

namespace refl {
namespace {
using MatrixVec = std::vector<arma::Mat<int>>;
using UniqueCartanIter =
    FilteredIterator<CartanIterator, arma::Mat<int>, UniqueMatrixFilter>;
using SemiPosIter =
    FilteredIterator<UniqueCartanIter, arma::Mat<int>, SemiPositiveFilter>;

auto get_cartan_iterator(cluster::QuiverMatrix const& q) {
  CartanIterator cartan(q);
  // UniqueCartanIter unique(std::move(cartan));
  // return SemiPosIter(std::move(unique));
  return UniqueCartanIter(std::move(cartan));
}
/**
 * For each vertex in the given quiver, find a semipositive quasi-Cartan which
 * serves that vertex. These matrices are returned in a vector indexed by the
 * label of the vertices.
 *
 * If no such quasi-Cartan companion exists for a given vertex, then there will
 * still be a matrix in the vector, however all its values will be zero.
 */
MatrixVec find_serving_cartans(cluster::QuiverMatrix const& q) {
  MatrixVec result(q.num_cols(), arma::Mat<int>(q.num_rows(), q.num_cols()));
  boost::dynamic_bitset<> found{q.num_cols(), 0};
  const MutationStar star(q);
  const arma::mat initial_vecs(q.num_rows(), q.num_cols(), arma::fill::eye);
  arma::mat mutated_vecs(q.num_rows(), q.num_cols());
  arma::mat gram_matrix(q.num_rows(), q.num_cols());

  CompatibleCartan compatible;

  auto cartan_iter = SemiPosIter(get_cartan_iterator(q));

  while (cartan_iter.has_next() && !found.all()) {
    arma::Mat<int> const& AQ = cartan_iter.next();
    VectorMutator vmut(q, AQ);
    int_fast16_t ncols = AQ.n_cols;
    for (int_fast16_t i = 0; i < ncols; ++i) {
      vmut.mutate(initial_vecs, i, mutated_vecs);
      gram_matrix = util::gram(mutated_vecs, AQ);
      if (!found[i] && compatible(star.qv(i), gram_matrix)) {
        result[i] = AQ;
        found[i] = true;
      }
    }
  }
  return result;
}
/**
 * Print the first semipositive quasi-Cartan companion serving each vertex of
 * the given quiver. If no such companion exists, then print that the vertex is
 * not served by a Cartan matrix.
 */
void output_serving_cartans(cluster::QuiverMatrix const& q,
                            std::ostream& os = std::cout) {
  auto res = refl::find_serving_cartans(q);
  int_fast16_t size = res.size();
  for (int_fast16_t i = 0; i < size; ++i) {
    if (res[i].at(0, 0) != 2) {
      os << i << " is not served by any semi-positive quasi-Cartan"
         << os.widen('\n');
    } else {
      std::string label;
      label.append(std::to_string(i)).append(" served by");
      res[i].print(os, label);
    }
  }
}
/**
 * Find the first semipositive quasi-Cartan fully compatible with q.
 *
 * If no such quasi-Cartan exists then the first item in the returned pair is
 * false, otherwise the first item is true and the second item is this first
 * Cartan matrix.
 */
std::pair<bool, arma::Mat<int>> first_compatible_cartan(
    cluster::QuiverMatrix const& q) {
  bool result = false;
  arma::Mat<int> cartan;
  const MutationStar star(q);
  arma::Mat<int> gram_matrix(q.num_rows(), q.num_cols());

  CompatibleCartan compatible;

  auto cartan_iter = get_cartan_iterator(q);
  SemiPositiveFilter semipos;

  CartanMutator cmut(q);
  while (cartan_iter.has_next() && !result) {
    bool is_comp = true;
    arma::Mat<int> const& AQ = cartan_iter.next();
    int_fast16_t ncols = AQ.n_cols;
    for (int_fast16_t i = 0; is_comp && i < ncols; ++i) {
      cmut(AQ, i, gram_matrix);
      is_comp = compatible(star.qv(i), gram_matrix);
    }
    result = is_comp && semipos(AQ);
    if (result) {
      cartan = AQ;
    }
  }
  return {result, cartan};
}
/* Check if a semipositive cartan matrix is fully compatible with the quiver. */
bool check_compatible(cluster::QuiverMatrix const& q,
                      arma::Mat<int> const& AQ,
                      std::ostream& os = std::cout) {
  bool result = true;
  const MutationStar star(q);
  arma::Mat<int> gram_matrix(q.num_rows(), q.num_cols());

  CompatibleCartan compatible;
  if (!compatible(q, AQ)) {
    os << "Initial matrix not compatible" << os.widen('\n');
    result = false;
  }
  CartanMutator cmut(q);
  int_fast16_t ncols = AQ.n_cols;
  for (int_fast16_t i = 0; i < ncols; ++i) {
    cmut(AQ, i, gram_matrix);
    if (!compatible(star.qv(i), gram_matrix)) {
      os << "Mutation at " << i << " not compatible" << os.widen('\n');
      result = false;
    }
  }
  return result;
}
/**
 * Check if the semipositive quasi-Cartan matrix AQ is fully compatible with the
 * given quiver.
 *
 * To check if it is, the Cartan is mutated in every possible direction, and for
 * each mutation is checked to be a companion of the corresponding mutated
 * quiver.
 */
bool repeat_check_compatible(cluster::QuiverMatrix const& q,
                             MutationStar const& star,
                             arma::Mat<int> const& AQ) {
  static arma::Mat<int> gram_matrix;
  bool result = true;
  gram_matrix.set_size(q.num_rows(), q.num_cols());

  CompatibleCartan compatible;
  result = compatible(q, AQ);

  CartanMutator cmut(q);
  int_fast16_t ncols = AQ.n_cols;
  for (int_fast16_t i = 0; result && i < ncols; ++i) {
    cmut(AQ, i, gram_matrix);
    result = compatible(star.qv(i), gram_matrix);
  }
  return result;
}
/**
 * Check whether the provided quasi-Cartan matrix is a companion of the given
 * quiver.
 *
 * The Cartan is provided as a QuiverMatrix, so will be converted into an arma
 * matrix. This will involve some allocation.
 */
bool check_compatible(cluster::QuiverMatrix const& q,
                      cluster::QuiverMatrix const& cartan,
                      std::ostream& os = std::cout) {
  arma::Mat<int> const AQ = util::to_arma(cartan);
  bool result = check_compatible(q, AQ, os);
  return result;
}
/**
 * Compute all fully-compatible, semipositive quasi-Cartan companions of the
 * given quiver. These Cartan matrices are returned in a vector.
 *
 * Only distinct, i.e. non-equivalent, Cartan matrices will be returned. (Where
 * equivalence is given by flipping signs at a vertex.)
 */
std::vector<arma::Mat<int>> all_compatible(cluster::QuiverMatrix const& q) {
  auto cartan_iter = get_cartan_iterator(q);
  MutationStar const star(q);
  CartanEquiv equiv;
  SemiPositiveFilter semipos;

  std::vector<arma::Mat<int>> result;
  result.reserve(2);

  do {
    auto const& first = cartan_iter.next();
    if (repeat_check_compatible(q, star, first) && semipos(first)) {
      result.push_back(first);
    }
  } while (result.empty() && cartan_iter.has_next());

  if (!result.empty()) {
    while (cartan_iter.has_next()) {
      auto const& n = cartan_iter.next();
      if (repeat_check_compatible(q, star, n) && semipos(n) &&
          std::find_if(result.begin(), result.end(),
                       [&n, &equiv](arma::Mat<int> const& c) {
                         return equiv(c, n);
                       }) == result.end()) {
        result.push_back(n);
      }
    }
  }
  return result;
}
/**
 * Check whether all fully-compatible, semi-positive quasi-Cartan companions of
 * the given quiver are equivalent (up to flipping signs at vertices). If not
 * all the possble companions are printed to the supplied ostream.
 */
bool check_all_compatible_equiv(cluster::QuiverMatrix const& q,
                                std::ostream& os = std::cout) {
  auto compatible_cartans = all_compatible(q);
  bool all_equiv = compatible_cartans.size() == 1;

  if (!all_equiv) {
    for (auto const& n : compatible_cartans) {
      n.print(os, "Compatible:");
    }
  }
  return all_equiv;
}
}
}
enum Func { ListCompatible, SameCompatible, CheckSingle, CompatibleEquiv };
void usage() {
  std::cout << "qvrefl -cels [-m matrix] [-i in_file] [-a cartan]"
            << std::cout.widen('\n');
  std::cout << "  -m Specify matrix to find quasi-Cartan companions of"
            << std::cout.widen('\n');
  std::cout << "  -i Specify input file of matrices to read"
            << std::cout.widen('\n');
  std::cout << "  -c Check the whole mutation class of the matrix (only with "
               "-l or -s)"
            << std::cout.widen('\n');
  std::cout << "  -e Check that all fully compatible cartans are equivalent"
            << std::cout.widen('\n');
  std::cout << "  -l List the first quasi-Cartan serving each vertex"
            << std::cout.widen('\n');
  std::cout << "  -s Check if there is a quasi-Cartan serving all vertices"
            << std::cout.widen('\n');
  std::cout
      << "  -a Specify a cartan matrix to check whether it serves every vertex"
      << std::cout.widen('\n');
  std::cout.flush();
}
int main(int argc, char* argv[]) {
  std::string matrix;
  std::string cartan;
  Func function = Func::ListCompatible;
  std::string input;
  bool mut_class = false;
  int c;
  while ((c = getopt(argc, argv, "elsm:i:hca:")) != -1) {
    switch (c) {
      case 'e':
        function = Func::CompatibleEquiv;
        break;
      case 'm':
        matrix = optarg;
        break;
      case 'a':
        cartan = optarg;
        function = Func::CheckSingle;
        break;
      case 'l':
        function = Func::ListCompatible;
        break;
      case 's':
        function = Func::SameCompatible;
        break;
      case 'i':
        input = optarg;
        break;
      case 'c':
        mut_class = true;
        break;
      case 'h':
      case '?':
      default:
        usage();
        return 1;
    }
  }
  if (matrix.length() < 4 && input.empty()) {
    usage();
    return 2;
  }
  if (function == Func::ListCompatible) {
    if (matrix.length() > 0) {
      if (mut_class) {
        cluster::EquivQuiverMatrix q(matrix);
        cluster::EquivMutationClassLoader cl(q);
        while (cl.has_next()) {
          auto quiver = cl.next_ptr();
          std::cout << *quiver << ":" << std::cout.widen('\n');
          refl::output_serving_cartans(*quiver);
        }
      } else {
        cluster::QuiverMatrix q(matrix);
        refl::output_serving_cartans(q);
      }
    } else {
      std::ifstream inf;
      inf.open(input);
      if (!inf.is_open()) {
        std::cerr << "Could not open file " << input << std::endl;
        return 1;
      }
      cluster::StreamIterator<cluster::QuiverMatrix> iter(inf);
      while (iter.has_next()) {
        auto quiver = iter.next();
        std::cout << *quiver << std::cout.widen('\n');
        refl::output_serving_cartans(*quiver);
      }
    }
  } else if (function == Func::SameCompatible) {
    if (matrix.length() > 0) {
      if (mut_class) {
        cluster::EquivQuiverMatrix q(matrix);
        cluster::EquivMutationClassLoader cl(q);
        while (cl.has_next()) {
          auto quiver = cl.next_ptr();
          bool result = refl::first_compatible_cartan(*quiver).first;
          std::cout << (result ? "True: " : "False: ") << *quiver
                    << std::cout.widen('\n');
        }
      } else {
        cluster::QuiverMatrix q(matrix);
        auto result = refl::first_compatible_cartan(q);
        std::cout << (result.first ? result.second : "False")
                  << std::cout.widen('\n');
      }
    } else {
      std::ifstream inf;
      inf.open(input);
      if (!inf.is_open()) {
        std::cerr << "Could not open file " << input << std::endl;
        return 1;
      }
      cluster::StreamIterator<cluster::QuiverMatrix> iter(inf);
      while (iter.has_next()) {
        auto quiver = iter.next();
        bool result = refl::first_compatible_cartan(*quiver).first;
        std::cout << (result ? "True: " : "False: ") << *quiver
                  << std::cout.widen('\n');
      }
    }
  } else if (function == Func::CheckSingle) {
    if (matrix.empty() || cartan.empty()) {
      usage();
      return 4;
    }
    refl::check_compatible(cluster::QuiverMatrix{matrix},
                           cluster::QuiverMatrix{cartan});
  } else if (function == Func::CompatibleEquiv) {
    if (!matrix.empty()) {
      std::cout << std::boolalpha << refl::check_all_compatible_equiv(
                                         cluster::QuiverMatrix{matrix})
                << std::cout.widen('\n');
    } else if (!input.empty()) {
      std::ifstream inf;
      inf.open(input);
      if (!inf.is_open()) {
        std::cerr << "Could not open file " << input << std::endl;
        return 1;
      }
      cluster::StreamIterator<cluster::QuiverMatrix> iter(inf);
      while (iter.has_next()) {
        auto quiver = iter.next();
        std::cout << std::boolalpha << refl::check_all_compatible_equiv(*quiver)
                  << ": " << *quiver << std::cout.widen('\n');
      }
    } else {
      usage();
      return 4;
    }
  } else {
    usage();
  }
  std::cout.flush();
  return 0;
}


/* Copyright Institute of Sound and Vibration Research - All rights reserved */

#include <libefl/vector_functions.hpp>
#include <libefl/vector_functions_reference.hpp>

#include <libefl/aligned_array.hpp>

#include <boost/test/unit_test.hpp>

#include <algorithm>
#include <cmath>
#include <complex>
#include <iostream>
#include <valarray>

namespace visr
{
namespace rbbl
{
namespace test
{

BOOST_AUTO_TEST_CASE( complexMultiply1 )
{
  const std::size_t alignment = 0;

  std::size_t vecSize = 27;
  efl::AlignedArray < std::complex<float> > a( vecSize, alignment );
  efl::AlignedArray < std::complex<float> > b( vecSize, alignment );

  efl::AlignedArray < std::complex<float> > c( vecSize, alignment );
  efl::AlignedArray < std::complex<float> > reference( vecSize, alignment );

  for( std::size_t runIdx( 0 ); runIdx < vecSize; ++runIdx )
  {
    a[runIdx] = std::complex<float>( static_cast<float>(runIdx), -static_cast<float>(runIdx) );
    b[runIdx] = std::complex<float>( -2.0f*static_cast<float>(runIdx), 3.0f*static_cast<float>(runIdx) );
  }

  std::fill_n( c.data(), vecSize, 0.0f );

  efl::ErrorCode const resRef = efl::reference::vectorMultiply( a.data( ), b.data( ), reference.data( ), vecSize, alignment );
  BOOST_CHECK( resRef == efl::noError );

  efl::ErrorCode const res = efl::vectorMultiply( a.data(), b.data(), c.data(), vecSize, alignment );
  BOOST_CHECK( res == efl::noError );

  efl::AlignedArray < std::complex<float> > ref( vecSize, alignment );
  std::transform( a.data( ), a.data( ) + vecSize, b.data( ), ref.data( ), [=]( std::complex<float> const & x, std::complex<float> const & y ) { return x * y; } );

  for( std::size_t vecIdx( 0 ); vecIdx < vecSize; ++vecIdx )
  {
    std::cout << "c[" << vecIdx << "]: " << ref[vecIdx] << " : " << reference[vecIdx] << " : " << c[vecIdx] << std::endl;
  }
}

BOOST_AUTO_TEST_CASE( complexMultiplyConstant )
{
  const std::size_t alignment = 2;

  std::size_t vecSize = 23;
  efl::AlignedArray < std::complex<float> > a( vecSize, alignment );
  std::complex<float> const c = { 0.385f, -0.75f};

  efl::AlignedArray < std::complex<float> > result( vecSize, alignment );
  efl::AlignedArray < std::complex<float> > reference( vecSize, alignment );

  for( std::size_t runIdx( 0 ); runIdx < vecSize; ++runIdx )
  {
    a[runIdx] = std::complex<float>( static_cast<float>(runIdx), -static_cast<float>(runIdx) );
  }

  efl::ErrorCode const resRef = efl::reference::vectorMultiplyConstantAddInplace( c, a.data( ), reference.data( ), vecSize, alignment );
  BOOST_CHECK( resRef == efl::noError );

  efl::ErrorCode const res = efl::vectorMultiplyConstantAddInplace( c, a.data(), result.data(), vecSize, alignment );
  BOOST_CHECK( res == efl::noError );

  for( std::size_t vecIdx( 0 ); vecIdx < vecSize; ++vecIdx )
  {
    std::cout << vecIdx << ": " << reference[vecIdx] << " : " << result[vecIdx] << std::endl;
  }

  for( std::size_t vecIdx( 0 ); vecIdx < vecSize; ++vecIdx )
  {
    BOOST_CHECK_CLOSE( std::abs(result[vecIdx] - reference[vecIdx]), 0.0f, 1.0e-6 );
  }
}

} // namespace test
} // namespace rbbl
} // namespace visr


/*
 * This file belongs to the Galois project, a C++ library for exploiting parallelism.
 * The code is being released under the terms of the 3-Clause BSD License (a
 * copy is located in LICENSE.txt at the top-level directory).
 *
 * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
 * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
 * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
 * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
 * DEALING OR USAGE OF TRADE.  NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
 * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
 * shall University be liable for incidental, special, indirect, direct or
 * consequential damages or loss of profits, interruption of business, or
 * related expenses which may arise from use of Software or Documentation,
 * including but not limited to those resulting from defects in Software and/or
 * Documentation, or loss or inaccuracy of data of any kind.
 */

#include "galois/Galois.h"
#include "galois/Bag.h"
#include "galois/Timer.h"
#include "galois/ParallelSTL.h"
#include "llvm/Support/CommandLine.h"
#include "Lonestar/BoilerPlate.h"

#include <gmpxx.h>
//#include <givaro/givgfq.h>
//#include <givaro/StaticElement.h>

#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/bind.hpp>
#include <boost/utility.hpp>

#include <fstream>
#include <map>
#include <numeric>
#include <algorithm>

namespace cll = llvm::cl;

static const char* name = "Buchberger Algorithm";
static const char* desc =
    "Generates Groebner basis for polynomial ideal using Buchberger Algorithm";
static const char* url = 0;

enum MonomialOrder { lex, grevlex };

static cll::opt<std::string> filename(cll::Positional,
                                      cll::desc("<input file>"), cll::Required);
static cll::opt<MonomialOrder> monomialOrder(
    cll::desc("Monomial order:"),
    cll::values(clEnumVal(lex, "lexicographic"),
                clEnumVal(grevlex, "graded reverse lexicographic"),
                clEnumValEnd),
    cll::init(grevlex));

#if 0
//! Unfortunate pun
struct GaloisField {
  typedef Givaro::GFqDom<long> Field;
  typedef Field::Element element_type;
  Field F;

  GaloisField(): F(2, 1) { }

  void init() { }

  void inverse(element_type& a) const {
    F.invin(a);
  }

  void add(element_type& a, const element_type& b) const {
    F.addin(a, b);
  }

  void subtract(element_type& a, const element_type& b) const {
    F.subin(a, b);
  }

  void negate(element_type& a) const {
    F.negin(a);
  }

  void divide(element_type& a, const element_type& b) const {
    F.divin(a, b);
  }

  void multiply(element_type& a, const element_type& b) const {
    F.mulin(a, b);
  }

  void assign(element_type& a, const element_type& b) const {
    F.assign(a, b);
  }

  void assignOne(element_type& a) const {
    F.assign(a, F.one);
  }

  int sign(const element_type& a) const {
    return neq(a, 0) ? 1 : 0;
  }

  bool neq(const element_type& a, const element_type& b) const {
    return a != b;
  }

  void write(std::ostream& out, const element_type& a) const {
    out << a;
  }
};
#endif

struct RationalField {
  typedef mpq_class element_type;

  void init() {}

  void inverse(element_type& a) const { a = 1 / a; }

  void add(element_type& a, const element_type& b) const { a += b; }

  void subtract(element_type& a, const element_type& b) const { a -= b; }

  void negate(element_type& a) const { a = -a; }

  void divide(element_type& a, const element_type& b) const { a /= b; }

  void multiply(element_type& a, const element_type& b) const { a *= b; }

  void assign(element_type& a, const element_type& b) const { a = b; }

  void assignOne(element_type& a) const { a = 1; }

  int sign(const element_type& a) const { return sgn(a); }

  bool neq(const element_type& a, const element_type& b) const {
    return a != b;
  }

  void write(std::ostream& out, const element_type& a) const {
    out << a.get_str();
  }
};

typedef RationalField Field;
// typedef GaloisField Field;
typedef Field::element_type number;

Field TheField;

template <class Order, class Alloc>
class Ring;

//! Weights to ease vectorization of lexicographic sorting
static int powersOfTwo[16] __attribute__((__aligned__(16)));

struct Term {
  typedef char exp_type;
  typedef char __attribute__((__aligned__(16))) * __restrict__ exp_ptr_type;

private:
  template <class Order, class Alloc>
  friend class Ring;

  Field::element_type m_coef;
  exp_ptr_type m_exps;
  int m_totalDegree;

public:
  Term() : m_coef(1), m_exps(NULL), m_totalDegree(0) {}

  Field::element_type& coef() { return m_coef; }
  const Field::element_type& coef() const { return m_coef; }

  exp_type& exp(int i) { return m_exps[i]; }
  const exp_type& exp(int i) const { return m_exps[i]; }

  int totalDegree() const { return m_totalDegree; }

  exp_ptr_type const& exps() const { return m_exps; }
  exp_ptr_type& exps() { return m_exps; }

  //! Compute LCM
  template <class R>
  void makeLcm(const Term& a, const Term& b, R& ring) {
    // Vectorized.
    // GCC 4.7 vectorizer prefers (1) over (2)
    int N = ring.numVars();
    for (int i = 0; i < N; ++i) { // (1)
      // for (int i = 0; i < ring.numVars(); ++i) { // (2)
      m_exps[i] = std::max(a.m_exps[i], b.m_exps[i]);
    }
  }

  template <class R>
  bool equals(const Term& b, R& ring) {
    // TODO vectorize
    int N = ring.numVars();
    for (int i = 0; i < N; ++i) {
      if (b.exp(i) != exp(i))
        return false;
    }
    return true;
  }

  //! a | b ?
  template <class R>
  bool divides(const Term& b, const R& ring) const {
    // TODO vectorize
    for (int i = 0; i < ring.numVars(); ++i) {
      if (b.exp(i) < exp(i))
        return false;
    }
    return true;
  }

  //! Relatively prime?
  template <class R>
  bool relPrime(const Term& b, const R& ring) const {
    // TODO vectorize
    for (int i = 0; i < ring.numVars(); ++i) {
      if (exp(i) && b.exp(i))
        return false;
    }
    return true;
  }
};

//! Sequence of terms
class Poly {
  // TODO: more bucketed representations!!!
  typedef std::vector<Term*> Terms;
  Terms terms;
  int m_totalDegree;

public:
  typedef Terms::iterator iterator;
  typedef Terms::const_iterator const_iterator;

  Poly() : m_totalDegree(0) {}

  int totalDegree() const { return m_totalDegree; }

  const Term* head() const { return terms.front(); }

  Term* head() { return terms.front(); }

  const_iterator begin() const { return terms.begin(); }

  const_iterator end() const { return terms.end(); }

  iterator begin() { return terms.begin(); }

  iterator end() { return terms.end(); }

  void push(Term* t) {
    terms.push_back(t);
    m_totalDegree += t->totalDegree();
  }

  bool empty() const { return terms.empty(); }

  //! f = f * s
  template <class R>
  void scaleBy(const Field::element_type& s, R& ring) {
    for (iterator ii = begin(), ei = end(); ii != ei; ++ii) {
      Term* t = *ii;
      TheField.multiply(t->coef(), s);
    }
  }

  //! f = f * a/b
  template <class R>
  void scaleBy(const Term& a, const Term& b, R& ring) {
    Field::element_type s;
    TheField.assign(s, a.coef());
    TheField.divide(s, b.coef());

    m_totalDegree = 0;

    for (iterator ii = begin(), ei = end(); ii != ei; ++ii) {
      Term* t = *ii;
      // Vectorized.
      for (int i = 0; i < ring.numVars(); ++i) {
        assert(a.exp(i) >= b.exp(i));
        t->exp(i) += a.exp(i) - b.exp(i);
      }
      TheField.multiply(t->coef(), s);
      ring.generateTotalDegree(*t);
      m_totalDegree += t->totalDegree();
    }
  }
};

class PolySet {
  // TODO: better allocator
  typedef std::vector<Poly*> Polys;

  Polys polys;

public:
  typedef Polys::iterator iterator;
  typedef Polys::const_iterator const_iterator;

  void push(Poly* p) { polys.push_back(p); }

  iterator begin() { return polys.begin(); }

  iterator end() { return polys.end(); }

  const_iterator begin() const { return polys.begin(); }

  const_iterator end() const { return polys.end(); }
};

template <class Order, class Alloc = std::allocator<char>>
class Ring : private boost::noncopyable {
private:
  Order order;
  Alloc m_alloc;
  int m_numVars;
  std::list<Poly*> polys;
  std::list<Term*> terms;

  //! Sizeof Term plus padding and space for exps
  size_t sizeofTerm() const {
    return sizeof(Term) + 15 + m_numVars * sizeof(Term::exp_type);
  }

  typedef Term::exp_type* exps_ptr_t;

  //! Find 16-byte aligned address after ptr
  exps_ptr_t expsPtr(const Term* ptr) const {
    return reinterpret_cast<exps_ptr_t>(((uintptr_t)(ptr + 1) + 15) & ~0x0F);
  }

public:
  const static int kBlockSize = 16 / sizeof(Term::exp_type);

  template <class AllocNew>
  struct realloc {
    typedef Ring<Order, AllocNew> other;
  };

  Ring(int numVars, const Alloc& alloc = Alloc()) : m_alloc(alloc) {
    m_numVars = ((numVars + kBlockSize - 1) / kBlockSize) * kBlockSize;
  }

  ~Ring() {
    for (std::list<Poly*>::iterator ii = polys.begin(), ei = polys.end();
         ii != ei; ++ii) {
      (*ii)->~Poly();
      m_alloc.deallocate(reinterpret_cast<char*>(*ii), sizeof(Poly));
    }
    for (std::list<Term*>::iterator ii = terms.begin(), ei = terms.end();
         ii != ei; ++ii) {
      (*ii)->~Term();
      m_alloc.deallocate(reinterpret_cast<char*>(*ii), sizeofTerm());
    }
  }

  // TODO
  Ring tempRing() const { return *this; }

  //! For ease of vectorization, this will always be a multiple of blockSize
  int numVars() const { return m_numVars; }

  bool gt(const Term& a, const Term& b) const { return order.gt(a, b, *this); }

  Poly& makePoly(const Poly& p = Poly()) {
    Poly* ptr = reinterpret_cast<Poly*>(m_alloc.allocate(sizeof(Poly)));
    new (ptr) Poly();
    for (Poly::const_iterator ii = p.begin(), ei = p.end(); ii != ei; ++ii) {
      const Term* t = *ii;
      ptr->push(&makeTerm(*t));
    }
    polys.push_back(ptr);
    return *ptr;
  }

  Term& makeTerm(const Term& t = Term()) {
    Term* ptr = reinterpret_cast<Term*>(m_alloc.allocate(sizeofTerm()));
    new (ptr) Term(t);
    ptr->m_exps = expsPtr(ptr);

    if (t.exps() != NULL) {
      memcpy(ptr->m_exps, t.exps(), m_numVars * sizeof(Term::exp_type));
    } else {
      memset(ptr->m_exps, 0, m_numVars * sizeof(Term::exp_type));
    }
    terms.push_back(ptr);
    return *ptr;
  }

  void generateTotalDegree(Term& t) {
    // Vectorized.
    // GCC 4.7 vectorizer prefers (1) over (2)
    int sum               = 0;
    Term::exp_ptr_type ii = t.exps();
    for (int i = 0; i < numVars(); ++i) // (1)
      sum += ii[i];
    t.m_totalDegree = sum;
    // std::accumulate(t.exps(), t.exps() + numVars(), 0, std::plus<int>()); //
    // (2)
  }

  void write(std::ostream& out, const Term& t,
             const std::vector<std::string>* idMap = NULL) const {
    if (TheField.sign(t.coef()) >= 0)
      out << "+";
    TheField.write(out, t.coef());

    for (int i = 0; i < numVars(); ++i) {
      int exp = t.exp(i);
      if (exp == 0)
        continue;
      out << " ";
      if (idMap)
        out << (*idMap)[i];
      else
        out << "x" << i;
      if (exp > 1)
        out << "^" << exp;
    }
  }

  void write(std::ostream& out, const Term* t,
             const std::vector<std::string>* idMap = NULL) const {
    write(out, *t, idMap);
  }

  void write(std::ostream& out, const Poly& p,
             const std::vector<std::string>* idMap = NULL) const {
    for (Poly::const_iterator ii = p.begin(), ei = p.end(); ii != ei; ++ii) {
      write(out, *ii, idMap);
    }
  }

  void write(std::ostream& out, const PolySet& polys,
             const std::vector<std::string>* idMap = NULL) const {
    for (PolySet::const_iterator ii = polys.begin(), ei = polys.end(); ii != ei;
         ++ii) {
      write(out, **ii, idMap);
      if (ii + 1 != ei)
        out << ", ";
    }
  }

  //! Rank under Buchberger's normal selection strategy
  int normalRank(const Poly& f, const Poly& g) const {
    const Term* fh = f.head();
    const Term* gh = g.head();
    int retval     = 0;

    // Vectorized.
    for (int i = 0; i < numVars(); ++i) {
      retval += std::max(fh->exp(i), gh->exp(i));
    }

    return retval;
  }

  //! Rank pairs using sugar strategy
  int rankPair(const Poly& f, const Poly& g) const {
    const Term* fh = f.head();
    const Term* gh = g.head();
    int retval     = std::max(f.totalDegree() - fh->totalDegree(),
                          g.totalDegree() - gh->totalDegree());
    int n          = normalRank(f, g);
    // prefer pairs with least sugar component, breaking ties with normalRank
    int sugar = retval + n;
    return (sugar << 8) | (n & 0xFF);
  }
};

class LexOrder {
  template <class R>
  bool gtSimple(const Term& a, const Term& b, const R& ring) const {
    for (int i = 0; i < ring.numVars(); ++i) {
      if (a.exp(i) < b.exp(i))
        return false;
      else if (a.exp(i) > b.exp(i))
        return true;
    }
    return false;
  }

  template <class R>
  bool gtVectorized(const Term& a, const Term& b, const R& ring) const {
    Term::exp_ptr_type aa = a.exps();
    Term::exp_ptr_type bb = b.exps();
    int* pp               = powersOfTwo;

    for (int block = 0; block < ring.numVars(); block += R::kBlockSize) {
      // Sigh. In GCC 4.7 this inner loop (1) is not vectorized because of
      // the condition (2)
      int smaller = 0;
      int larger  = 0;
      for (int i = 0; i < R::kBlockSize; ++i) { // (1)
        int idx = block + i;
        smaller += aa[idx] < bb[idx] ? pp[R::kBlockSize - i] : 0;
        larger += aa[idx] > bb[idx] ? pp[R::kBlockSize - i] : 0;
      }
      if (smaller == larger) // (2)
        continue;
      return larger > smaller;
    }

    return false;
  }

public:
  template <class R>
  bool gt(const Term& a, const Term& b, const R& ring) const {
    return gtVectorized(a, b, ring);
  }
};

class GrevlexOrder {
  template <class R>
  bool gtSimple(const Term& a, const Term& b, const R& ring) const {
    if (a.totalDegree() != b.totalDegree())
      return a.totalDegree() > b.totalDegree();

    for (int i = ring.numVars() - 1; i >= 0; --i) {
      if (a.exp(i) < b.exp(i))
        return true;
      else if (a.exp(i) > b.exp(i))
        return false;
    }
    return false;
  }

  template <class R>
  bool gtVectorized(const Term& a, const Term& b, const R& ring) const {
    if (a.totalDegree() != b.totalDegree())
      return a.totalDegree() > b.totalDegree();

    Term::exp_ptr_type aa = a.exps();
    Term::exp_ptr_type bb = b.exps();
    int* pp               = powersOfTwo;

    for (int block = ring.numVars() - 1; block >= 0; block -= R::kBlockSize) {
      // Sigh. In GCC 4.7 this inner loop (1) is not vectorized because of
      // the condition (2)
      int smaller = 0;
      int larger  = 0;
      for (int i = 0; i < R::kBlockSize; ++i) { // (1)
        int idx = block - i;
        smaller += aa[idx] < bb[idx] ? pp[R::kBlockSize - i] : 0;
        larger += aa[idx] > bb[idx] ? pp[R::kBlockSize - i] : 0;
      }
      if (smaller == larger) // (2)
        continue;
      return larger > smaller;
    }

    return false;
  }

public:
  template <class R>
  bool gt(const Term& a, const Term& b, const R& ring) const {
    return gtVectorized(a, b, ring);
  }
};

struct PolyPair {
  Poly* a;
  Poly* b;
  Term* lcm;
  int index;
  bool m_useless;
  PolyPair(Poly* _a, Poly* _b, Term* _lcm, int _index = 0)
      : a(_a), b(_b), lcm(_lcm), index(_index), m_useless(false) {}
  bool useless() const { return m_useless; }
  void makeUseless() { m_useless = true; }
};

//! r = a - b
template <class R>
Poly* subtract(const Poly& a, const Poly& b, R& ring) {
  Poly* result            = &ring.makePoly();
  Poly::const_iterator aa = a.begin(), ea = a.end();
  Poly::const_iterator bb = b.begin(), eb = b.end();

  while (aa != ea && bb != eb) {
    Term* a = *aa;
    Term* b = *bb;
    Term t;
    if (ring.gt(*a, *b)) {
      t = Term(*a);
      ++aa;
    } else if (ring.gt(*b, *a)) {
      t = Term(*b);
      TheField.negate(t.coef());
      ++bb;
    } else {
      t = Term(*a);
      TheField.subtract(t.coef(), b->coef());
      ++aa;
      ++bb;
    }
    if (TheField.neq(t.coef(), 0)) {
      result->push(&ring.makeTerm(t));
    }
  }
  for (; aa != ea; ++aa) {
    result->push(*aa);
  }
  for (; bb != eb; ++bb) {
    Term* b = *bb;
    Term t(*b);
    TheField.negate(t.coef());
    assert(TheField.neq(t.coef(), 0));
    result->push(&ring.makeTerm(t));
  }
  return result;
}

//! Compute s-polynomial.
//! spoly(f,g) = f * lcm/LT(f) - g * lcm/LT(g) where lcm = LCM(LM(f), LM(g))
template <class R>
Poly* spoly(const Term& lcm, const Poly& f, const Poly& g, R& ring) {
  // ff = f * lcm/LT(f)
  Poly& ff = ring.makePoly(f);
  ff.scaleBy(lcm, *f.head(), ring);
  // gg = g * lcm/LT(g)
  Poly& gg = ring.makePoly(g);
  gg.scaleBy(lcm, *g.head(), ring);

  return subtract(ff, gg, ring);
};

//! Reduce f with respect to polys.
//! Not canonical unless polys is groebner basis (obvs)
template <class R>
Poly* reduce(const Poly& f, const PolySet& polys, R& ring) {
  //  std::cerr << "    "; ring.write(std::cerr, f);

  const Poly* cur         = &f;
  Poly::const_iterator ff = cur->begin(), ef = cur->end();

  while (ff != ef) {
    bool reduced = false;

    for (PolySet::const_iterator pp = polys.begin(), ep = polys.end(); pp != ep;
         ++pp) {
      const Poly* p = *pp;

      // when we are doing inter reduction, i.e., f reduce G when f \in G, skip
      // ourselves
      if (&f == p)
        continue;

      // Right now we never delete terms reduced to zero by interReduce() so
      // ignore them here
      if (p->empty())
        continue;

      const Term* ph = p->head();

      if (!ph->divides(**ff, ring))
        continue;

      //      std::cerr << " ("; ring.write(std::cerr, *ph); std::cerr << "|";
      //      ring.write(std::cerr, **ff); std::cerr << ")";
      // cur = cur - p * ff/ph
      Poly& g = ring.makePoly(*p);
      g.scaleBy(**ff, *ph, ring);
      cur = subtract(*cur, g, ring);

      ff = cur->begin();
      ef = cur->end();
      //      std::cerr << " => "; ring.write(std::cerr, *cur);
      reduced = true;
      break;
    }
    if (!reduced)
      ++ff;
  }

  //  std::cerr << "\n";

  return const_cast<Poly*>(cur);
}

galois::Statistic bkUpdate("BKUpdate");
galois::Statistic mfUpdate("MFUpdate");
galois::Statistic bpUpdate("BPUpdate");

//! Updates basis with h, adds new pairs and marks some previous pairs as
//! useless.
template <class R1, class R2, class Pushable>
void update(Poly* h, PolySet& basis, galois::InsertBag<PolyPair>& pairs,
            R1& localRing, R2& ring, Pushable& out) {
  // Gebauer-Moeller criterion B_k
  Term& lcm_t = localRing.makeTerm();
  for (galois::InsertBag<PolyPair>::iterator ii = pairs.begin(),
                                             ei = pairs.end();
       ii != ei; ++ii) {
    PolyPair& p = *ii;
    if (p.useless())
      continue;
    if (!h->head()->divides(*p.lcm, localRing))
      continue;
    lcm_t.makeLcm(*h->head(), *p.a->head(), localRing);
    if (lcm_t.equals(*p.lcm, localRing))
      continue;
    lcm_t.makeLcm(*h->head(), *p.b->head(), localRing);
    if (lcm_t.equals(*p.lcm, localRing))
      continue;
    p.makeUseless();
    bkUpdate += 1;
  }

  // Successive application of various deletion criteria
  Term& lcm_hi = localRing.makeTerm();
  Term& lcm_hj = localRing.makeTerm();
  for (PolySet::const_iterator ii = basis.begin(), ei = basis.end(); ii != ei;
       ++ii) {

    // Buchberger's Product criterion
    if (h->head()->relPrime(*(*ii)->head(), localRing)) {
      bpUpdate += 1;
      continue;
    }

    // Gebauer-Moeller criteria M and F
    lcm_hi.makeLcm(*h->head(), *(*ii)->head(), localRing);
    bool condM = false;
    for (PolySet::const_iterator jj = ii + 1, ej = basis.end(); jj != ej;
         ++jj) {
      lcm_hj.makeLcm(*h->head(), *(*jj)->head(), localRing);
      if (lcm_hj.divides(lcm_hi, localRing)) {
        condM = true;
        break;
      }
    }

    if (!condM) {
      Term& lcm = ring.makeTerm(lcm_hi);
      out.push(&pairs.push(PolyPair(h, *ii, &lcm, ring.rankPair(*h, **ii))));
    } else {
      mfUpdate += 1;
    }
  }

  basis.push(h);
}

galois::Statistic zeroUpdate("ZeroUpdate");

template <class R>
struct Process {
  typedef typename R::template realloc<
      galois::PerIterAllocTy::rebind<char>::other>::other LocalRing;

  PolySet& basis;
  galois::InsertBag<PolyPair>& pairs;
  R& ring;

  Process(PolySet& _basis, galois::InsertBag<PolyPair>& _pairs, R& r)
      : basis(_basis), pairs(_pairs), ring(r) {}

  void operator()(const PolyPair* p, galois::UserContext<PolyPair*>& ctx) {
    if (p->useless()) {
      return;
    }

    LocalRing localRing(ring.numVars(), ctx.getPerIterAlloc());

    Poly* s = spoly(*p->lcm, *p->a, *p->b, localRing);
    Poly* h = reduce(*s, basis, localRing);

    if (!h->empty()) {
      Poly* hh = &ring.makePoly(*h);
      update(hh, basis, pairs, localRing, ring, ctx);
    } else {
      zeroUpdate += 1;
    }
  }
};

template <class R>
struct Verifier {
  PolySet& g;
  R& ring;

  Verifier(PolySet& _g, R& r) : g(_g), ring(r) {}

  bool operator()(const PolyPair& p) {
    // TODO opportunity for temporary
    Poly* s = spoly(*p.lcm, *p.a, *p.b, ring);
    Poly* h = reduce(*s, g, ring);
    return !h->empty();
  }
};

template <class C, class R>
void allPairs(const PolySet& ideal, C& c, R& ring) {
  for (PolySet::const_iterator ii = ideal.begin(), ei = ideal.end(); ii != ei;
       ++ii) {
    for (PolySet::const_iterator jj = ideal.begin(), ej = ideal.end(); jj != ej;
         ++jj) {
      if (*ii == *jj)
        continue;
      if ((*ii)->empty() || (*jj)->empty())
        continue;
      Term& lcm = ring.makeTerm();
      lcm.makeLcm(*(*ii)->head(), *(*jj)->head(), ring);
      c.push(PolyPair(*ii, *jj, &lcm, ring.rankPair(**ii, **jj)));
    }
  }
}

struct Indexer {
  int operator()(const PolyPair& p) const { return p.index; }
  int operator()(const PolyPair* p) const { return p->index; }
};

template <class R>
void interReduce(PolySet& polys, R& ring) {
  for (PolySet::iterator ii = polys.begin(), ei = polys.end(); ii != ei; ++ii) {
    Poly*& p      = *ii;
    Poly* reduced = reduce(*p, polys, ring);
    // TODO opportunity for temporary
    boost::swap(p, reduced);
    if (p->empty())
      continue;
    Field::element_type s(p->head()->coef());
    TheField.inverse(s);
    p->scaleBy(s, ring);
  }
}

template <class R>
void buchberger(PolySet& ideal, PolySet& basis, R& ring) {
  galois::InsertBag<PolyPair> pairs;
  galois::InsertBag<PolyPair*> initial;

  for (PolySet::iterator ii = ideal.begin(), ei = ideal.end(); ii != ei; ++ii) {
    if ((*ii)->empty())
      continue;
    update(*ii, basis, pairs, ring, ring, initial);
  }
  using namespace galois::worklists;
  typedef OrderedByIntegerMetric<Indexer, PerSocketChunkLIFO<8>> OBIM;
  galois::for_each(initial.begin(), initial.end(),
                   Process<R>(basis, pairs, ring), galois::wl<OBIM>());

  interReduce(basis, ring);
}

namespace parser {
namespace qi     = boost::spirit::qi;
namespace ascii  = boost::spirit::ascii;
namespace fusion = boost::fusion;

struct Coef {
  typedef boost::variant<char, char> Sign;
  Sign sign;
  typedef boost::variant<fusion::vector<int, int>, int> Rational;
  galois::optional<Rational> rational;
};

struct Mono {
  std::string id;
  galois::optional<int> expo;
};

struct Term {
  Coef coef;
  std::vector<Mono> monos;
};

struct Poly {
  std::vector<Term> terms;
};
} // namespace parser

// Macros need to be used outside of namespace scope

BOOST_FUSION_ADAPT_STRUCT(parser::Coef,
                          (parser::Coef::Sign,
                           sign)(galois::optional<parser::Coef::Rational>,
                                 rational))

BOOST_FUSION_ADAPT_STRUCT(parser::Mono,
                          (std::string, id)(galois::optional<int>, expo))

BOOST_FUSION_ADAPT_STRUCT(parser::Term,
                          (parser::Coef, coef)(std::vector<parser::Mono>,
                                               monos))

BOOST_FUSION_ADAPT_STRUCT(parser::Poly, (std::vector<parser::Term>, terms))

namespace parser {
namespace qi     = boost::spirit::qi;
namespace ascii  = boost::spirit::ascii;
namespace fusion = boost::fusion;

// +2xy^3 - z^3, ...
template <typename It>
struct PolySetGrammar
    : qi::grammar<It, std::vector<parser::Poly>(), ascii::space_type> {
  qi::rule<It, std::string(), ascii::space_type> id;
  qi::rule<It, parser::Coef::Sign(), ascii::space_type> sign;
  qi::rule<It, parser::Coef::Rational(), ascii::space_type> rational;
  qi::rule<It, parser::Coef(), ascii::space_type> coef;
  qi::rule<It, int(), ascii::space_type> expo;
  qi::rule<It, parser::Mono(), ascii::space_type> mono;
  qi::rule<It, parser::Term(), ascii::space_type> term;
  qi::rule<It, std::vector<parser::Term>(), ascii::space_type> poly;
  qi::rule<It, std::vector<parser::Poly>(), ascii::space_type> start;

  PolySetGrammar() : PolySetGrammar::base_type(start, "polynomial list") {
    // sign := + | -
    sign %= qi::char_("+") | qi::char_("-");
    sign.name("sign");
    // rational := (int / int) | int
    rational %= (qi::int_ >> qi::lit("/") > qi::int_) | qi::int_;
    rational.name("rational");
    // coef := sign rational?
    coef %= sign >> -rational;
    coef.name("coefficent");
    // expo := ^ int
    expo %= qi::lit("^") > qi::int_;
    expo.name("exponent");
    // mono := id expo?
    id %= qi::lexeme[+ascii::alnum];
    id.name("identifier");
    mono %= id >> -expo;
    mono.name("mononomial");
    // term := coef "*"? (mono "*"?)*
    term %= coef > -qi::lit("*") >> *(mono >> -qi::lit("*"));
    term.name("term");
    // poly := term+
    poly %= +term;
    poly.name("polynomial");
    // start := poly (, poly)*
    start %= poly % ',';

    using boost::phoenix::construct;
    using boost::phoenix::val;
    qi::on_error<qi::fail>(start, std::cerr
                                      << val("Error! Expecting ")
                                      << qi::labels::_4 << val(" here: \"")
                                      << construct<std::string>(qi::labels::_3,
                                                                qi::labels::_2)
                                      << val("\"\n"));
  }
};

// Q[x y z w ...]
template <class It>
struct HeaderGrammar
    : qi::grammar<It, std::vector<std::string>(), ascii::space_type> {
  qi::rule<It, std::string(), ascii::space_type> id;
  qi::rule<It, std::vector<std::string>(), ascii::space_type> start;

  HeaderGrammar() : HeaderGrammar::base_type(start, "Ring Definition") {
    id %= qi::lexeme[+ascii::alnum];
    id.name("id");
    // Q[ <id> (, <id>)* ]
    start %= qi::lit("Q[") >> id % ',' >> qi::lit("]");

    using boost::phoenix::construct;
    using boost::phoenix::val;
    qi::on_error<qi::fail>(start, std::cerr
                                      << val("Error! Expecting ")
                                      << qi::labels::_4 << val(" here: \"")
                                      << construct<std::string>(qi::labels::_3,
                                                                qi::labels::_2)
                                      << val("\"\n"));
  }
};

//! A simple parser.
template <class Order>
class Parser : private boost::noncopyable {
  typedef ::Term TheTerm;
  typedef ::Poly ThePoly;
  typedef Ring<Order> TheRing;
  typedef std::map<std::string, int> NameMap;
  typedef std::vector<std::string> IdMap;
  NameMap nameMap;
  IdMap idMap;
  PolySet m_polys;
  Order order;
  TheRing* m_ring;

  template <class It>
  void readHeader(It begin, It end) {
    HeaderGrammar<It> grammar;
    bool r = qi::phrase_parse(begin, end, grammar, ascii::space, idMap);

    if (!r || begin != end) {
      std::cerr << "Parse failure.\n";
      abort();
    }

    // Parse header
    for (IdMap::iterator ii = idMap.begin(), ei = idMap.end(); ii != ei; ++ii) {
      if (nameMap.find(*ii) != nameMap.end()) {
        std::cerr << "Duplicate variable name: " << *ii << "\n";
        abort();
      }
      int index    = nameMap.size();
      nameMap[*ii] = index;
    }
  }

  template <class It>
  void readPolys(It begin, It end, std::vector<parser::Poly>& polys) {
    PolySetGrammar<It> grammar;
    bool r = qi::phrase_parse(begin, end, grammar, ascii::space, polys);
    if (!r || begin != end) {
      std::cerr << "Parse failure at: " << std::string(begin, end) << "\n";
      abort();
    }
  }

  struct RationalVisitor : public boost::static_visitor<Field::element_type> {
    Field::element_type operator()(const fusion::vector<int, int>& x) const {
      Field::element_type a(fusion::at_c<0>(x));
      Field::element_type b(fusion::at_c<1>(x));
      TheField.divide(a, b);
      return a;
    }
    Field::element_type operator()(const int& x) const {
      return Field::element_type(x);
    }
  };

  void parseCoef(const Coef& coef, Field::element_type& c) {
    const char* sign = boost::get<char>(&coef.sign);
    bool neg         = *sign == '-';

    TheField.assign(c, 1);
    if (coef.rational) {
      c = boost::apply_visitor(RationalVisitor(), *coef.rational);
    }
    // c.canonicalize();
    if (neg)
      TheField.negate(c);
  }

  void parseMono(const Mono& mono, TheTerm& t) {
    NameMap::iterator ii = nameMap.find(mono.id);
    if (ii == nameMap.end()) {
      std::cerr << "Unknown variable name: " << mono.id << "\n";
      abort();
    }
    int index = ii->second;
    int expo  = mono.expo ? *mono.expo : 1;
    t.exp(index) += expo;
  }

  void parseTerm(const Term& term, std::vector<TheTerm*>& terms) {
    TheTerm& t = m_ring->makeTerm();
    std::for_each(term.monos.begin(), term.monos.end(),
                  boost::bind(&Parser::parseMono, this, _1, boost::ref(t)));
    parseCoef(term.coef, t.coef());
    m_ring->generateTotalDegree(t);
    terms.push_back(&t);
  }

  struct GreaterThan {
    Ring<Order>& ring;
    GreaterThan(Ring<Order>& r) : ring(r) {}
    bool operator()(const TheTerm* a, const TheTerm* b) const {
      return ring.gt(*a, *b);
    }
  };

  void parsePoly(const Poly& poly) {
    std::vector<TheTerm*> terms;
    std::for_each(poly.terms.begin(), poly.terms.end(),
                  boost::bind(&Parser::parseTerm, this, _1, boost::ref(terms)));

    std::sort(terms.begin(), terms.end(), GreaterThan(*m_ring));
    ThePoly& p = m_ring->makePoly();
    for (std::vector<TheTerm*>::iterator ii = terms.begin(), ei = terms.end();
         ii != ei; ++ii) {
      p.push(*ii);
    }
    m_polys.push(&p);
  }

  void writeHeader(std::ostream& out) const {
    out << "Q[";
    for (IdMap::const_iterator ii = idMap.begin(), ei = idMap.end(); ii != ei;
         ++ii) {
      out << *ii;
      if (ii + 1 != ei)
        out << ", ";
    }
    out << "]\n";
  }

  void writePolys(std::ostream& out, const PolySet& polys) const {
    m_ring->write(out, polys, &idMap);
    out << "\n";
  }

public:
  typedef TheRing RingTy;

  Parser() : m_ring(0) {}

  ~Parser() {
    if (m_ring)
      delete m_ring;
  }

  void read(std::istream& in) {
    std::string header;
    getline(in, header);
    readHeader(header.begin(), header.end());

    m_ring = new Ring<Order>(idMap.size());

    in.unsetf(std::ios::skipws);
    boost::spirit::istream_iterator begin(in), end;

    std::vector<Poly> polys;
    readPolys(begin, end, polys);
    std::for_each(polys.begin(), polys.end(),
                  boost::bind(&Parser::parsePoly, this, _1));

    std::cout << "Rational ring of " << nameMap.size() << " ("
              << m_ring->numVars() << ") variables\n";
    std::cout << "Ideal of " << polys.size() << " polynomials\n";
  }

  void write(std::ostream& out, const PolySet& polys) {
    writeHeader(out);
    writePolys(out, polys);
  }

  RingTy& ring() { return *m_ring; }

  PolySet& polys() { return m_polys; }
};

} // namespace parser

template <class Order>
void run() {
  std::ifstream scanner(filename.c_str());
  if (!scanner.good()) {
    std::cerr << "Couldn't open file: " << filename << "\n";
    abort();
  }

  typedef parser::Parser<Order> ParserTy;
  ParserTy P;
  P.read(scanner);
  scanner.close();

  P.write(std::cout, P.polys()); // REMOVe

  TheField.init();

  galois::StatTimer T;
  T.start();
  interReduce(P.polys(), P.ring());
  PolySet basis;
  buchberger(P.polys(), basis, P.ring());
  T.stop();

  if (!skipVerify) {
    galois::InsertBag<PolyPair> pairs;
    allPairs(basis, pairs, P.ring());
    Verifier<typename ParserTy::RingTy> v(basis, P.ring());
    if (galois::ParallelSTL::find_if(pairs.begin(), pairs.end(), v) !=
        pairs.end()) {
      std::cerr << "Basis is not Groebner.\n";
      assert(0 && "Triangulation failed");
      abort();
    }
  }
  P.write(std::cout, basis);
  std::cout << "Groebner basis with "
            << std::distance(basis.begin(), basis.end()) << " polynomials\n";
}

int main(int argc, char** argv) {
  galois::StatManager statManager;
  statManager.push(zeroUpdate);
  statManager.push(bkUpdate);
  statManager.push(mfUpdate);
  statManager.push(bpUpdate);

  LonestarStart(argc, argv, name, desc, url);

  for (unsigned i = 0; i < sizeof(powersOfTwo) / sizeof(*powersOfTwo); ++i)
    powersOfTwo[i] = 2 << (i - 1);

  switch (monomialOrder) {
  case lex:
    run<LexOrder>();
    break;
  case grevlex:
    run<GrevlexOrder>();
    break;
  default:
    abort();
  }

  return 0;
}


#pragma once

#include <vector>
#include <Eigen/Core>
#include <Eigen/LU>
#include <Eigen/Dense>

namespace ImageInformationAnalyzer
{
    namespace Domain
    {
        using namespace std::literals::string_literals;

        class FloatingPointImageData
        {
        public:
            const int Width;
            const int Height;
            const std::vector<std::vector<double>> ImageBuffer;
            const std::vector<std::vector<Eigen::Vector3d>> NormalBuffer;

        private:
            double maxValue_;
            double minValue_;

        public:
            explicit FloatingPointImageData::FloatingPointImageData(const int width, const int height
                , const std::vector<std::vector<double>> imageBuffer
                , const std::vector<std::vector<Eigen::Vector3d>> normalBuffer) : Width(width), Height(height), ImageBuffer(imageBuffer), NormalBuffer(normalBuffer)
            {
                maxValue_ = DBL_MIN;
                minValue_ = DBL_MAX;

                for(auto line : imageBuffer)
                {
                    for(auto value : line)
                    {
                        if(value > maxValue_)
                        {
                            maxValue_ = value;
                        }
                        if(value < minValue_)
                        {
                            minValue_ = value;
                        }
                    }
                }
            }

            inline double GetMaxValue() const { return maxValue_; }
            inline double GetMinValue() const { return minValue_; }

            virtual ~FloatingPointImageData() = default;
        };
    }
}


/*!
 * @file
 * Contains unit tests for `boost::mpl::permutations`.
 */

#include <boost/mpl/permutations.hpp>

#include <boost/mpl/assert.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/set.hpp>
#include <boost/mpl/set_equal.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/type_traits/is_same.hpp>

template <typename Sequence, typename Permutations>
struct assert_permutations {
    BOOST_MPL_ASSERT((boost::mpl::set_equal<
        typename boost::mpl::permutations<Sequence>::type,
        Permutations,
        boost::mpl::equal<
            boost::mpl::_1, boost::mpl::_2,
            boost::mpl::quote2<boost::is_same>
        >
    >));
};

using namespace boost::mpl;
template <int> struct t;

// empty
template struct assert_permutations<
    vector<>,
    set<>
>;

// 1 element
template struct assert_permutations<
    vector<t<0> >,
    set<
        vector<t<0> >
    >
>;

// 2 elements
template struct assert_permutations<
    vector<t<0>, t<1> >,
    set<
        vector<t<0>, t<1> >,
        vector<t<1>, t<0> >
    >
>;

// 3 elements
template struct assert_permutations<
    vector<t<0>, t<1>, t<2> >,
    set<
        vector<t<0>, t<1>, t<2> >,
        vector<t<0>, t<2>, t<1> >,

        vector<t<1>, t<0>, t<2> >,
        vector<t<1>, t<2>, t<0> >,

        vector<t<2>, t<0>, t<1> >,
        vector<t<2>, t<1>, t<0> >
    >
>;

int main() { }


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to 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)

#ifndef BOOST_GEOMETRY_ARITHMETIC_DOT_PRODUCT_HPP
#define BOOST_GEOMETRY_ARITHMETIC_DOT_PRODUCT_HPP

#include <cstddef>

#include <boost/concept/requires.hpp>

#include <boost/geometry/geometries/concepts/point_concept.hpp>
#include <boost/geometry/util/select_coordinate_type.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template <typename P1, typename P2, std::size_t Dimension, std::size_t DimensionCount>
struct dot_product_maker
{
    typedef typename select_coordinate_type<P1, P2>::type coordinate_type;

    static inline coordinate_type apply(P1 const& p1, P2 const& p2)
    {
        return get<Dimension>(p1) * get<Dimension>(p2)
            + dot_product_maker<P1, P2, Dimension+1, DimensionCount>::apply(p1, p2);
    }
};

template <typename P1, typename P2, std::size_t DimensionCount>
struct dot_product_maker<P1, P2, DimensionCount, DimensionCount>
{
    typedef typename select_coordinate_type<P1, P2>::type coordinate_type;

    static inline coordinate_type apply(P1 const& p1, P2 const& p2)
    {
        return get<DimensionCount>(p1) * get<DimensionCount>(p2);
    }
};

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

/*!
    \brief Computes the dot product (or scalar product) of 2 vectors (points).
    \ingroup arithmetic
    \param p1 first point
    \param p2 second point
    \return the dot product
 */
template <typename P1, typename P2>
inline typename select_coordinate_type<P1, P2>::type dot_product(
        P1 const& p1, P2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<P1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<P2>) );

    return detail::dot_product_maker
        <
            P1, P2,
            0, dimension<P1>::type::value - 1
        >::apply(p1, p2);
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ARITHMETIC_DOT_PRODUCT_HPP


//
// boost-geometry-render
//
// Copyright (c) 2011
// osyo-manga : http://d.hatena.ne.jp/osyo-manga/
//
// License:
// Boost Software License - Version 1.0
// <http://www.boost.org/LICENSE_1_0.txt>
//
#include <cstdlib>
#include <gl/glut.h>
#include <gl/graphics.hpp>
#include <gl/geometry_render.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/geometry/geometries/linestring.hpp>

namespace bg = boost::geometry;

template<typename Geometry>
struct geometry_renderer{
	geometry_renderer(Geometry& geometry) : geometry_(geometry){};
	void operator ()(){
		glColor3d(1.0f, 0.0f, 0.0f);
		gl::render(geometry_);
	}
private:
	Geometry& geometry_;
};

template<typename Geometry>
geometry_renderer<Geometry>
make_geometry_renderer(Geometry& geometry){
	return geometry_renderer<Geometry>(geometry);
}

int
main(int argc, char* argv[]){
	typedef bg::model::d2::point_xy<float> point_type;
	bg::model::polygon<point_type> polygon;
	bg::exterior_ring(polygon) = boost::assign::list_of<point_type>
		( 0.0f,  0.5f)
		( 0.5f,  0.0f)
		( 0.1f, -0.1f)
		( 0.0f, -0.5f)
		(-0.1f, -0.1f)
		(-0.5f,  0.0f);

	gl::graphics g(argc, argv, 500, 500);
	gl::displayfunc(g, make_geometry_renderer(polygon));

	g.run();
	return 0;
}


#include <iostream>
#include <random>
#include <cmath>
#include <armadillo>
#include <sstream>
#include "logistic_regression.h"
#include "file.h"
//#include "barrier.h"
using namespace std;
using namespace arma;
using namespace multiverso;

//initialize the logistic regression parameter settings
logistic_regression::logistic_regression(){}
logistic_regression::logistic_regression(int data_size,
                            double learning_rate,
                            double regularized,
                            int max_num_iteration,
                            int epoch_size,
                            int dimention,
                            double gamma/*momentum*/)
{
    DATA_SIZE = data_size;
	LEARNING_RATE = learning_rate;
	REGULARIZED = regularized;
	MAX_NUM_ITERATION = max_num_iteration;
	EPOCH_SIZE = epoch_size;
    DIMENTION = dimention;
    parameter = zeros<vec>(DIMENTION);
    full_gradient = zeros<vec>(DIMENTION);
    local_parameter = zeros<vec>(DIMENTION);
    momentum = zeros<vec>(DIMENTION);
    GAMMA = gamma;
}
logistic_regression::logistic_regression(Option* option_)
{
    DATA_SIZE = option_->data_size;
	LEARNING_RATE = option_->learning_rate;
	REGULARIZED = option_->regularized;
	MAX_NUM_ITERATION = option_->max_num_iteration;
	EPOCH_SIZE = option_->epoch_size;
    DIMENTION = option_->dimention;
    sample_epoch = zeros<vec>(EPOCH_SIZE*MAX_NUM_ITERATION);
    parameter = zeros<vec>(DIMENTION);
    full_gradient = zeros<vec>(DIMENTION);
    local_parameter = zeros<vec>(DIMENTION);
    momentum = zeros<vec>(DIMENTION);
    GAMMA = option_->gamma;
    this->option_ = option_;

}

//record the begin time of the training process
void logistic_regression::begin(time_t& begin)
{
    time(&begin);
}
//record the end time of the training process
void logistic_regression::end(time_t& end)
{
    time(&end);
}
//training the parameters. The fl2-regularization is added
void logistic_regression::train(int trainer_id, multiverso::Barrier *barrier)
{
    //produce the samples by a certain probability distribution
    default_random_engine random(time(NULL));
    if(trainer_id==0)
    {
        //set the epoch size. Defaultly, it is set by the variable: EPOCH_SIZE
        setEpochSize(EPOCH_SIZE);
        sample_epoch = produceSamples(random);
    }
    barrier->Wait();
    for(int i=0;i<MAX_NUM_ITERATION;i++)
    {
multiverso::Log::Info(">>>>>>>>>>>>>>>>learning thread %dth!\n",trainer_id);
        //compute the full gradient in parallel way
        vec full_gradient_thread = computeFullGradient(parameter,trainer_id, option_->thread_cnt);
        //sum all the full gradient in serial way
        mutex_.lock();
        full_gradient = full_gradient+full_gradient_thread;
        mutex_.unlock();
        local_parameter = parameter;
        barrier->Wait();
        for(int j=trainer_id;j<EPOCH_SIZE;j+=option_->thread_cnt)
        {
            //compute the reduced variance
            vec vr = computeReducedVariance(parameter, local_parameter, full_gradient, sample_epoch(i*EPOCH_SIZE+j));
            //update the parameters
            //add the write lock
            mutex_.lock();
            updateParameters(local_parameter, vr, LEARNING_RATE);
            mutex_.unlock();
        }
        barrier->Wait();
        if(trainer_id == 0)
        {
           //Identify the parameters for the next iteration
           identifyParameters(local_parameter, parameter);
           //evaluate the loss
           if(i%1==0){
               computeLoss(parameter, training_x, training_y);
	       //parameter.load("parameter.txt");
 	       }
        }//end if
    }//end for
}

//produce the samples by a certain probability distribution
vec logistic_regression::produceSamples(std::default_random_engine random)
{
    int n_instances = EPOCH_SIZE*MAX_NUM_ITERATION;
    vec samples = zeros<vec>(n_instances);
    uniform_int_distribution<int> dis1(0, DATA_SIZE-1);
    for (int i=0;i<n_instances;i++)
    {
        samples(i) = dis1(random);
    }
    return samples;
}

//set the epoch size. Defaultly, it is set by the variable: EPOCH_SIZE
void logistic_regression::setEpochSize(int size)
{
    EPOCH_SIZE = size;
}

//compute the stochastic local gradient
vec logistic_regression::computeStochasticGradient(vec& parameters, int index)
{
    double w_x = as_scalar(parameters.t()*training_x.col(index));
    double y = training_y[index];
    double temp0 = (1/(1+std::exp(y*w_x)))*(-1*y);
    vec gradient(temp0*training_x.col(index)+2*REGULARIZED*parameters);
    return gradient;
}

//compute the full gradient via multiple threads
vec logistic_regression::computeFullGradient(vec& global_parameter,int trainer_id, int thread_cnt)
{
    vec full_gradient_local = zeros<vec>(DIMENTION);
    for(int i=trainer_id;i<DATA_SIZE;i+=thread_cnt)//consider the constant
    {
        vec temp0 = computeStochasticGradient(global_parameter, i);
        full_gradient_local = full_gradient_local + temp0;
    }
    full_gradient_local = full_gradient_local/DATA_SIZE;

    return full_gradient_local;
}

//compute the reduced variance
vec logistic_regression::computeReducedVariance(vec& global_parameter, vec& local_parameter, vec& full_gradient, int index)
{
    vec vr = zeros<vec>(DIMENTION);
    vec temp0 = computeStochasticGradient(local_parameter, index);
    vec temp1 = computeStochasticGradient(global_parameter, index);
    vr=temp0-temp1+full_gradient;
    return vr;
}

//update the parameters
void logistic_regression::updateParameters(vec& local_parameter, vec& vr, double learning_rate)
{
    vec temp = learning_rate*vr + GAMMA*momentum/*momentum*/;
    local_parameter = local_parameter - temp;
    momentum = temp;
}

//Identify the parameters for the next iteration
void logistic_regression::identifyParameters(vec& local_parameter, vec& global_parameter)
{
    global_parameter = local_parameter;
}

//compute the loss function
double logistic_regression::computeLoss(vec& parameter, sp_mat& x, vec& y)
{
    double loss=0;
    for(int i=0;i<DATA_SIZE;i++)
    {
        double temp = as_scalar(parameter.t()*x.col(i));
        loss = loss - log(1/(1+exp(-1*temp*y(i))));
    }
    loss = loss/DATA_SIZE+REGULARIZED*(as_scalar(parameter.t()*parameter));
    ostringstream s_loss;
    s_loss<<loss<<"\n";
    string loss_str = s_loss.str();
    file f("lr_loss.txt");
    f.write(loss_str);
    //multiverso::Log::Info("The loss now is: %f\n",loss);
    return loss;
}

//get ready to parepare the data from the multiverso framework
void logistic_regression::init(DataBlock* data_block)
{
    sp_mat temp_trn_x;
    sp_mat temp_tst_x;
    vec temp_trn_y;
    vec temp_tst_y;

    data_block->GetSamples(temp_trn_x,temp_tst_x,temp_trn_y,temp_tst_y);
    training_x = temp_trn_x.t();
    training_y = temp_trn_y;
}

//the entrance from the multiverso to the training thread, and the training process is finally started.
void logistic_regression::train_test(int trainer_id, multiverso::Barrier *barrier)
{
    int fast, slow, self;
    double wait_time=0;
    multiverso::Multiverso::GetClock(&fast, &slow, &self, &wait_time);
    //log the wait time

    //begin training
    multiverso::Log::Info("Process: %d and thread: %d begins!\n", multiverso::Multiverso::ProcessRank(),trainer_id);
    time_t begin_time=0;
    time_t end_time=0;
    begin(begin_time);
    train(trainer_id,barrier);
    end(end_time);
}

//set the parameters when pulling the parameters from multiverso
void logistic_regression::setParameters(std::vector<double*> &blocks)
{
    //NOTICE: since logistic regression conducts the binary classification, the number of class is 2. (Here, class_num=1 means the number of class is 2.)
    for(int i=0;i<option_->class_num;i++)
    {
        for (int j=0;j < option_->dimention;j++)
        {
            parameter[j] = blocks[i][j];//since class_num=1, parameter will be valued once.
        }
    }
}

//get the parameters
void logistic_regression::getParameters(std::vector<double*> &blocks)
{
    for(int i=0;i < option_->class_num; i++)
    {
        for (int j=0;j < option_->dimention;j++)
        {
            blocks[i][j] = parameter[j];
        }
    }
}


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

#include <boost/simd/arithmetic/functions/correct_fma.hpp>
#include <boost/simd/include/functions/scalar/two_add.hpp>
#include <boost/simd/include/functions/scalar/two_prod.hpp>
#include <boost/simd/include/functions/scalar/ldexp.hpp>
#include <boost/simd/include/functions/scalar/max.hpp>
#include <boost/simd/include/functions/scalar/multiplies.hpp>
#include <boost/simd/include/functions/scalar/exponent.hpp>
#include <boost/simd/include/functions/scalar/sign.hpp>
#include <boost/simd/include/functions/scalar/bitwise_cast.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/dispatch/attributes.hpp>

namespace boost { namespace simd { namespace ext
{

  BOOST_DISPATCH_IMPLEMENT         ( correct_fma_, tag::cpu_
                                   , (A0)
                                   , (scalar_< single_<A0> >)
                                     (scalar_< single_<A0> >)
                                     (scalar_< single_<A0> >)
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(3)
    {
      return static_cast<A0>( static_cast<double>(a0)*static_cast<double>(a1)
                            + static_cast<double>(a2)
                            );
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( correct_fma_, tag::cpu_
                                   , (A0)
                                   , (scalar_< floating_<A0> >)
                                     (scalar_< floating_<A0> >)
                                     (scalar_< floating_<A0> >)
                                   )
  {
    typedef A0 result_type;

    BOOST_SIMD_FUNCTOR_CALL_REPEAT(3)
    {
      result_type p, rp, s, rs;
#ifndef BOOST_SIMD_DONT_CARE_FMA_OVERFLOW
      typedef typename boost::dispatch::meta::as_integer<A0>::type iA0;
      iA0 e0 = exponent(a0);
      iA0 e1 = exponent(a1);
      iA0 e = -boost::simd::max(e0, e1)/2;
      result_type ae2  = ldexp(a2, e);
      bool choose = (e0 > e1);
      result_type amax = choose ? ldexp(a0, e) : ldexp(a1, e);
      result_type amin = choose ? a1 : a0;
      two_prod(amax, amin, p, rp);
      two_add(p, ae2, s, rs);
      return ldexp(s+(rp+rs), -e);
#else
      two_prod(a0, a1, p, rp);
      two_add(p, a2, s, rs);
      return s+(rp+rs);
#endif
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( correct_fma_, tag::cpu_
                                   , (A0)
                                   , (scalar_< int_<A0> >)
                                     (scalar_< int_<A0> >)
                                     (scalar_< int_<A0> >)
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(3)
    {
      // correct fma has to ensure "no intermediate overflow".
      // This is done in the case of signed integers by transtyping to unsigned type
      // to perform the computations in a guaranteed 2-complement environment
      // since signed integer oveflow in C++ produces "undefined results"
      typedef typename dispatch::meta::as_integer<A0, unsigned>::type utype;
      return A0(correct_fma(utype(a0), utype(a1), utype(a2)));
    }
  };

  BOOST_DISPATCH_IMPLEMENT         ( correct_fma_, tag::cpu_
                                   , (A0)
                                   , (scalar_< uint_<A0> >)
                                     (scalar_< uint_<A0> >)
                                     (scalar_< uint_<A0> >)
                                   )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(3)
    {
      return multiplies(a0, a1)+a2;
    }
  };
} } }

#endif


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include <igl/polyvector_field_poisson_reconstruction.h>
#include <igl/grad.h>
#include <igl/doublearea.h>
#include <igl/sparse.h>
#include <igl/repdiag.h>
#include <igl/slice.h>
#include <igl/slice_into.h>
#include <igl/colon.h>

#include <Eigen/Sparse>

template <typename DerivedV, typename DerivedF, typename DerivedSF, typename DerivedS>
IGL_INLINE void igl::polyvector_field_poisson_reconstruction(
  const Eigen::PlainObjectBase<DerivedV> &Vcut,
  const Eigen::PlainObjectBase<DerivedF> &Fcut,
  const Eigen::PlainObjectBase<DerivedS> &sol3D_combed,
                                                               Eigen::PlainObjectBase<DerivedSF> &scalars)
  {
    Eigen::SparseMatrix<typename DerivedV::Scalar> gradMatrix;
    igl::grad(Vcut, Fcut, gradMatrix);

    Eigen::VectorXd FAreas;
    igl::doublearea(Vcut, Fcut, FAreas);
    FAreas = FAreas.array() * .5;

    int nf = FAreas.rows();
    Eigen::SparseMatrix<typename DerivedV::Scalar> M,M1;
    Eigen::VectorXi II = igl::colon<int>(0, nf-1);

    igl::sparse(II, II, FAreas, M1);
    igl::repdiag(M1, 3, M) ;

    int half_degree = sol3D_combed.cols()/3;

    int numF = Fcut.rows();
    scalars.setZero(Vcut.rows(),half_degree);

    Eigen::SparseMatrix<typename DerivedV::Scalar> Q = gradMatrix.transpose()* M *gradMatrix;

    //fix one point at Ik=fix, value at fixed xk=0
    int fix = 0;
    Eigen::VectorXi Ik(1);Ik<<fix;
    Eigen::VectorXd xk(1);xk<<0;

    //unknown indices
    Eigen::VectorXi Iu(Vcut.rows()-1,1);
    Iu<<igl::colon<int>(0, fix-1),  igl::colon<int>(fix+1,Vcut.rows()-1);

    Eigen::SparseMatrix<typename DerivedV::Scalar> Quu, Quk;
    igl::slice(Q, Iu, Iu, Quu);
    igl::slice(Q, Iu, Ik, Quk);
    Eigen::SimplicialLDLT<Eigen::SparseMatrix<typename DerivedV::Scalar> > solver;
    solver.compute(Quu);

    Eigen::VectorXd vec; vec.setZero(3*numF,1);
    for (int i =0; i<half_degree; ++i)
    {
      vec<<sol3D_combed.col(i*3+0),sol3D_combed.col(i*3+1),sol3D_combed.col(i*3+2);
      Eigen::VectorXd b = gradMatrix.transpose()* M * vec;
      Eigen::VectorXd bu = igl::slice(b, Iu);

      Eigen::VectorXd rhs = bu-Quk*xk;
      Eigen::VectorXd yu = solver.solve(rhs);

      Eigen::VectorXd y(Vcut.rows(),1);
      igl::slice_into(yu, Iu, 1, y);y(Ik[0])=xk[0];
      scalars.col(i) = y;
    }
}

template <typename DerivedV, typename DerivedF, typename DerivedSF, typename DerivedS, typename DerivedE>
IGL_INLINE double igl::polyvector_field_poisson_reconstruction(
                                                               const Eigen::PlainObjectBase<DerivedV> &Vcut,
                                                               const Eigen::PlainObjectBase<DerivedF> &Fcut,
                                                               const Eigen::PlainObjectBase<DerivedS> &sol3D_combed,
                                                               Eigen::PlainObjectBase<DerivedSF> &scalars,
                                                               Eigen::PlainObjectBase<DerivedS> &sol3D_recon,
                                                               Eigen::PlainObjectBase<DerivedE> &max_error )
{

  igl::polyvector_field_poisson_reconstruction(Vcut, Fcut, sol3D_combed, scalars);

  Eigen::SparseMatrix<typename DerivedV::Scalar> gradMatrix;
  igl::grad(Vcut, Fcut, gradMatrix);
  int numF = Fcut.rows();
  int half_degree = sol3D_combed.cols()/3;

    //    evaluate gradient of found scalar function
  sol3D_recon.setZero(sol3D_combed.rows(),sol3D_combed.cols());

    for (int i =0; i<half_degree; ++i)
    {
      Eigen::VectorXd vec_poisson = gradMatrix*scalars.col(i);
      sol3D_recon.col(i*3+0) = vec_poisson.segment(0*numF, numF);
      sol3D_recon.col(i*3+1) = vec_poisson.segment(1*numF, numF);
      sol3D_recon.col(i*3+2) = vec_poisson.segment(2*numF, numF);
    }

    max_error.setZero(numF,1);
    for (int i =0; i<half_degree; ++i)
    {
      Eigen::VectorXd diff = (sol3D_recon.block(0, i*3, numF, 3)-sol3D_combed.block(0, i*3, numF, 3)).rowwise().norm();
      diff = diff.array() / sol3D_combed.block(0, i*3, numF, 3).rowwise().norm().array();
      max_error = max_error.cwiseMax(diff.cast<typename DerivedE::Scalar>());
    }

    return max_error.mean();
  }

  #ifdef IGL_STATIC_LIBRARY
  // Explicit template instantiation
  template double igl::polyvector_field_poisson_reconstruction<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 1, 0, -1, 1> >&);
template double igl::polyvector_field_poisson_reconstruction<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
  #endif


//  (C) Copyright Nick Thompson 2020.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <benchmark/benchmark.h>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/mpfr.hpp>

using namespace boost::math::constants;
using boost::multiprecision::mpfr_float;

void LaplaceLimit(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(laplace_limit<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(LaplaceLimit)->RangeMultiplier(2)->Range(128, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Dottie(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(dottie<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Dottie)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void ReciprocalFibonacci(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(reciprocal_fibonacci<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(ReciprocalFibonacci)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Pi(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(pi<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Pi)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Gauss(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(gauss<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Gauss)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Exp1(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(e<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Exp1)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Catalan(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(catalan<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Catalan)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Plastic(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(plastic<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Plastic)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void RootTwo(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(root_two<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(RootTwo)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void ZetaThree(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(zeta_three<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(ZetaThree)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Euler(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(euler<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Euler)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void LnTwo(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(ln_two<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(LnTwo)->RangeMultiplier(2)->Range(512, 1<<20)->Complexity()->Unit(benchmark::kMicrosecond);

void Glaisher(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(glaisher<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

BENCHMARK(Glaisher)->RangeMultiplier(2)->Range(512, 4096)->Complexity()->Unit(benchmark::kMicrosecond);

void Khinchin(benchmark::State& state)
{
    mpfr_float::default_precision(state.range(0));
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(khinchin<mpfr_float>());
    }
    state.SetComplexityN(state.range(0));
}

// There is a performance bug in the Khinchin constant:
BENCHMARK(Khinchin)->RangeMultiplier(2)->Range(512, 512)->Complexity()->Unit(benchmark::kMicrosecond);

BENCHMARK_MAIN();


#include <jni.h>
#include <string>
#include <math.h>
#include <iostream>
#include <Eigen/Dense>
#include <ceres/ceres.h>

using Eigen::MatrixXd;

extern "C" JNIEXPORT jstring JNICALL
Java_com_qyh_cerestest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

//    Eigen::MatrixXd m = Eigen::MatrixXd::Random(3, 3);
//    m = MatrixXd::Constant(3, 3, 1.2) * 50;

    return env->NewStringUTF(hello.c_str());
}

using namespace Eigen;

Vector3f vec;
Vector3f vec2;
Vector3f vecRtrn;

void vecLoad(float x, float y, float z, float x2, float y2, float z2) {
    vec(0) = x;
    vec(1) = y;
    vec(2) = z;
    vec2(0) = x2;
    vec2(1) = y2;
    vec2(2) = z2;
}

void vecAdd(Vector3f vecA, Vector3f vecB) {
    vecRtrn = vecA + vecB;
}

extern "C"
JNIEXPORT jfloatArray JNICALL
Java_com_qyh_cerestest_MainActivity_test(JNIEnv *env, jobject instance, jfloatArray array1_,
                                         jfloatArray array2_) {
    jfloatArray result;
    result = env->NewFloatArray(3);
    if (result == NULL) {
        return NULL; /* out of memory error thrown */
    }

    jfloat array1[3];
    jfloat* flt1 = env->GetFloatArrayElements( array1_,0);
    jfloat* flt2 = env->GetFloatArrayElements( array2_,0);

    vecLoad(flt1[0], flt1[1], flt1[2], flt2[0], flt2[1], flt2[2]);
    vecAdd(vec, vec2);

    array1[0] = vecRtrn[0];
    array1[1] = vecRtrn[1];
    array1[2] = vecRtrn[2];

    env->ReleaseFloatArrayElements(array1_, flt1, 0);
    env->ReleaseFloatArrayElements(array2_, flt2, 0);
    env->SetFloatArrayRegion(result, 0, 3, array1);
    return result;

}

using namespace ceres;

struct CostFunctor {
    template <typename T>
    bool operator()(const T* const x, T* residual) const {
        residual[0] = T(10.0) - x[0];
        return true;
    }
};

extern "C"
JNIEXPORT jstring JNICALL
Java_com_qyh_cerestest_MainActivity_testCeres(JNIEnv *env, jobject instance) {
    std::string hello = "Hello Ceres";

    // 寻优参数x的初始值，为5
    double initial_x = 5.0;
    double x = initial_x;

    // 第二部分：构建寻优问题
    Problem problem;
    CostFunction* cost_function =
            new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor); //使用自动求导，将之前的代价函数结构体传入，第一个1是输出维度，即残差的维度，第二个1是输入维度，即待寻优参数x的维度。
    problem.AddResidualBlock(cost_function, NULL, &x); //向问题中添加误差项，本问题比较简单，添加一个就行。

    //第三部分： 配置并运行求解器
    Solver::Options options;
    options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法
    options.minimizer_progress_to_stdout = true;//输出到cout
    Solver::Summary summary;//优化信息
    ceres::Solve(options, &problem, &summary);//求解!!!

    std::cout << summary.BriefReport() << "\n";//输出优化的简要信息
    //最终结果
    std::cout << "x : " << initial_x
              << " -> " << x << "\n";

    return env->NewStringUTF(summary.BriefReport().c_str());
}