#define BOOST_TEST_MODULE BNMR_SLR
#include <boost/test/included/unit_test.hpp>

#include <cmath>
#include <triumf/bnmr/nuclei.hpp>
#include <triumf/bnmr/slr/bi_exp.hpp>
#include <triumf/bnmr/slr/cbrt_exp.hpp>
#include <triumf/bnmr/slr/exp.hpp>
#include <triumf/bnmr/slr/gauss_dist_exp.hpp>
#include <triumf/bnmr/slr/magnesium_31/exp.hpp>
#include <triumf/bnmr/slr/mod_str_exp.hpp>
#include <triumf/bnmr/slr/sq_exp.hpp>
#include <triumf/bnmr/slr/sqrt_exp.hpp>
#include <triumf/bnmr/slr/str_exp.hpp>
#include <triumf/numpy.hpp>
#include <tuple>

typedef std::tuple<float, double, long double> test_types;

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  BOOST_TEST(triumf::bnmr::slr::pulsed_exp<T>(-1.0, nuclear_lifetime,
                                              pulse_length, initial_asymmetry,
                                              slr_rate) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::pulsed_exp<T>(0.0, nuclear_lifetime,
                                              pulse_length, initial_asymmetry,
                                              slr_rate) == initial_asymmetry);
  //
  for (auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_exp<T>(time, nuclear_lifetime,
                                                pulse_length, initial_asymmetry,
                                                slr_rate) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_exp_31mg, T, test_types) {
  //
  constexpr T pulse_length = 1.0;
  constexpr T nuclear_lifetime =
      triumf::bnmr::nuclei::magnesium_31<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  BOOST_TEST(triumf::bnmr::slr::magnesium_31::pulsed_exp<T>(
                 -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::magnesium_31::pulsed_exp<T>(
                 0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == initial_asymmetry);
  //
  for (auto &time : triumf::numpy::linspace<T>(0.0, 4.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::magnesium_31::pulsed_exp<T>(
                   time, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_sq_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  BOOST_TEST(
      triumf::bnmr::slr::pulsed_sq_exp<T>(-1.0, nuclear_lifetime, pulse_length,
                                          initial_asymmetry, slr_rate) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::pulsed_sq_exp<T>(
                 0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == initial_asymmetry);
  //
  for (auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_sq_exp<T>(
                   time, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_sqrt_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  BOOST_TEST(triumf::bnmr::slr::pulsed_sqrt_exp<T>(
                 -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::pulsed_sqrt_exp<T>(
                 0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == initial_asymmetry);
  //
  for (auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_sqrt_exp<T>(
                   time, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_cbrt_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  BOOST_TEST(triumf::bnmr::slr::pulsed_cbrt_exp<T>(
                 -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::pulsed_cbrt_exp<T>(
                 0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 slr_rate) == initial_asymmetry);
  //
  for (auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_cbrt_exp<T>(
                   time, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_str_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  for (const auto &beta : triumf::numpy::linspace<T>(0.1, 1.0, 10)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_str_exp<T>(
                   -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate, beta) == 0.0);
    BOOST_TEST(triumf::bnmr::slr::pulsed_str_exp<T>(
                   0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate, beta) == initial_asymmetry);
    //
    for (const auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
      BOOST_TEST(triumf::bnmr::slr::pulsed_str_exp<T>(
                     time, nuclear_lifetime, pulse_length, initial_asymmetry,
                     slr_rate, beta) <= initial_asymmetry);
    }
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_mod_str_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  for (const auto &beta : triumf::numpy::linspace<T>(0.1, 1.0, 10)) {
    for (const auto &slr_rate_initial :
         triumf::numpy::logspace<T>(-2.0, 2.0, 10)) {
      BOOST_TEST(triumf::bnmr::slr::pulsed_mod_str_exp<T>(
                     -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                     slr_rate_initial, slr_rate, beta) == 0.0);
      BOOST_TEST(triumf::bnmr::slr::pulsed_mod_str_exp<T>(
                     0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                     slr_rate_initial, slr_rate, beta) == initial_asymmetry);
      //
      for (const auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
        BOOST_TEST(triumf::bnmr::slr::pulsed_mod_str_exp<T>(
                       time, nuclear_lifetime, pulse_length, initial_asymmetry,
                       slr_rate_initial, slr_rate, beta) <= initial_asymmetry);
      }
    }
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_bi_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T fraction_slow = 0.75;
  constexpr T slr_rate_slow = 0.10;
  constexpr T slr_rate_fast = 10.0;
  //
  BOOST_TEST(triumf::bnmr::slr::pulsed_bi_exp<T>(
                 -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 fraction_slow, slr_rate_slow, slr_rate_fast) == 0.0);
  BOOST_TEST(triumf::bnmr::slr::pulsed_bi_exp<T>(
                 0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                 fraction_slow, slr_rate_slow,
                 slr_rate_fast) == initial_asymmetry);
  //
  for (const auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_bi_exp<T>(
                   time, nuclear_lifetime, pulse_length, initial_asymmetry,
                   fraction_slow, slr_rate_slow,
                   slr_rate_fast) <= initial_asymmetry);
  }
}

//
BOOST_AUTO_TEST_CASE_TEMPLATE(pulsed_gauss_dist_exp, T, test_types) {
  //
  constexpr T pulse_length = 4.0;
  constexpr T nuclear_lifetime = triumf::bnmr::nuclei::lithium_8<T>::lifetime();
  constexpr T initial_asymmetry = 1.0;
  constexpr T slr_rate = 1.0;
  //
  for (const auto &sigma : triumf::numpy::logspace<T>(-4.0, -1.0, 10)) {
    BOOST_TEST(triumf::bnmr::slr::pulsed_gauss_dist_exp<T>(
                   -1.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate, sigma) == 0.0);
    BOOST_TEST(triumf::bnmr::slr::pulsed_gauss_dist_exp<T>(
                   0.0, nuclear_lifetime, pulse_length, initial_asymmetry,
                   slr_rate, sigma) == initial_asymmetry);
    //
    for (const auto &time : triumf::numpy::linspace<T>(0.0, 16.0, 100)) {
      BOOST_TEST(triumf::bnmr::slr::pulsed_gauss_dist_exp<T>(
                     time, nuclear_lifetime, pulse_length, initial_asymmetry,
                     slr_rate, sigma) <= initial_asymmetry);
    }
  }
}


#include <algorithm>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <climits>
#include <numeric>
#include <Eigen/Dense>
#undef NDEBUG
#include <cassert>

#include "io_redirect.h"

#define FOR(i, a, b) for(std::size_t (i) = (a); (i) < (b); ++(i))
#define REP(i, n) FOR(i, 0, n)
#define TRACE(x) std::cout << #x <<" = " <<(x) <<std::endl;

using namespace std;

using UInt = uint64_t;
using Int = int64_t;

static const std::vector<std::string> NUMBERS = { "ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"};

int main(int argc, char* argv[])
{
  //////////////////////// INPUT/OUTPUT //////////////////////////
  if (!redirect_io(argc, argv))
    return 0;
  ////////////////////////////////////////////////////////////////

  std::set<char> a;
  for (const auto& n : NUMBERS)
    for (const char c : n)
      a.insert(c);

  Eigen::MatrixXd m(a.size(), NUMBERS.size());
  m.setZero();

  REP(i, NUMBERS.size())
  {
    REP(j, NUMBERS[i].size())
    {
      const auto d = std::distance(a.cbegin(), a.find(NUMBERS[i][j]));
      m(d, i) += 1;
    }
  }

  size_t num_test_cases;
  cin >> num_test_cases;

  for (size_t i = 0; i < num_test_cases; ++i)
  {
    std::string vs;
    cin >> vs;

    Eigen::VectorXd b(a.size());
    b.setZero();
    REP(c, vs.size())
    {
      const auto d = std::distance(a.cbegin(), a.find(vs[c]));
      b(d) += 1;
    }
    const Eigen::VectorXd x = m.fullPivHouseholderQr().solve(b);

    string o{};
    REP(c, NUMBERS.size())
    {
      const size_t count = static_cast<size_t>(std::round(x(c)));
      REP(d, count)
      {
        o += std::to_string(c);
      }
    }
    std::cout <<"Case #" <<(i+1) <<": " <<o <<endl;
  }

  ////////////////////////////////////////////////////////////////
  cleanup_io();
  return 0;
}


/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory for Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include <Utils/Math/AutomaticDifferentiation/VectorDerivatives3D.h>
#include <gmock/gmock.h>
#include <Eigen/Geometry>

using namespace testing;
namespace Scine {
namespace Utils {
namespace Tests {

/**
 * @class AVectorDerivatives3DTest VectorDerivatives3DTest.cpp
 * @brief Comprises tests for the class Scine::Utils::VectorDerivatives3D.
 * @test
 */
class AVectorDerivatives3DTest : public Test {
 public:
  double x = 0.231;
  double y = -0.2922;
  double z = 0.787;
  Eigen::Vector3d v;

 protected:
  void SetUp() override {
    v = Eigen::Vector3d(x, y, z);
  }
};

TEST_F(AVectorDerivatives3DTest, IsCorrectForDotProductWithItself) {
  auto dv = VectorDerivatives3D::spatialVectorHessian3D(v);

  AutomaticDifferentiation::Second3D dotProduct = dv.dot(dv);

  ASSERT_THAT(dotProduct.value(), DoubleEq(x * x + y * y + z * z));
  ASSERT_THAT(dotProduct.dx(), DoubleEq(2 * x));
  ASSERT_THAT(dotProduct.dy(), DoubleEq(2 * y));
  ASSERT_THAT(dotProduct.dz(), DoubleEq(2 * z));
  ASSERT_THAT(dotProduct.XX(), DoubleEq(2));
  ASSERT_THAT(dotProduct.YY(), DoubleEq(2));
  ASSERT_THAT(dotProduct.ZZ(), DoubleEq(2));
  ASSERT_THAT(dotProduct.XY(), DoubleEq(0));
  ASSERT_THAT(dotProduct.XZ(), DoubleEq(0));
  ASSERT_THAT(dotProduct.YZ(), DoubleEq(0));
}

TEST_F(AVectorDerivatives3DTest, IsCorrectForDotProductWithVector3d) {
  auto dv = VectorDerivatives3D::spatialVectorHessian3D(v);
  double x2 = -3.2, y2 = 0.21, z2 = 5.121;
  Eigen::Vector3d v2(x2, y2, z2);

  AutomaticDifferentiation::Second3D dotProduct = dv.dot(v2);

  ASSERT_THAT(dotProduct.value(), DoubleEq(x * x2 + y * y2 + z * z2));
  ASSERT_THAT(dotProduct.dx(), DoubleEq(x2));
  ASSERT_THAT(dotProduct.dy(), DoubleEq(y2));
  ASSERT_THAT(dotProduct.dz(), DoubleEq(z2));
  ASSERT_THAT(dotProduct.XX(), DoubleEq(0));
  ASSERT_THAT(dotProduct.YY(), DoubleEq(0));
  ASSERT_THAT(dotProduct.ZZ(), DoubleEq(0));
  ASSERT_THAT(dotProduct.XY(), DoubleEq(0));
  ASSERT_THAT(dotProduct.XZ(), DoubleEq(0));
  ASSERT_THAT(dotProduct.YZ(), DoubleEq(0));
}

TEST_F(AVectorDerivatives3DTest, IsCorrectForCrossProduct) {
  double x2 = -3.2, y2 = 0.21, z2 = 5.121;
  Eigen::Vector3d v2(x2, y2, z2);
  double factor = 3.121;

  auto dv = VectorDerivatives3D::spatialVectorHessian3D(v);
  auto dv2 = VectorDerivatives3D::spatialVectorHessian3D(v2) * factor;

  auto crossProduct = dv.cross(dv2);
  Eigen::Vector3d expectedValue = v.cross(v2 * factor);

  // Check values (compare with Eigen::cross):
  ASSERT_THAT(crossProduct.x().value(), DoubleEq(expectedValue.x()));
  ASSERT_THAT(crossProduct.y().value(), DoubleEq(expectedValue.y()));
  ASSERT_THAT(crossProduct.z().value(), DoubleEq(expectedValue.z()));

  // Check only x component derivatives
  auto xDer = crossProduct.x();
  ASSERT_THAT(xDer.dx(), DoubleEq(0));
  ASSERT_THAT(xDer.dy(), DoubleEq(factor * z2 - factor * z));
  ASSERT_THAT(xDer.dz(), DoubleEq(factor * y - factor * y2));
  ASSERT_THAT(xDer.XX(), DoubleEq(0));
  ASSERT_THAT(xDer.YY(), DoubleEq(0));
  ASSERT_THAT(xDer.ZZ(), DoubleEq(0));
  ASSERT_THAT(xDer.XY(), DoubleEq(0));
  ASSERT_THAT(xDer.XZ(), DoubleEq(0));
  ASSERT_THAT(xDer.YZ(), DoubleEq(0));
}

TEST_F(AVectorDerivatives3DTest, IsCorrectForNorm) {
  AutomaticDifferentiation::Second3D X(5 * x, 5, 0, 0);
  AutomaticDifferentiation::Second3D Y(2 * y, 0, 2, 0);
  AutomaticDifferentiation::Second3D Z(-z, 0, 0, -1);

  VectorDerivatives3D dv(X, Y, Z);

  auto norm = dv.norm();
  double root = std::sqrt(25 * x * x + 4 * y * y + z * z);

  ASSERT_THAT(norm.value(), DoubleEq(root));
  ASSERT_THAT(norm.dx(), DoubleEq(5 * (5 * x) / root));
  ASSERT_THAT(norm.dy(), DoubleEq(2 * (2 * y) / root));
  ASSERT_THAT(norm.dz(), DoubleEq((-1) * (-z) / root));
}

} /* namespace Tests */
} /* namespace Utils */
} /* namespace Scine */


/*
 * gr_checker.cpp
 *
 *  Created on: 04.01.2015
 *      Author: schlund
 */

#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>

#include <boost/program_options.hpp>

#include "datastructs/matrix.h"
#include "datastructs/equations.h"

#include "polynomials/commutative_polynomial.h"
#include "polynomials/non_commutative_polynomial.h"
#include "polynomials/lossy_non_commutative_polynomial.h"

#include "semirings/pseudo_linear_set.h"
#include "semirings/semilinear_set.h"

#include "semirings/semilinSetNdd.h"
#include "semirings/semilinSetNdd.h"

#include "parser.h"

#include "solvers/newton_generic.h"
#include "solvers/solver_utils.h"

#include "utils/string_util.h"
#include "utils/timer.h"

// check whether a set of grammars generates the same language up to commuativity
// (and modulo additional overapproximations given by the semiring)
template <typename SR>
void check_all_equal_commutative(const std::string& startsymbol, const std::vector<std::string>& inputs) {

  Parser p;
  int num_grammars = inputs.size();

  auto nc_equations = p.free_parser(inputs[0]);

  std::cout << "Eq (non-comm) : " << std::endl;
  PrintEquations(nc_equations);

  // Use appropriate semiring (has to be commutative!)
  auto equations_fst = MakeCommEquationsAndMap(nc_equations, [](const FreeSemiring &c) -> SR {
    auto srconv = SRConverter<SR>();
    return c.Eval(srconv);
  });

  std::cout << "Eq (comm) : "  << std::endl;
  PrintEquations(equations_fst);

  Timer timer;
  timer.Start();

  ValuationMap<SR> sol_fst = apply_solver<NewtonCL, CommutativePolynomial>(equations_fst, true, false, 0, false);

  bool all_equal = true;
  for(int i=1; i<num_grammars; i++) {
    auto equations = MakeCommEquationsAndMap(p.free_parser(inputs[i]), [](const FreeSemiring &c) -> SR {
      auto srconv = SRConverter<SR>();
      return c.Eval(srconv);
    });

    ValuationMap<SR> sol = apply_solver<NewtonCL, CommutativePolynomial>(equations, true, false, 0, false);

    if(startsymbol.compare("") == 0) {
      if(sol[equations[0].first] != sol_fst[equations_fst[0].first]) {
        std::cout << "[DIFF] Difference found for startsymbols (" << equations_fst[0].first << "," << equations[0].first << ")" << std::endl;
        std::cout << "0:" << result_string(sol_fst) << std::endl << i << ":" << result_string(sol) << std::endl;
        all_equal = false;
        break;
      }
    }
    else {

      if(sol.find(Var::GetVarId(startsymbol)) == sol.end() || sol_fst.find(Var::GetVarId(startsymbol)) == sol_fst.end()) {
        std::cout << "[ERROR] startsymbol (" << startsymbol << ") does not occur!"<< std::endl;
        return;
      }
      else if(sol[Var::GetVarId(startsymbol)] != sol_fst[Var::GetVarId(startsymbol)]) {
        std::cout << "[DIFF] Difference found for startsymbol (" << startsymbol << ")" << std::endl << "0:" << result_string(sol_fst)
                                               << std::endl << i << ":" << result_string(sol) << std::endl;
        all_equal = false;
        break;
      }
    }

  }

  if(all_equal) {
    std::cout << "[EQUIV] All grammars equivalent modulo commutativity" << std::endl;
  }

  timer.Stop();
  std::cout
  << "Total checking time:\t" << timer.GetMilliseconds().count()
  << " ms" << " ("
  << timer.GetMicroseconds().count()
  << "us)" << std::endl;

}

void check_all_equal_lossy(const std::string& startsymbol, const std::vector<std::string>& inputs, int refinementDepth) {
  int num_grammars = inputs.size();
  Parser p;
  auto eq_tmp = MapEquations(p.free_parser(inputs[0]), [](const FreeSemiring &c) -> LossyFiniteAutomaton {
    auto srconv = SRConverter<LossyFiniteAutomaton>();
    return c.Eval(srconv);
  });

  auto equations_fst = NCEquationsBase<LossyFiniteAutomaton>(eq_tmp.begin(), eq_tmp.end());

  VarId S_1;
  if(startsymbol.compare("") == 0) {
    S_1 = equations_fst[0].first;
  } else {
    S_1 = Var::GetVarId(startsymbol);
  }

  Timer timer;
  timer.Start();

  bool all_equal = true;
  for(int i=1; i<num_grammars; i++) {

    auto eq_tmp2 = MapEquations(p.free_parser(inputs[i]), [](const FreeSemiring &c) -> LossyFiniteAutomaton {
      auto srconv = SRConverter<LossyFiniteAutomaton>();
      return c.Eval(srconv);
    });
    auto equations = NCEquationsBase<LossyFiniteAutomaton>(eq_tmp2.begin(), eq_tmp2.end());

    VarId S_2;
    if(startsymbol.compare("") == 0) {
      S_2 = equations[0].first;
    } else {
      S_2 = Var::GetVarId(startsymbol);
    }

    auto witness = NonCommutativePolynomial<LossyFiniteAutomaton>::refineCourcelle(equations_fst, S_1, equations, S_1, refinementDepth);

    if(witness != LossyFiniteAutomaton::null()) {
      if(startsymbol.compare("") == 0) {
        std::cout << "[DIFF] Difference found for startsymbols (" << equations_fst[0].first << "," << equations[0].first << ")" << std::endl;
      }
      else {
        std::cout << "[DIFF] Difference found for startsymbols (" << S_1 << "," << S_2 << ")" << std::endl;
      }
        std::cout << "Witness: " << witness.string() << std::endl;
        all_equal = false;
        break;
    }
  }

  if(all_equal) {
    std::cout << "[EQUIV] All grammars equivalent modulo subword-closure" << std::endl;
  }

  timer.Stop();
  std::cout
  << "Total checking time:\t" << timer.GetMilliseconds().count()
  << " ms" << " ("
  << timer.GetMicroseconds().count()
  << "us)" << std::endl;

}

/*
 * Tests whether two grammars generate the same language modulo commutativity.
 * We use semilinear sets in constant-period representation to represent Parikh images
 * and check their equivalence via NDDs.
 */
int main(int argc, char* argv[]) {
  namespace po = boost::program_options;

  po::options_description generic("Generic options");
  generic.add_options()
        ( "help,h", "print this help message" )
        ( "startsymbol,s", po::value<std::string>(), "start symbol of the grammars")
        ( "input", po::value<std::vector<std::string> >(), "input grammars (at least two): g1 g2 [g3] [...]" )
        ( "slset", "commutative abstraction via semilinear sets" )
        ( "lossy", "abstraction via subword closure" )
        ("refD", po::value<int>(), "refinement Depth for Lossy Approximation")
        ;

  po::positional_options_description pos;
  pos.add("input", -1);

  po::variables_map vm;
  po::store(po::command_line_parser(argc, argv).options(generic).positional(pos).run(), vm);
  po::notify(vm);

  SemilinSetNdd::genepi_init();

  if(vm.count("help")) {
    std::cout << generic << std::endl;
    return EXIT_SUCCESS;
  }

  std::vector<std::string> input_files = vm["input"].as< std::vector<std::string> >();
  int num_grammars = input_files.size();
  std::vector<std::string> inputs;

  std::string startsymbol = "";

  if(vm.count("startsymbol")) {
    startsymbol = vm["startsymbol"].as<std::string>();
    std::cout << "Comparing startsymbols (" << startsymbol << ")" << std::endl;
  }
  else {
    std::cout << "No startsymbol specified, using defaults." << std::endl;
  }

  if (vm.count("input") && num_grammars > 1) {
    // we are reading the input from the given files
    std::ifstream file;

    for(auto& filename : input_files) {
      file.open(filename, std::ifstream::in);
      if (file.fail()) {
        std::cerr << "Could not open input file: " << filename << std::endl;
      }
      std::string line;
      std::vector<std::string> input;
      while (std::getline(file, line)) {
        input.push_back(line);
      }
      // join the input into one string
      inputs.push_back( std::accumulate(input.begin(), input.end(), std::string("")) );
      file.close();
    }
  } else {
    std::cout << "Please provide at least two input files!" << std::endl;
    return EXIT_FAILURE;
  }

  if(!vm.count("slset") && !vm.count("lossy")) {
    std::cout << "Please specify the abstraction used for checking equivalence!" << std::endl;
    return EXIT_FAILURE;
  }

  if(vm.count("slset")) {
    std::cout << "Plain Semilinear Sets" << std::endl;
    // no overapproximation -- just plain semilinear sets with simplification
    // (the approximations are not sound for inequivalence-testing!)
    check_all_equal_commutative<SemilinearSetL>(startsymbol, inputs);
  }

  if(vm.count("lossy")) {
    std::cout << "Lossy Approximation" << std::endl;

    int refinementDepth = 0;
    if(vm.count("refD")) {
        refinementDepth = vm["refine"].as<int>();
    }

    check_all_equal_lossy(startsymbol, inputs, refinementDepth);
  }

  SemilinSetNdd::genepi_dealloc();

  return EXIT_SUCCESS;
}


#include <iostream>
#include <math/TransformTool.hpp>
#include <math/common.hpp>
#include <boost/algorithm/string.hpp>

using namespace Eigen;
using namespace std;

typedef Eigen::Matrix<double, 7, 1> Vector7d;

int main(int argc, const char** argv)
{
    if(argc != 8) return -1;

    Vector7d tq;
    for(int i=1; i<argc; ++i)
        tq[i-1] = atof(argv[i]);

    TransformTool tt;
    cout << tt.tq2matrix(tq) << endl;
    return 0;
}


#ifndef CRADLE_GEOMETRY_DECODE_MATRIX_HPP
#define CRADLE_GEOMETRY_DECODE_MATRIX_HPP

#include <boost/optional/optional.hpp>
#include <cradle/geometry/angle.hpp>
#include <cradle/geometry/common.hpp>

// This file provides various functions for decoding transformation matrices.

namespace cradle {

// Determine if the given transformation matrix has a rotational component.
template<unsigned N, typename T>
bool
has_rotation(matrix<N, N, T> const& m);

// Given a matrix that represents a 3D rotation about the X-axis (and only
// that), this returns the angle of the rotation.  For any other matrix,
// it returns an uninitialized value.
template<typename T>
optional<angle<T, radians>>
decode_rotation_about_x(matrix<3, 3, T> const& m);

// Decode a rotation about the Y-axis.
template<typename T>
optional<angle<T, radians>>
decode_rotation_about_y(matrix<3, 3, T> const& m);

// Decode a rotation about the Z-axis.
template<typename T>
optional<angle<T, radians>>
decode_rotation_about_z(matrix<3, 3, T> const& m);

} // namespace cradle

#include <cradle/geometry/decode_matrix.ipp>

#endif


/**
 * @file test_irreducible_ntl.hpp
 *
 * @test program for f2p_is_irreducible.
 *
 * @author Mutsuo Saito
 * @author Makoto Matsumoto (Hiroshima University)
 *
 * Copyright (C) 2019 Mutsuo Saito, Makoto Matsumoto
 * and Hiroshima University.
 * All rights reserved.
 *
 * The MIT License is applied to this software, see
 * LICENSE.txt
 */

#include <NTL/GF2X.h>
#include <NTL/GF2XFactoring.h>
#include "test_ntl.hpp"
#include "f2p_gmp.h"
#include <string>
#include <stdio.h>
#include <inttypes.h>

using namespace NTL;
using namespace std;

int test_irreducible(const string& hexstr)
{
    GF2X poly;
    hexto_poly(poly, hexstr);
    bool irre_ntl = IterIrredTest(poly);
    mpz_t gmppoly;
    mpz_init(gmppoly);
    f2p_set_hexstr(gmppoly, hexstr.c_str());
    bool irre_f2p = f2p_is_irreducible(gmppoly);
    int ok = 1;
    if (irre_ntl == irre_f2p) {
        ok = 1;
    } else {
        printf("irrentl = %d, irre_f2p = %d\n", irre_ntl, irre_f2p);
        ok = 0;
    }
    mpz_clear(gmppoly);
    return ok;
}

int test_irreducible(int n, const bool verbose)
{
    GF2X poly;
    GF2X ranpoly;
    int ok = 1;
    string str;
    char okstr[2] = {'x','o'};
    BuildIrred(poly, n);
    for (int i = 0; i < 100; i++) {
        BuildRandomIrred(ranpoly, poly);
        to_hexstring(str, ranpoly);
        int r = test_irreducible(str);
        if (verbose) {
            printf("%c", okstr[r & 1]);
        }
        ok &= r;
    }
    for (int i = 0; i < 100; i++) {
        random(ranpoly, n);
        to_hexstring(str, ranpoly);
        int r = test_irreducible(str);
        if (verbose) {
            printf("%c", okstr[r & 1]);
        }
        ok &= r;
    }
    if (verbose) {
        printf("\n");
    }
    if (ok) {
        printf("irreducible OK\n");
        return 0;
    } else {
        printf("irreducible NG\n");
        return 1;
    }
}

int main(int argc, char * argv[])
{
    bool verbose = false;
    int r = 0;
    int n = 200;
    if (argc > 1 && argv[1][0] == 'v') {
        verbose = true;
    }
    r += test_irreducible(n, verbose);
    if (r == 0) {
        return 0;
    } else {
        return -1;
    }
}


//==================================================================================================
//  GlobalTypes.hpp
//
//  Created by Zachary Clawson on 7/19/2015
//  Copyright (c) 2015 Zachary Clawson. All rights reserved.
//==================================================================================================

#ifndef __GlobalTypes_hpp
#define __GlobalTypes_hpp

#include <Eigen/Dense>

// Double:
using real_t = double;
//using real_t = float;

// Double vector:
//namespace Eigen{

using Vector2real_t   = Eigen::Matrix<real_t, 2, 1>;
using ArrayXreal_t    = Eigen::Array<real_t, Eigen::Dynamic, Eigen::Dynamic>;
using MatrixXreal_t   = Eigen::Matrix<real_t, Eigen::Dynamic, Eigen::Dynamic>;

//}

#endif /* GLOBALTYPES_HPP */


#ifndef OPTIMIZERS_HPP
#define OPTIMIZERS_HPP

// Eigen includes --------------------
#include <Eigen/Dense>

// Own includes --------------------
#include "optimizers/base-optimizer.hpp"

namespace NNet { // begin NNet

	/**
	 *SGDOptimizer.
	 */
	template< typename NetworkType >
	class SGDOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		SGDOptimizer( ) = delete;
		explicit SGDOptimizer( NetworkType& network, NumericType learningRate = 0.001 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ) {
		}
		SGDOptimizer( SGDOptimizer const& other ) = delete;
		~SGDOptimizer( ) = default;

		//get/set member functions
		NumericType getLearningRate( ) { return mLearningRate; }

		// interface
		void applyWeightUpdate( std::size_t batchSize ) override {
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				auto coeff = 1.0 / static_cast< NumericType >( batchSize );
				// std::cout << "LearningRate, coeff: " << mLearningRate << ", " << coeff << std::endl;
				// std::cout << "WeightMat Before: " << weightMat.rows( ) << ", " << weightMat.cols( ) << std::endl
				// 		  << weightMat << std::endl;
				// std::cout << "WeightGradMat Before: " << std::endl
				// 		  << weightGradMat << std::endl;
				weightMat = weightMat - mLearningRate * coeff * weightGradMat;
			}
		}
	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate;

	}; // end of class SGDOptimizer

    /**
	 *MomentumOptimizer.
	 */
	template< typename NetworkType >
	class MomentumOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		MomentumOptimizer( ) = delete;
		explicit MomentumOptimizer( NetworkType& network, NumericType learningRate = 0.001, NumericType momentum = 0.9 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ), mMomentum( momentum ) {
			for ( auto const& layer : this -> getTrainableLayers( ) ) {
				MatrixXType const& weightGradMat = layer -> getWeightGradMat( );
				auto numRows = weightGradMat.rows( );
				auto numCols = weightGradMat.cols( );
				MatrixXType mat( numRows, numCols );
				mat.setZero( );
				mWeightGradMatSaves.emplace_back( mat );
			}
		}
		MomentumOptimizer( MomentumOptimizer const& other ) = delete;
		~MomentumOptimizer( ) = default;

		//get/set member functions
		NumericType getLearningRate( ) const { return mLearningRate; }
		void setLearningRate( NumericType learningRate ) { mLearningRate = learningRate; }
		NumericType getMomentum( ) const { return mMomentum; }
		void setMomentum( NumericType momentum ) { mMomentum = momentum; }

		// interface
		void applyWeightUpdate( std::size_t batchSize ) override {
			auto v_iter = mWeightGradMatSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				NumericType coeff = 1.0 / static_cast< NumericType >( batchSize );
				// compute the velocity v_{t+1} at time t+1
				// note that v_iter at t = 0 is zero
				MatrixXType v = mMomentum * (*v_iter) - mLearningRate * coeff * weightGradMat;
				*v_iter = v;
				weightMat = weightMat + v;
				++v_iter;
			}
		}
	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate, mMomentum;
		std::vector< MatrixXType > mWeightGradMatSaves = { };
	}; // end of class MomentumOptimizer

	/**
	 *NesterovMomentumOptimizer.
	 */
	template< typename NetworkType >
	class NesterovMomentumOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		NesterovMomentumOptimizer( ) = delete;
		explicit NesterovMomentumOptimizer( NetworkType& network, NumericType learningRate = 0.001, NumericType momentum = 0.9 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ), mMomentum( momentum ) {
			for ( auto const& layer : this -> getTrainableLayers( ) ) {
				MatrixXType const& weightGradMat = layer -> getWeightGradMat( );
				auto numRows = weightGradMat.rows( );
				auto numCols = weightGradMat.cols( );
				MatrixXType mat( numRows, numCols );
				mat.setZero( );
				mWeightGradMatSaves.emplace_back( mat );
			}
		}
		NesterovMomentumOptimizer( NesterovMomentumOptimizer const& other ) = delete;
		~NesterovMomentumOptimizer( ) = default;

		//get/set member functions
		NumericType getLearningRate( ) { return mLearningRate; }
		void setLearningRate( NumericType learningRate ) { mLearningRate = learningRate; }
		NumericType getMomentum( ) const { return mMomentum; }
		void setMomentum( NumericType momentum ) { mMomentum = momentum; }

		// interface
		void applyInterimUpdate( ) override {
			auto v_iter = mWeightGradMatSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				weightMat = weightMat + mMomentum * ( *v_iter );
				++v_iter;
			}
		}
		void applyWeightUpdate( std::size_t batchSize ) override {
			auto v_iter = mWeightGradMatSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				NumericType coeff = 1.0 / static_cast< NumericType >( batchSize );
				MatrixXType v = mMomentum * (*v_iter) - mLearningRate * coeff * weightGradMat;
				*v_iter = v;
				weightMat = weightMat - mLearningRate * coeff * weightGradMat;
				++v_iter;
			}
		}
	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate, mMomentum;
		std::vector< MatrixXType > mWeightGradMatSaves = { };
	}; // end of class NesterovMomentumOptimizer

	/**
	 *AdaGradOptimizer.
	 */
	template< typename NetworkType >
	class AdaGradOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		AdaGradOptimizer() = delete;
		explicit AdaGradOptimizer( NetworkType& network, NumericType learningRate = 0.01 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ) {
			for ( auto const& layer : this -> getTrainableLayers( ) ) {
				MatrixXType const& weightGradMat = layer -> getWeightGradMat( );
				auto numRows = weightGradMat.rows( );
				auto numCols = weightGradMat.cols( );
				MatrixXType mat( numRows, numCols );
				mat.setZero( );
				mGradMatAccumSaves.emplace_back( mat );
			}
		}
		AdaGradOptimizer(const AdaGradOptimizer &c) = delete;
		~AdaGradOptimizer() = default;

		//get/set member functions
		NumericType getLearningRate( ) const { return mLearningRate; }
		void setLearningRate( NumericType learningRate ) { mLearningRate = learningRate; }

		// interface
		void applyWeightUpdate( std::size_t batchSize ) override {
			auto r_iter = mGradMatAccumSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				NumericType coeff = 1.0 / static_cast< NumericType >( batchSize );
				// accumulate squared gradient
				MatrixXType grad_sq = coeff * coeff * weightGradMat.cwiseProduct( weightGradMat );
				*r_iter = (*r_iter) + grad_sq;
				MatrixXType r = *r_iter;
				r = r.unaryExpr( [this]( auto const& ele ) {
					return ( mLearningRate / ( 1.0e-7 + std::sqrt( ele ) ) );
				} );
				weightMat = weightMat - r.cwiseProduct( coeff * weightGradMat );
				++r_iter;
			}
		}

	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate;
		std::vector< MatrixXType > mGradMatAccumSaves = { };
	}; // end of class AdaGradOptimizer

	/**
	 *RMSPropOptimizer.
	 */
	template< typename NetworkType >
	class RMSPropOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		RMSPropOptimizer() = delete;
		explicit RMSPropOptimizer( NetworkType& network, NumericType learningRate = 0.001, NumericType decayRate = 0.9 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ), mDecayRate( decayRate ) {
			for ( auto const& layer : this -> getTrainableLayers( ) ) {
				MatrixXType const& weightGradMat = layer -> getWeightGradMat( );
				auto numRows = weightGradMat.rows( );
				auto numCols = weightGradMat.cols( );
				MatrixXType mat( numRows, numCols );
				mat.setZero( );
				mGradMatAccumSaves.emplace_back( mat );
			}
		}
		RMSPropOptimizer( RMSPropOptimizer const& other ) = delete;
		~RMSPropOptimizer() = default;

		//get/set member functions
		NumericType getLearningRate( ) const { return mLearningRate; }
		void setLearningRate( NumericType learningRate ) { mLearningRate = learningRate; }
		NumericType getDecayRate( ) const { return mDecayRate; }
		void setDecayRate( NumericType decayRate ) { mDecayRate = decayRate; }

		// interface
		void applyWeightUpdate( std::size_t batchSize ) override {
			auto r_iter = mGradMatAccumSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				NumericType coeff = 1.0 / static_cast< NumericType >( batchSize );
				// accumulate squared gradient
				MatrixXType grad_sq = coeff * coeff * weightGradMat.cwiseProduct( weightGradMat );
				*r_iter = mDecayRate * (*r_iter) + (1.0 - mDecayRate ) * grad_sq;
				MatrixXType r = *r_iter;
				r = r.unaryExpr( [this]( auto const& ele ) {
					return ( mLearningRate / ( 1.0e-6 + std::sqrt( ele ) ) );
				} );
				weightMat = weightMat - r.cwiseProduct( coeff * weightGradMat );
				++r_iter;
			}
		}

	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate, mDecayRate;
		std::vector< MatrixXType > mGradMatAccumSaves = { };
	}; // end of class RMSPropOptimizer

	/**
	 *RMSPropNestMomOptimizer.
	 */
	template< typename NetworkType >
	class RMSPropNestMomOptimizer
		: public BaseOptimizer< NetworkType > {
	public: 	// public typedefs
		using NumericType = typename NetworkType::NumericType;
		using VectorXType = typename NetworkType::VectorXType;
		using MatrixXType = typename NetworkType::MatrixXType;

	private: 	// private typedefs

	public: 	//public member functions
		RMSPropNestMomOptimizer() = delete;
		explicit RMSPropNestMomOptimizer( NetworkType& network, NumericType learningRate = 0.001, NumericType momentum = 0.9, NumericType decayRate = 0.9 )
			: BaseOptimizer< NetworkType >( network ), mLearningRate( learningRate ), mMomentum( momentum ), mDecayRate( decayRate ) {
			for ( auto const& layer : this -> getTrainableLayers( ) ) {
				MatrixXType const& weightGradMat = layer -> getWeightGradMat( );
				auto numRows = weightGradMat.rows( );
				auto numCols = weightGradMat.cols( );
				MatrixXType mat( numRows, numCols );
				mat.setZero( );
				mWeightGradMatSaves.emplace_back( mat );
				mGradMatAccumSaves.emplace_back( mat );
			}
		}
		RMSPropNestMomOptimizer( RMSPropNestMomOptimizer const& other ) = delete;
		~RMSPropNestMomOptimizer() = default;

		//get/set member functions
		NumericType getLearningRate( ) const { return mLearningRate; }
		void setLearningRate( NumericType learningRate ) { mLearningRate = learningRate; }
		NumericType getMomentum( ) const { return mMomentum; }
		void setMomentum( NumericType momentum ) { mMomentum = momentum; }
		NumericType getDecayRate( ) const { return mDecayRate; }
		void setDecayRate( NumericType decayRate ) { mDecayRate = decayRate; }

		// interface
		void applyInterimUpdate( ) override {
			auto v_iter = mWeightGradMatSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				weightMat = weightMat + mMomentum * ( *v_iter );
				++v_iter;
			}
		}
		void applyWeightUpdate( std::size_t batchSize ) override {
			auto v_iter = mWeightGradMatSaves.begin( );
			auto r_iter = mGradMatAccumSaves.begin( );
			for ( auto& layerPtr : this -> getTrainableLayers( ) ) {
				MatrixXType& weightMat = layerPtr -> getWeightMat( );
				MatrixXType const& weightGradMat = layerPtr -> getWeightGradMat( );
				NumericType coeff = 1.0 / static_cast< NumericType >( batchSize );
				// accumulate squared gradient
				MatrixXType grad_sq = coeff * coeff * weightGradMat.cwiseProduct( weightGradMat );
				*r_iter = mDecayRate * (*r_iter) + (1.0 - mDecayRate ) * grad_sq;
				MatrixXType r = *r_iter;
				r = r.unaryExpr( [this]( auto const& ele ) {
					return ( mLearningRate / ( 1.0e-7 + std::sqrt( ele ) ) );
				} );
				MatrixXType v = mMomentum * ( *v_iter ) - r.cwiseProduct( coeff * weightGradMat );
				*v_iter = v;
				weightMat = weightMat - r.cwiseProduct( coeff * weightGradMat );
				++v_iter;
				++r_iter;
			}
		}

	private: 	//private member functions

	public: 	//public data members

	private: 	//private data members
		NumericType mLearningRate, mMomentum, mDecayRate;
		std::vector< MatrixXType > mWeightGradMatSaves = { }, mGradMatAccumSaves = { };
	}; // end of class RMSPropNestMomOptimizer

} // end NNet

#endif // OPTIMIZERS_HPP


#include <Eigen/Dense>
#include <iostream>

using namespace Eigen;

template <typename Derived1, typename Derived2>
void copyUpperTriangularPart(MatrixBase<Derived1>& dst, const MatrixBase<Derived2>& src)
{
  /* Note the 'template' keywords in the following line! */
  dst.template triangularView<Upper>() = src.template triangularView<Upper>();
}

int main()
{
  MatrixXi m1 = MatrixXi::Ones(5,5);
  MatrixXi m2 = MatrixXi::Random(4,4);
  std::cout << "m2 before copy:" << std::endl;
  std::cout << m2 << std::endl << std::endl;
  copyUpperTriangularPart(m2, m1.topLeftCorner(4,4));
  std::cout << "m2 after copy:" << std::endl;
  std::cout << m2 << std::endl << std::endl;
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2016 Sebastian Schlenkrich

*/

#ifndef quantlib_templateauxilliaries_regression_hpp
#define quantlib_templateauxilliaries_regression_hpp

//#include <ql/types.hpp>
//#include <boost/function.hpp>

//#include <ql/experimental/template/auxilliaries/templatesvd.hpp>
#include <ql/experimental/templatemodels/auxilliaries/qrfactorisationT.hpp>

namespace TemplateAuxilliaries {

    template <class Type>
    class Regression {

    protected:

        size_t                               maxDegree_;  // max polynomial degree
        std::vector< std::vector<size_t> >   multIdx_;    // list of all multi-indeces with degree <= maxDegree_
        std::vector<Type>                    beta_;       // linear coefficients

        inline void divide( std::vector<size_t> x, size_t idx, size_t degree) {
            if (idx==x.size()-1) {
                x[idx] = degree;
                multIdx_.push_back(x);
            } else {
                for (size_t k=0; k<=degree; ++k) {
                    x[idx] = k;
                    divide(x, idx+1, degree-k);
                }
            }
        }

        // initialise multi-index matrix via recursive call of divide()
        inline void setUpMultiIndex(const size_t dim, const size_t maxDegree) {
            multIdx_.clear();
            std::vector<size_t> x(dim,0);
            for (size_t k=0; k<=maxDegree; ++k) divide(x,0,k);
        }

        // perform actual regression calculation
        inline void calculateRegression( const std::vector< std::vector<Type> >& controls,
                                         const std::vector< Type >&              observations ) {
            std::vector<Type> b(observations);
            std::vector< std::vector<Type> >  M(controls.size());
            for (size_t i=0; i<M.size(); ++i) M[i] = monomials(controls[i]);
            qrsolveles(M,b);
            for (size_t i=0; i<beta_.size(); ++i) beta_[i] = b[i];

        }

    public:

        Regression ( const std::vector< std::vector<Type> >& controls,
                     const std::vector< Type >&              observations,
                     const size_t                            maxDegree ) : maxDegree_(maxDegree) {
            // check dimensions
            size_t nRows = 0;
            if (controls.size()==observations.size()) nRows = controls.size();

            if (nRows>0) setUpMultiIndex(controls[0].size(),maxDegree_);
            size_t nCols = multIdx_.size();

            // initialise beta
            beta_.resize(nCols,0.0);
            if ((nRows>0)&&(nRows>=nCols))
                calculateRegression(controls,observations);  // if nRows < nCols regression does not really make sense

        }

        const std::vector<Type> monomials( const std::vector<Type>& x ) const {
            std::vector<Type> y(multIdx_.size(),0.0);
            if ((multIdx_.size()==0)||(multIdx_[0].size()!=x.size())) return y;  // dimension mismatch
            for (size_t i=0; i<y.size(); ++i) {
                y[i] = 1.0;
                for (size_t j=0; j<x.size(); ++j) {  // don't want to use pow coz not clear how it's implemented
                    for (size_t k=0; k<multIdx_[i][j]; ++k) y[i] *= x[j];
                }
            }
            return y;
        }

        const Type value( const std::vector<Type>& x ) const {
            std::vector<Type> y = monomials(x);
            if (y.size()!=beta_.size()) return 0.0;  // dimension mismatch
            Type res = 0.0;
            for (size_t i=0; i<y.size(); ++i) res += beta_[i] * y[i];
            return res;
        }

        // inspectors

        const size_t                              maxDegree() const { return maxDegree_; }
        const std::vector< std::vector<size_t> >& multIdx()   const { return multIdx_;   }
        const std::vector<Type>&                  beta()      const { return beta_;      }

        const std::vector< std::vector<Type> >    multiIndex() const {  // workaround for Excel interface debugging
            std::vector< std::vector<Type> > M(multIdx_.size());
            for (size_t i=0; i<multIdx_.size(); ++i) {
                M[i].resize(multIdx_[i].size());
                for (size_t j=0; j<multIdx_[i].size(); ++j) M[i][j] = multIdx_[i][j];
            }
            return M;
        }

    };

}

#endif  /* ifndef quantlib_templateauxilliaries_regression_hpp */


#ifndef CANNON_PHYSICS_SYSTEMS_INVERTED_PENDULUM_H
#define CANNON_PHYSICS_SYSTEMS_INVERTED_PENDULUM_H

#include <cmath>
#include <utility>
#include <random>

#include <Eigen/Dense>

#include <ompl/control/spaces/RealVectorControlSpace.h>

#include <cannon/physics/systems/system.hpp>
#include <cannon/physics/euler_integrator.hpp>
#include <cannon/log/registry.hpp>

using namespace Eigen;

using namespace cannon::log;

namespace oc = ompl::control;
namespace ob = ompl::base;

namespace cannon {
  namespace physics {
    namespace systems {

      struct PendSystem : System {
        PendSystem(double g = 10.0, double m = 1.0, double l = 1.0, double dt =
            0.05, double max_speed = 8.0) : g_(g), m_(m), l_(l), dt_(dt),
        max_speed_(max_speed) {}

        virtual void operator()(const VectorXd& x, VectorXd& dxdt, const double /*t*/) override {
          double th = x[0];
          double thdot = x[1];
          double u = x[2];

          double new_thdot = (-3 * g_ / (2 * l_)) * std::sin(th + M_PI) +
            (3.0 * u / (m_ * std::pow(l_, 2.0)));
          double new_th = thdot + (new_thdot * dt_);

          dxdt.resize(3);
          dxdt[0] = new_th;
          dxdt[1] = new_thdot;
          dxdt[2] = 0.0;
        }

        virtual void ompl_ode_adaptor(const oc::ODESolver::StateType& q,
            const oc::Control* control, oc::ODESolver::StateType& qdot) override {

          const double u = control->as<oc::RealVectorControlSpace::ControlType>()->values[0];

          VectorXd s(3);
          s[0] = q[0];
          s[1] = q[1];
          s[2] = u;
          VectorXd dsdt(3);

          (*this)(s, dsdt, 0.0);

          qdot.resize(q.size(), 0);
          for (unsigned int i = 0; i < q.size(); i++) {
            qdot[i] = dsdt[i];
          }
        }

        virtual std::tuple<MatrixXd, MatrixXd, VectorXd> get_linearization(const VectorXd& x) override {
          MatrixXd A = MatrixXd::Zero(2, 2);
          MatrixXd B = MatrixXd::Zero(2, 1);
          VectorXd c = VectorXd::Zero(2);

          // TODO

          return std::make_tuple(A, B, c);
        }

        virtual void
        get_continuous_time_linearization(const oc::ODESolver::StateType &q,
                                          Ref<MatrixXd> A,
                                          Ref<MatrixXd> B) override {
          throw std::runtime_error("Not implemented yet");
        }

        static void ompl_post_integration(const ob::State* /*state*/, const
            oc::Control* /*control*/, const double /*duration*/, ob::State *result) {
          // Nothing needed
        }

        // Parameters
        double g_;
        double m_;
        double l_;
        double dt_;
        double max_speed_;
      };

      class InvertedPendulum {
        public:
          InvertedPendulum(double max_torque = 2.0) : max_torque_(max_torque),
          e_(s_, 3, 0.05) {
            std::random_device rd;
            gen_ = std::mt19937(rd());

            th_dis_ = std::uniform_real_distribution<double>(-M_PI, M_PI);
            thdot_dis_ = std::uniform_real_distribution<double>(-1.0, 1.0);

            state_ = Vector3d::Zero(3);
            reset();
          }

          std::pair<VectorXd, double> step(double u) {
            double th = state_[0];
            double thdot = state_[1];

            double reward = -(std::pow(normalize_(th), 2.0) + (0.1 * std::pow(thdot,
                    2.0)) + (0.001 * std::pow(u, 2.0)));

            double clipped_u = std::max(-max_torque_, std::min(max_torque_, u));
            state_[2] = clipped_u;

            e_.set_state(state_);
            state_ = e_.step();

            state_[0] = std::atan2(std::sin(state_[0]),std::cos(state_[0]));
            state_[1] = std::max(-8.0, std::min(state_[1], 8.0));

            return std::make_pair(state_.head(2), reward);
          }

          VectorXd reset() {
            double th = th_dis_(gen_);
            double thdot = thdot_dis_(gen_);

            state_[0] = th;
            state_[1] = thdot;
            state_[2] = 0.0;

            return state_.head(2);
          }

          PendSystem s_;

        private:
          inline double normalize_(double th) {
            return (std::fmod((th + M_PI), (2.0 * M_PI)) - M_PI);
          }

          double max_torque_;

          EulerIntegrator<PendSystem> e_;

          Vector3d state_;

          std::mt19937 gen_;
          std::uniform_real_distribution<double> th_dis_;
          std::uniform_real_distribution<double> thdot_dis_;
      };

    } // namespace systems
  } // namespace physics
} // namespace cannon

#endif /* ifndef CANNON_PHYSICS_SYSTEMS_INVERTED_PENDULUM_H */


//
// Created by liyubo on 12/15/17.
//

#include <iostream>
using namespace std;
#include <ctime>
// Eigen 部分
#include <Eigen/Core>
// 稠密矩阵的代数运算（逆，特征值等）
#include <Eigen/Dense>
#include"hw.h"
#define MATRIX_SIZE 50

void homework()
{

//作业
    Eigen::MatrixXd matrix_A;
    matrix_A = Eigen::MatrixXd::Random( 100, 100 );
    Eigen::MatrixXd matrix_b;
    matrix_b = Eigen::MatrixXd::Random( 100, 1 );
    Eigen::MatrixXd x;
// cout << matrix_A << endl;
// cout << matrix_b << endl;
    x = matrix_A.llt().solve(matrix_b);   //llt Cholesky来解方程

    /*******************时间比较*********************/

    clock_t time_stt = clock();
    x = matrix_A.colPivHouseholderQr().solve(matrix_b);  //利用QR分解求解方程
    cout <<"time use in Qr decomposition is " <<1000*  (clock() - time_stt)/(double)CLOCKS_PER_SEC <<"ms" << endl;

    time_stt = clock();
    x = matrix_A.fullPivLu().solve(matrix_b);   //LU
    cout <<"time use  in fullPivLu  is " <<1000*  (clock() - time_stt)/(double)CLOCKS_PER_SEC <<"ms" << endl;

    time_stt = clock();
    x = matrix_A.inverse()*matrix_b;;  //利用求逆来解方程
    cout <<"time use  in normal inverse  is " <<1000*  (clock() - time_stt)/(double)CLOCKS_PER_SEC <<"ms" << endl;

    time_stt = clock();
    x = matrix_A.llt().solve(matrix_b);   //llt Cholesky来解方程
    cout <<"time use  in llt(Cholesky)  is " <<1000*  (clock() - time_stt)/(double)CLOCKS_PER_SEC <<"ms" << endl;

    time_stt = clock();
    x = matrix_A.ldlt().solve(matrix_b);   //ldlt
    cout <<"time use  in ldlt is " <<1000*  (clock() - time_stt)/(double)CLOCKS_PER_SEC <<"ms"<< endl;
}


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#include <boost/simd/function/divides.hpp>
#include <boost/simd/pack.hpp>
#include <boost/simd/function/saturated.hpp>
#include <boost/simd/meta/cardinal_of.hpp>
#include <simd_test.hpp>
#include <boost/simd/function/fast.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/zero.hpp>

namespace bs = boost::simd;

template <typename T, std::size_t N, typename Env>
void test(Env& $)
{
  using p_t = bs::pack<T, N>;

  T a1[N], a2[N], b[N];
  for(std::size_t i = 0; i < N; ++i)
  {
    a1[i] = (i%2) ? T(1+i) : T(3+i);
    a2[i] = (i%2) ? T(i+N) : T(2*i+1);
    b[i] = bs::divides(a1[i], a2[i]);
  }

  p_t aa1(&a1[0], &a1[0]+N);
  p_t aa2(&a2[0], &a2[0]+N);
  p_t bb(&b[0], &b[0]+N);

  STF_ULP_EQUAL(bs::divides(aa1, aa2), bb, 0.5);
  STF_ULP_EQUAL(aa1/aa2, bb, 0.5);
}

STF_CASE_TPL("Check divides on pack" , STF_NUMERIC_TYPES)
{
  static const std::size_t N = bs::pack<T>::static_size;

  test<T, N>($);
  test<T, N/2>($);
  test<T, N*2>($);
}

STF_CASE_TPL( "Check divides behavior with floating", STF_IEEE_TYPES )
{
  namespace bs = boost::simd;
  using bs::divides;
  using p_t = bs::pack<T>;
  using r_t = decltype(divides(p_t(), p_t()));

  STF_TYPE_IS(r_t, p_t);

#ifndef BOOST_SIMD_NO_INVALIDS
  STF_IEEE_EQUAL(divides(bs::Inf<p_t>(),  bs::Inf<p_t>()), bs::Nan<r_t>());
  STF_IEEE_EQUAL(divides(bs::Minf<p_t>(), bs::Minf<p_t>()), bs::Nan<r_t>());
  STF_IEEE_EQUAL(divides(bs::Nan<p_t>(),  bs::Nan<p_t>()), bs::Nan<r_t>());
#endif
  STF_EQUAL(divides(p_t(1), p_t(0)), bs::Inf<r_t>());
  STF_IEEE_EQUAL(divides(p_t(0), p_t(0)), bs::Nan<r_t>());
  STF_EQUAL(divides(p_t(1), p_t(1)), bs::One<r_t>());
}


///////////////////////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2015.
//  Copyright Nikhar Agrawal 2015.
//  Copyright Paul Bristow 2015.
//  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)

//! \file
//!\brief Tests for the transcendental exponential function of (fixed_point) for a tiny digit range.

#include <cmath>

#define BOOST_TEST_MODULE test_negatable_func_exp_tiny
#define BOOST_LIB_DIAGNOSTIC

#include <boost/fixed_point/fixed_point.hpp>
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_negatable_func_exp_tiny)
{
  typedef boost::fixed_point::negatable<4, -11> fixed_point_type;
  typedef fixed_point_type::float_type          float_point_type;

  const fixed_point_type tol = ldexp(fixed_point_type(1), fixed_point_type::resolution + 2);

  // Check positive arguments.
  for(int i = 12; i >= 2; --i)
  {
    const fixed_point_type x = exp(fixed_point_type(3.1415926535897932385L) / fixed_point_type(i));

    using std::exp;
    const float_point_type y = exp(float_point_type(3.1415926535897932385L) / float_point_type(i));

    BOOST_CHECK_CLOSE_FRACTION(x, fixed_point_type(y), tol);
  }

  // Check negative arguments.
  for(int i = 12; i >= 2; --i)
  {
    const fixed_point_type x = exp(fixed_point_type(-3.1415926535897932385L) / fixed_point_type(i));

    using std::exp;
    const float_point_type y = exp(float_point_type(-3.1415926535897932385L) / float_point_type(i));

    BOOST_CHECK_CLOSE_FRACTION(x, fixed_point_type(y), tol);
  }

  const fixed_point_type local_e = boost::fixed_point::negatable_constants<fixed_point_type>::e();

  BOOST_CHECK_EQUAL(exp(fixed_point_type(1)), local_e);
}


#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/matrix_cl.hpp>
#include <stan/math/opencl/copy.hpp>
#include <test/unit/util.hpp>
#include <Eigen/Dense>
#include <gtest/gtest.h>

TEST(KernelGenerator, constant_test) {
  stan::math::matrix_cl<double> m1_cl(stan::math::constant(1.2, 3, 4));

  Eigen::MatrixXd res = stan::math::from_matrix_cl(m1_cl);
  EXPECT_MATRIX_EQ(res, Eigen::MatrixXd::Constant(3, 4, 1.2));
}

#endif


#include <catch2/catch.hpp>

#include <Eigen/Core>

#include <ipc/broad_phase/hash_grid.hpp>

using namespace ipc;

// TEST_CASE("AABB initilization", "[hash_grid][AABB]")
// {
//     int dim = GENERATE(2, 3);
//     CAPTURE(dim);
//     AABB aabb;
//     ArrayMax3d actual_center(dim);
//     SECTION("Empty AABB")
//     {
//         aabb = AABB(ArrayMax3d::Zero(dim), ArrayMax3d::Zero(dim));
//         actual_center.setZero();
//     }
//     SECTION("Box centered at zero")
//     {
//         ArrayMax3d min =
//             ArrayMax3d::Random(dim).array() - 1; // in range [-2, 0]
//         ArrayMax3d max = -min;
//         aabb = AABB(min, max);
//         actual_center.setZero();
//     }
//     SECTION("Box not centered at zero")
//     {
//         ArrayMax3d min(dim), max(dim);
//         if (dim == 2) {
//             min << 5.1, 3.14;
//             max << 10.4, 7.89;
//             actual_center << 7.75, 5.515;
//         } else {
//             min << 5.1, 3.14, 7.94;
//             max << 10.4, 7.89, 10.89;
//             actual_center << 7.75, 5.515, 9.415;
//         }
//         aabb = AABB(min, max);
//     }
//     ArrayMax3d center_diff = aabb.getCenter() - actual_center;
//     CHECK(center_diff.matrix().norm() == Approx(0.0).margin(1e-12));
// }

TEST_CASE("AABB overlapping", "[has_grid][AABB]")
{
    AABB a, b;
    bool are_overlapping = false;
    SECTION("a to the right of b")
    {
        a = AABB(Eigen::Array2d(-1, 0), Eigen::Array2d(0, 1));
        SECTION("overlapping")
        {
            b = AABB(Eigen::Array2d(-0.5, 0), Eigen::Array2d(0.5, 1));
            are_overlapping = true;
        }
        SECTION("not overlapping")
        {
            b = AABB(Eigen::Array2d(0.5, 0), Eigen::Array2d(1.5, 1));
            are_overlapping = false;
        }
    }
    SECTION("b to the right of a")
    {
        b = AABB(Eigen::Array2d(-1, 0), Eigen::Array2d(0, 1));
        SECTION("overlapping")
        {
            a = AABB(Eigen::Array2d(-0.5, 0), Eigen::Array2d(0.5, 1));
            are_overlapping = true;
        }
        SECTION("not overlapping")
        {
            a = AABB(Eigen::Array2d(0.5, 0), Eigen::Array2d(1.5, 1));
            are_overlapping = false;
        }
    }
    SECTION("a above b")
    {
        a = AABB(Eigen::Array2d(0, -1), Eigen::Array2d(1, 0));
        SECTION("overlapping")
        {
            b = AABB(Eigen::Array2d(0, -0.5), Eigen::Array2d(1, 0.5));
            are_overlapping = true;
        }
        SECTION("not overlapping")
        {
            b = AABB(Eigen::Array2d(0, 0.5), Eigen::Array2d(1, 1.5));
            are_overlapping = false;
        }
    }
    SECTION("a above b")
    {
        b = AABB(Eigen::Array2d(0, -1), Eigen::Array2d(1, 0));
        SECTION("overlapping")
        {
            a = AABB(Eigen::Array2d(0, -0.5), Eigen::Array2d(1, 0.5));
            are_overlapping = true;
        }
        SECTION("not overlapping")
        {
            a = AABB(Eigen::Array2d(0, 0.5), Eigen::Array2d(1, 1.5));
            are_overlapping = false;
        }
    }
    CHECK(AABB::are_overlapping(a, b) == are_overlapping);
}


#include <gtest/gtest.h>

#include <Eigen/Dense>
#include "TransformMatrix.hpp"

using namespace Geometry2d;

TEST(TransformMatrix, Convert) {
    // Test conversion to/from Eigen
    TransformMatrix transform(Point(0, 1), 1.0);
    Eigen::Matrix<double, 3, 3> transformEigen = transform;
    EXPECT_EQ(transformEigen * Eigen::Vector3d(0, 0, 1),
              Eigen::Vector3d(0, 1, 1));
    TransformMatrix transformedBack = transformEigen;
    EXPECT_EQ(transform * Point(0, 1), transformedBack * Point(0, 1));
    EXPECT_EQ(transform * Point(1, 0), transformedBack * Point(1, 0));
}

TEST(TransformMatrix, Compose) {
    // Test composition: self-compose a 90-degree rotation with an offset from
    // the origin
    TransformMatrix transform1(Point(0, 1), M_PI / 2);
    TransformMatrix transform2(Point(1, 0), M_PI / 2);

    EXPECT_LT(((transform1 * transform2) * Point(1, 0) -
               transform1 * (transform2 * Point(1, 0)))
                  .mag(),
              1e-6)
        << "Composition should be associative!";

    EXPECT_LT(((transform2 * transform1) * Point(1, 0) -
               transform2 * (transform1 * Point(1, 0)))
                  .mag(),
              1e-6)
        << "Composition should be associative!";
}

TEST(TransformMatrix, Reconstruct) {
    Eigen::Matrix<double, 3, 3> transformEigen;
    transformEigen << 0, 1, 0, -1, 0, 1, 0, 0, 1;
    TransformMatrix transform = transformEigen;
    EXPECT_EQ(transform.origin(), Point(0, 1));
    EXPECT_NEAR(transform.rotation(), -M_PI / 2 + 2 * M_PI, 1e-6);
}


//  (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)

#ifndef BOOST_MATH_TOOLS_CENTERED_CONTINUED_FRACTION_HPP
#define BOOST_MATH_TOOLS_CENTERED_CONTINUED_FRACTION_HPP

#include <cmath>
#include <cstdint>
#include <vector>
#include <ostream>
#include <iomanip>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <array>
#include <type_traits>
#include <boost/math/tools/is_standalone.hpp>

#ifndef BOOST_MATH_STANDALONE
#include <boost/core/demangle.hpp>
#endif

namespace boost::math::tools {

template<typename Real, typename Z = int64_t>
class centered_continued_fraction {
public:
    centered_continued_fraction(Real x) : x_{x} {
        static_assert(std::is_integral_v<Z> && std::is_signed_v<Z>,
                      "Centered continued fractions require signed integer types.");
        using std::round;
        using std::abs;
        using std::sqrt;
        using std::isfinite;
        if (!isfinite(x))
        {
            throw std::domain_error("Cannot convert non-finites into continued fractions.");
        }
        b_.reserve(50);
        Real bj = round(x);
        b_.push_back(static_cast<Z>(bj));
        if (bj == x)
        {
            b_.shrink_to_fit();
            return;
        }
        x = 1/(x-bj);
        Real f = bj;
        if (bj == 0)
        {
            f = 16*(std::numeric_limits<Real>::min)();
        }
        Real C = f;
        Real D = 0;
        int i = 0;
        while (abs(f - x_) >= (1 + i++)*std::numeric_limits<Real>::epsilon()*abs(x_))
        {
            bj = round(x);
            b_.push_back(static_cast<Z>(bj));
            x = 1/(x-bj);
            D += bj;
            if (D == 0) {
                D = 16*(std::numeric_limits<Real>::min)();
            }
            C = bj + 1/C;
            if (C==0)
            {
                C = 16*(std::numeric_limits<Real>::min)();
            }
            D = 1/D;
            f *= (C*D);
        }
        // Deal with non-uniqueness of continued fractions: [a0; a1, ..., an, 1] = a0; a1, ..., an + 1].
        if (b_.size() > 2 && b_.back() == 1)
        {
            b_[b_.size() - 2] += 1;
            b_.resize(b_.size() - 1);
        }
        b_.shrink_to_fit();

        for (size_t i = 1; i < b_.size(); ++i)
        {
            if (b_[i] == 0) {
                std::ostringstream oss;
                oss << "Found a zero partial denominator: b[" << i << "] = " << b_[i] << "."
                    #ifndef BOOST_MATH_STANDALONE
                    << " This means the integer type '" << boost::core::demangle(typeid(Z).name())
                    #else
                    << " This means the integer type '" << typeid(Z).name()
                    #endif
                    << "' has overflowed and you need to use a wider type,"
                    << " or there is a bug.";
                throw std::overflow_error(oss.str());
            }
        }
    }

    Real khinchin_geometric_mean() const {
        if (b_.size() == 1)
        {
            return std::numeric_limits<Real>::quiet_NaN();
        }
        using std::log;
        using std::exp;
        using std::abs;
        const std::array<Real, 7> logs{std::numeric_limits<Real>::quiet_NaN(), Real(0), log(static_cast<Real>(2)), log(static_cast<Real>(3)), log(static_cast<Real>(4)), log(static_cast<Real>(5)), log(static_cast<Real>(6))};
        Real log_prod = 0;
        for (size_t i = 1; i < b_.size(); ++i)
        {
            if (abs(b_[i]) < static_cast<Z>(logs.size()))
            {
                log_prod += logs[abs(b_[i])];
            }
            else
            {
                log_prod += log(static_cast<Real>(abs(b_[i])));
            }
        }
        log_prod /= (b_.size()-1);
        return exp(log_prod);
    }

    const std::vector<Z>& partial_denominators() const {
        return b_;
    }

    template<typename T, typename Z2>
    friend std::ostream& operator<<(std::ostream& out, centered_continued_fraction<T, Z2>& ccf);

private:
    const Real x_;
    std::vector<Z> b_;
};

template<typename Real, typename Z2>
std::ostream& operator<<(std::ostream& out, centered_continued_fraction<Real, Z2>& scf) {
    constexpr const int p = std::numeric_limits<Real>::max_digits10;
    if constexpr (p == 2147483647)
    {
        out << std::setprecision(scf.x_.backend().precision());
    }
    else
    {
        out << std::setprecision(p);
    }

    out << "[" << scf.b_.front();
    if (scf.b_.size() > 1)
    {
        out << "; ";
        for (size_t i = 1; i < scf.b_.size() -1; ++i)
        {
            out << scf.b_[i] << ", ";
        }
        out << scf.b_.back();
    }
    out << "]";
    return out;
}

}
#endif


#include "WilhelmsenProjection.h"
#include <iterator>
#include <cassert>

#ifdef USE_EIGEN
#include <Eigen/LU>
USING_PART_OF_NAMESPACE_EIGEN
#else
#include <cml/cml.h>
using namespace cml;
#endif

template <class T> std::vector<int> WilhelmsenProjection<T>::constraints;
template <class T> const T WilhelmsenProjection<T>::eps   = 1e-7;
template <class T> const T WilhelmsenProjection<T>::eps2  = 1e-14;

// --------------------------------------------------------------------------
template <class T>
#ifdef USE_EIGEN
        Eigen::Matrix<T, Eigen::Dynamic, 1>
#else
        cml::vector< T, cml::dynamic<> >
#endif
        WilhelmsenProjection<T>::projectSubspace(const std::vector<vector6> &S,
                                              const vector6 &q)
{
    // set up an nxn linear system, Ax = b, to solve for the projection
    //  if S = { e_1, e_2, ..., e_n }
    //  then the system is \sum_{i=1}^n x_i <e_i e_j> = <q e_j> for j = 1..n

    int n = S.size();
    vectorn result(n);

    // residuals to check which solution is better
//    T cmlR, eigenR;

#ifdef USE_EIGEN
    // use Eigen's LU factorization to solve the normal equations
    matrixn A(n, n);
    for (int i = 0; i < n; ++i)
        for (int j = i; j < n; ++j)
            A(i, j) = A(j, i) = S[i].dot(S[j]);

    vectorn b(n);
    for (int j = 0; j < n; ++j)
        b[j] = q.dot(S[j]);

    vectorn x;
    A.lu().solve(b, &x);
    result = x;

//    T det = A.determinant();
//    if (fabs(det) < eps2)
//        std::cout << "Warning: det(A) = " << det << std::endl;

    // compute the residual from the solution
//    eigenR = (A*x - b).norm();

#else
    // set up the matrix A
    matrixn A(n, n);
    for (int i = 0; i < n; ++i)
        for (int j = i; j < n; ++j)
            A(i, j) = A(j, i) = dot(S[i], S[j]);

    // set up the vector b
    vectorn b(n);
    for (int j = 0; j < n; ++j)
        b[j] = dot(q, S[j]);

    // compute x by inverting the matrix A
    vectorn x = inverse(A) * b;
    result = x;

    // compute the residual from the solution
//    cmlR = length(A*x - b);
#endif

    // output a status to see which one performs better
//    static int counter = 0;
//    if (++counter > 500)
//    {
//        std::cout << "CML residual: " << cmlR << '\t'
//                << "Eigen residual:" << eigenR << std::endl;
//        counter = 0;
//    }

    return result;
}

// --------------------------------------------------------------------------
template <class T>
#ifdef USE_EIGEN
        Eigen::Matrix<T, 6, 1>
#else
        cml::vector< T, cml::fixed<6> >
#endif
        WilhelmsenProjection<T>::projectCone(const std::vector<vector6> &K,
                                             const vector6 &q)
{
    // Step 0: find an initial vector in the direction of q
    int n = K.size(), ki = 0;
    vector6 p = zero6();

    std::vector<vector6>    F;
    std::vector<int>        index;
    std::vector<T>          lambda;
    std::vector<bool>       used(n, false);

    for (; ki < n && dot(q, K[ki]) < eps2; ++ki);
    if (ki < n) {
        F.push_back(K[ki]);
        index.push_back(ki);
        lambda.push_back(dot(q, F[0]) / norm2(F[0]));
        p = lambda[0] * F[0];
        used[ki] = true;
    }
    else return p;

    // main algorithm iteration loop
    for (;;)
    {
        // Step 1: look for the next generator that lies between the current
        //         subcone and the target point
        vector6 eta = q - p;
        if (norm2(eta) < eps2) break;

        // this search is linear... can we do better?
        for (ki = 0; ki < n; ++ki) {
            if (used[ki]) continue;
            if (dot(eta, K[ki]) > eps) break;
        }
        if (ki < n) {
            F.push_back(K[ki]);
            index.push_back(ki);
            lambda.push_back(0.0);
            used[ki] = true;
        }
        else break;

        // auxiliary loop to reduce the subcone to the smallest face
        bool repeat;
        do
        {
            // Step 2: project the target onto the span of F and see if the point
            //         lies within the subcone generated by F
            vectorn beta = projectSubspace(F, q);
            int m = F.size();
            T lo = smallest(beta);

            // if the projected point is outside the subcone, we need to find
            // the nearest point in the subspace within the subcone generated by F
            if (lo < -eps)
            {
                T rho = 1.0;
                for (int i = 0; i < m; ++i)
                    if (lambda[i] - beta[i] >= eps)
                        rho = std::min(rho, lambda[i] / (lambda[i] - beta[i]));

                // compute new barycentric coordinates gamma
                for (int i = 0; i < m; ++i)
                    beta[i] = (1.0 - rho) * lambda[i] + rho * beta[i];
                lo = smallest(beta);
                repeat = true;
            }
            // otherwise the projected point is already within the subcone,
            // so just remove redundant generators from F
            else repeat = false;

            // remove generators not on the separating hyperplane (zero barycentric)
            if (lo < eps) {
                std::vector<vector6> E; E.swap(F);
                std::vector<int> edex; edex.swap(index);
                lambda.clear();
                for (int i = 0; i < m; ++i) {
                    if (beta[i] >= eps) {
                        F.push_back(E[i]);
                        index.push_back(edex[i]);
                        lambda.push_back(beta[i]);
                    }
                }
            }
            else {
                for (int i = 0; i < m; ++i)
                    lambda[i] = beta[i];
            }

            // compute the new projected point p based on F and lambda
            m = F.size();
            p = zero6();
            for (int i = 0; i < m; ++i)
                p += lambda[i] * F[i];

        } while (repeat);
    }

    constraints = index;

    return p;
}

// --------------------------------------------------------------------------
template <class T>
        void WilhelmsenProjection<T>::conditionGenerators(std::vector<vector6> &K)
{
    std::vector<vector6> C;
    for (typename std::vector<vector6>::iterator kt = K.begin(); kt != K.end(); ++kt)
    {
        // check vector length
        T len = norm(*kt);
        if (len < eps) continue;
        vector6 v = *kt / len;

        // check for parallel vectors
        bool indep = true;
        for (typename std::vector<vector6>::iterator ct = C.begin(); ct != C.end(); ++ct)
            if (1.0 - dot(v, *ct) < eps) { indep = false; break; }
        if (indep) C.push_back(v);
    }

    // swap K with the set of conditioned generators
    K.swap(C);
}

// --------------------------------------------------------------------------
// debug function to print out the last set of active constraints

template <class T>
        void WilhelmsenProjection<T>::printConstraints()
{
    std::cout << "Wilhelmson constraints: ";
    std::copy(constraints.begin(), constraints.end(),
              std::ostream_iterator<int>(std::cout, "  "));
    std::cout << std::endl;
}

// --------------------------------------------------------------------------
// template instantiations for double and long double data types

template class WilhelmsenProjection<double>;
template class WilhelmsenProjection<long double>;

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


#include <iostream>

#include "AnimatedTetrahedronMesh.h"

#include <Eigen/Dense>

#include <map>

template<class T>
struct LatticeMesh : public AnimatedTetrahedonMesh<T>
{
    using Base = AnimatedTetrahedonMesh<T>;

    // from AnimatedTetrahedonMesh
    using Base::m_meshElements;
    using Base::m_particleX;
    using Base::initializeUSD;
    using Base::initializeTopology;
    using Base::initializeParticles;
    using Vector3 = typename Base::Vector3;

    std::array<int, 3> m_cellSize; // dimensions in grid cells
    int m_radius; // radius of sphere in grid cells
    T m_gridDX;

    std::vector<std::array<int, 3>> m_activeCells; // Marks the "active" cells in the lattice
    std::map<std::array<int, 3>, int> m_activeNodes; // Maps the "active" nodes to their particle index

    void initialize()
    {
        initializeUSD("Demo3D.usda");

        // Activate cells within a sphere of radius m_radius (in cells)

        for(int cell_i = 0; cell_i < m_cellSize[0]; cell_i++)
        for(int cell_j = 0; cell_j < m_cellSize[1]; cell_j++)
        for(int cell_k = 0; cell_k < m_cellSize[1]; cell_k++){

            int r = (cell_i - m_cellSize[0]/2) * (cell_i - m_cellSize[0]/2) +
                    (cell_j - m_cellSize[1]/2) * (cell_j - m_cellSize[1]/2) +
                    (cell_k - m_cellSize[2]/2) * (cell_k - m_cellSize[2]/2);

            if(r <= m_radius * m_radius)
                m_activeCells.push_back(std::array<int, 3>{cell_i, cell_j, cell_k});

        }

        std::cout << "Created a model including " << m_activeCells.size() << " lattice cells" <<std::endl;

        // Create (uniquely numbered) particles at the node corners of active cells

        for(const auto& cell: m_activeCells){
            std::array<int, 3> node;
            for(node[0] = cell[0]; node[0] <= cell[0]+1; node[0]++)
            for(node[1] = cell[1]; node[1] <= cell[1]+1; node[1]++)
            for(node[2] = cell[2]; node[2] <= cell[2]+1; node[2]++){
                auto search = m_activeNodes.find(node);
                if(search == m_activeNodes.end()){ // Particle not yet created at this lattice node location -> make one
                    m_activeNodes.insert({node, m_particleX.size()});
                    m_particleX.emplace_back(m_gridDX * T(node[0]), m_gridDX * T(node[1]), m_gridDX * T(node[2]));
                }
            }
        }
        std::cout << "Model contains " << m_particleX.size() << " particles" << std::endl;

        // Make tetrahedra out of all active cells (6 tetrahedra per cell)

        for(const auto& cell: m_activeCells){
            int vertexIndices[2][2][2];
            for(int i = 0; i <= 1; i++)
            for(int j = 0; j <= 1; j++)
            for(int k = 0; k <= 1; k++){
                std::array<int, 3> node{cell[0] + i, cell[1] + j, cell[2] + k};
                auto search = m_activeNodes.find(node);
                if(search != m_activeNodes.end())
                    vertexIndices[i][j][k] = search->second;
                else
                    throw std::logic_error("particle at cell vertex not found");
            }

            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][0], vertexIndices[1][1][0], vertexIndices[1][1][1]});
            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][0], vertexIndices[1][1][1], vertexIndices[1][0][1]});
            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][0][1], vertexIndices[1][1][1], vertexIndices[0][0][1]});
            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][1], vertexIndices[0][1][1], vertexIndices[0][0][1]});
            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][1], vertexIndices[0][1][0], vertexIndices[0][1][1]});
            m_meshElements.push_back(std::array<int, 4>{ vertexIndices[0][0][0], vertexIndices[1][1][0], vertexIndices[0][1][0], vertexIndices[1][1][1]});
        }

        // Perform the USD-specific initialization of topology & particles
        // (this will also create a boundary *surface* to visualuze

        initializeTopology();
        initializeParticles();

        // Check particle indexing in mesh

        for(const auto& element: m_meshElements)
            for(const auto vertex: element)
                if(vertex < 0 || vertex >= m_particleX.size())
                    throw std::logic_error("mismatch between mesh vertex and particle array");
    }
};

int main(int argc, char *argv[])
{
    LatticeMesh<float> simulationMesh;
    simulationMesh.m_cellSize = { 20, 20, 20 };
    simulationMesh.m_radius = 8;
    simulationMesh.m_gridDX = 0.1;

    // Initialize the simulation example
    simulationMesh.initialize();

    // Output the initial shape of the mesh
    simulationMesh.writeFrame(0);

    // Write the entire timeline to USD
    simulationMesh.writeUSD();

    return 0;
}


#include "eigen_ext.hpp"
#include "function.hpp"
#include "parameters.hpp"
#include <cassert>
#include <iostream>

#include <Eigen/Core>
#include <Eigen/SparseCore>

namespace pear {
void fun_diff(Vec &Cu, Vec &Cv, Vec &Ru, Vec &Rv, Mat &RudCu, Mat &RudCv,
              Mat &RvdCu, Mat &RvdCv) {

  int np = Cu.rows();

  // FUNCTIONS
  Ru = pear::Vmu * Cu.array() /
       ((pear::Kmu + Cu.array()) * (1 + Cv.array() / pear::Kmv));

  Rv = pear::rq * Ru.array() + pear::Vmfv / (1 + Cu.array() / pear::Kmfu);

  // DERIVATIVES
  Vec RudCu_diag(np);
  Vec RudCVdiag(np);
  Vec RvdCu_diag(np);
  Vec RvdCVdiag(np);

  RudCu_diag = (pear::Kmu * pear::Kmv * pear::Vmu) /
               ((Cu.array() + pear::Kmu).pow(2) * (Cv.array() + pear::Kmv));

  RudCVdiag = -(Cu.array() * pear::Kmv * pear::Vmu) /
              ((Cu.array() + pear::Kmu) * (Cv.array() + pear::Kmv).pow(2));

  RvdCu_diag = (pear::Kmv * pear::Vmu * pear::rq) /
                   ((Cu.array() + pear::Kmu) * (Cv.array() + pear::Kmv)) -
               (pear::Kmfu * pear::Vmfv) / (Cu.array() + pear::Kmfu).pow(2) -
               (Cu.array() * pear::Kmv * pear::Vmu * pear::rq) /
                   ((Cu.array() + pear::Kmu).pow(2) * (Cv.array() + pear::Kmv));

  RvdCVdiag = -(Cu.array() * pear::Kmv * pear::Vmu * pear::rq) /
              ((Cu.array() + pear::Kmu) * (Cv.array() + pear::Kmv).pow(2));

  RudCu = RudCu_diag.asDiagonal();
  RvdCu = RudCu_diag.asDiagonal();
  RudCv = RudCu_diag.asDiagonal();
  RvdCv = RudCu_diag.asDiagonal();
}

} // namespace pear


#ifndef __IRLS_H__
#define __IRLS_H__

#include <armadillo>

#include <glm/glm_info.hpp>
#include <glm/models/glm_model.hpp>

/**
 * Maximum number of iterations in the IRLS algorithm.
 */
static const int IRLS_MAX_ITERS = 25;

/**
 * Smallest change in likelihood before terminating the IRLS algorithm.
 */
static double IRLS_TOLERANCE = 10e-8;

/**
* Sets the weights of missing observations to zero, so that
* the will not influence the regression.
*
* @param missing Missing individuals are indicated by 1.
* @param w Vector of weights, missing entries will be replaced by 0.
*/
void set_missing_to_zero(const arma::uvec &missing, arma::vec &w);

/**
 * Compute the chisquare cdf for a vector of chi square variables.
 *
 * @param x Vector of chi square values.
 * @param df Degrees of freedom.
 *
 * @return Vector of corresponding p-values.
 */
arma::vec chi_square_cdf(const arma::vec &x, unsigned int df);

/**
 * Solves the weighted least square problem:
 *
 *   W*X*W*b = X*W*y
 *
 * The algorithm uses the singular value decomposition, to
 * compute the solution b:
 *
 *   b = V * S^-1 * U^t sqrt( w ) * y
 *
 * where wX = U S V^t
 *
 * @param X The design matrix.
 * @param y The right hand side.
 * @param w The weight for each observation.
 * @param fast_inversion If true use less robust but faster inversion.
 *
 * @return The vector b that minimizes the weighted least squares problem.
 */
arma::vec weighted_least_squares(const arma::mat &X, const arma::vec &y, const arma::vec &w, bool fast_inversion = false);

/**
 * Compute the adjusted dependent variates in the Iteratively reweighted
 * least squares algorithm.
 *
 * @param eta The linearized parameter.
 * @param mu The mean value parameter.
 * @param mu_eta The derivative of mu with respect to eta.
 * @param y The observations.
 *
 * @return The adjusted dependent variates.
 */
arma::vec compute_z(const arma::vec &eta, const arma::vec &mu, const arma::vec &mu_eta, const arma::vec &y);

/**
 * Compute the weight vector that is used in one iteration
 * in the Iteratively reweighted least squares algorithm.
 *
 * @param var Variance of each observation.
 * @param mu_eta The derivative of mu with respect to eta.
 *
 * @return A weight vector.
 */
arma::vec compute_w(const arma::vec &var, const arma::vec& mu_eta);

/**
 * This function performs the iteratively reweighted
 * least squares algorithm to estimate beta coefficients
 * of a genearlized linear model.
 *
 * @param X The design matrix (caller is responsible for
 *          adding an intercept).
 * @param y The observations.
 * @param model The GLM model to estimate.
 * @param output Output statistics of the estimated betas.
 * @param fast_inversion If true use less robust but faster inversion.
 *
 * @return Estimated beta coefficients.
 */
arma::vec irls(const arma::mat &X, const arma::vec &y, const glm_model &model, glm_info &output, bool fast_inversion = false);

/**
 * This function performs the iteratively reweighted
 * least squares algorithm to estimate beta coefficients
 * of a genearlized linear model.
 *
 * @param X The design matrix (caller is responsible for
 *          adding an intercept).
 * @param y The observations.
 * @param missing Identifies missing sampels by 1 and non-missing by 0.
 * @param model The GLM model to estimate.
 * @param output Output statistics of the estimated betas.
 * @param fast_inversion If true use less robust but faster inversion.
 *
 * @return Estimated beta coefficients.
 */
arma::vec irls(const arma::mat &X, const arma::vec &y, const arma::uvec &missing, const glm_model &model, glm_info &output, bool fast_inversion = false);

#endif /* End of __IRLS_H__ */


#ifndef MLT_MODELS_IMPLEMENTATIONS_AUTOENCODER_HPP
#define MLT_MODELS_IMPLEMENTATIONS_AUTOENCODER_HPP

#include <limits>
#include <tuple>

#include <Eigen/Core>

#include "../../defs.hpp"

namespace mlt {
namespace models {
namespace implementations {
namespace autoencoder {
	template <class HiddenActivation, class ReconstructionActivation>
	auto loss(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();

		return (((reconstruction_activation.compute(reconstruction_z)) - target).array().pow(2).sum() / (2 * input.cols())) +
			regularization * hidden_weights.array().pow(2).sum() +
			regularization * reconstruction_weights.array().pow(2).sum();
	}

	template <class HiddenActivation, class ReconstructionActivation>
	tuple<MatrixXd, VectorXd, MatrixXd, VectorXd>
	gradient(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();
		auto recontstruction_error = ((reconstruction_activation.compute(reconstruction_z) - target)).eval();
		auto reconstruction_delta = (recontstruction_error.cwiseProduct(reconstruction_activation.gradient(reconstruction_z))).eval();

		auto hidden_delta = ((reconstruction_weights.transpose() * reconstruction_delta).cwiseProduct(hidden_activation.gradient(hidden_z))).eval();

		return{ (hidden_delta * input.transpose() / input.cols()) + regularization * 2 * hidden_weights,
			hidden_delta.rowwise().sum() / input.cols(),
			(reconstruction_delta * hidden_a.transpose() / input.cols()) + regularization * 2 * reconstruction_weights,
			reconstruction_delta.rowwise().sum() / input.cols() };
	}

	template <class HiddenActivation, class ReconstructionActivation>
	tuple<double, MatrixXd, VectorXd, MatrixXd, VectorXd>
	loss_and_gradient(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();
		auto recontstruction_error = ((reconstruction_activation.compute(reconstruction_z) - target)).eval();
		auto reconstruction_delta = (recontstruction_error.cwiseProduct(reconstruction_activation.gradient(reconstruction_z))).eval();

		auto loss = (recontstruction_error.array().pow(2).sum() / (2 * input.cols())) +
			regularization * hidden_weights.array().pow(2).sum() +
			regularization * reconstruction_weights.array().pow(2).sum();

		auto hidden_delta = ((reconstruction_weights.transpose() * reconstruction_delta).cwiseProduct(hidden_activation.gradient(hidden_z))).eval();

		return{ loss,
			(hidden_delta * input.transpose() / input.cols()) + regularization * 2 * hidden_weights,
			hidden_delta.rowwise().sum() / input.cols(),
			(reconstruction_delta * hidden_a.transpose() / input.cols()) + regularization * 2 * reconstruction_weights,
			reconstruction_delta.rowwise().sum() / input.cols() };
	}

	template <class HiddenActivation, class ReconstructionActivation>
	auto sparse_loss(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, double sparsity, double sparsity_weight,
	MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();

		auto rho_hat = ((hidden_a.rowwise().sum() / input.cols()).unaryExpr([](double x) { return abs(x - 1.0) < numeric_limits<double>::epsilon() ? (x + numeric_limits<double>::epsilon()) : x; })).eval();
		auto sparsity_penalty = ((sparsity * (sparsity / rho_hat.array()).log()) + ((1 - sparsity) * ((1 - sparsity) / (1 - rho_hat.array())).log())).sum();

		return (((reconstruction_activation.compute(reconstruction_z)) - target).array().pow(2).sum() / (2 * input.cols())) +
			regularization * hidden_weights.array().pow(2).sum() +
			regularization * reconstruction_weights.array().pow(2).sum() +
			sparsity_weight * sparsity_penalty;
	}

	template <class HiddenActivation, class ReconstructionActivation>
	tuple<MatrixXd, VectorXd, MatrixXd, VectorXd>
	sparse_gradient(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, double sparsity, double sparsity_weight,
	MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();
		auto recontstruction_error = ((reconstruction_activation.compute(reconstruction_z) - target)).eval();
		auto reconstruction_delta = (recontstruction_error.cwiseProduct(reconstruction_activation.gradient(reconstruction_z))).eval();

		auto rho_hat = ((hidden_a.rowwise().sum() / input.cols()).unaryExpr([](double x) { return abs(x - 1.0) < numeric_limits<double>::epsilon() ? (x + numeric_limits<double>::epsilon()) : x; })).eval();
		auto sparsity_delta = ((-sparsity / rho_hat.array()) + ((1 - sparsity) / (1 - rho_hat.array()))).matrix().eval();
		auto hidden_delta = (((reconstruction_weights.transpose() * reconstruction_delta).colwise() +
			(sparsity_weight * sparsity_delta)).cwiseProduct(hidden_activation.gradient(hidden_z))).eval();

		return{ (hidden_delta * input.transpose() / input.cols()) + regularization * 2 * hidden_weights,
			hidden_delta.rowwise().sum() / input.cols(),
			(reconstruction_delta * hidden_a.transpose() / input.cols()) + regularization * 2 * reconstruction_weights,
			reconstruction_delta.rowwise().sum() / input.cols() };
	}

	template <class HiddenActivation, class ReconstructionActivation>
	tuple<double, MatrixXd, VectorXd, MatrixXd, VectorXd>
	sparse_loss_and_gradient(const HiddenActivation& hidden_activation, const ReconstructionActivation& reconstruction_activation,
	MatrixXdRef hidden_weights, VectorXdRef hidden_intercepts,
	MatrixXdRef reconstruction_weights, VectorXdRef reconstruction_intercepts,
	double regularization, double sparsity, double sparsity_weight,
	MatrixXdRef input, MatrixXdRef target) {
		auto hidden_z = ((hidden_weights * input).colwise() + hidden_intercepts).eval();
		auto hidden_a = (hidden_activation.compute(hidden_z)).eval();
		auto reconstruction_z = ((reconstruction_weights * hidden_a).colwise() + reconstruction_intercepts).eval();
		auto recontstruction_error = ((reconstruction_activation.compute(reconstruction_z) - target)).eval();
		auto reconstruction_delta = (recontstruction_error.cwiseProduct(reconstruction_activation.gradient(reconstruction_z))).eval();

		auto rho_hat = ((hidden_a.rowwise().sum() / input.cols()).unaryExpr([](double x) { return abs(x - 1.0) < numeric_limits<double>::epsilon() ? (x + numeric_limits<double>::epsilon()) : x; })).eval();
		auto sparsity_delta = ((-sparsity / rho_hat.array()) + ((1 - sparsity) / (1 - rho_hat.array()))).eval().matrix();
		auto hidden_delta = (((reconstruction_weights.transpose() * reconstruction_delta).colwise() +
			(sparsity_weight * sparsity_delta)).cwiseProduct(hidden_activation.gradient(hidden_z))).eval();

		auto loss = (recontstruction_error.array().pow(2).sum() / (2 * input.cols())) +
			regularization * hidden_weights.array().pow(2).sum() +
			regularization * reconstruction_weights.array().pow(2).sum() +
			sparsity_weight * ((sparsity * (sparsity / rho_hat.array()).log()) + ((1 - sparsity) * ((1 - sparsity) / (1 - rho_hat.array())).log())).sum();

		return{ loss,
			(hidden_delta * input.transpose() / input.cols()) + regularization * 2 * hidden_weights,
			hidden_delta.rowwise().sum() / input.cols(),
			(reconstruction_delta * hidden_a.transpose() / input.cols()) + regularization * 2 * reconstruction_weights,
			reconstruction_delta.rowwise().sum() / input.cols() };
	}
}
}
}
}
#endif