/**
 * @file calc-characteristic.cpp
 *
 * @brief calculate characteristic polynomial.
 *
 * @author Mutsuo Saito (Hiroshima University)
 * @author Makoto Matsumoto (The University of Tokyo)
 *
 * Copyright (C) 2012 Mutsuo Saito, Makoto Matsumoto,
 * Hiroshima University and The University of Tokyo.
 * All rights reserved.
 *
 * The 3-clause BSD License is applied to this software, see
 * LICENSE.txt
 */
#include <iostream>
#include <fstream>
#include <inttypes.h>
#include <stdint.h>
#include <dSFMT-calc-jump.hpp>
#include <NTL/GF2X.h>
#include <NTL/vec_GF2.h>
#include <NTL/GF2XFactoring.h>

using namespace NTL;
using namespace std;
using namespace dsfmt;

static void read_file(GF2X& lcmpoly, long line_no, const string& file);

int main(int argc, char *argv[]) {
    if (argc < 2) {
	cout << argv[0] << " filename" << endl;
	return -1;
    }
    string filename = argv[1];
    GF2X poly;
    read_file(poly, 0, filename);

    vec_pair_GF2X_long factors;
    CanZass(factors, poly);
    cout << "degree = " << dec << deg(poly) << endl;
    cout << "=== factors ===" << endl;
    for (int i = 0; i < factors.length(); i++) {
	cout << factors[i].a;
	cout << ":";
	cout << dec << deg(factors[i].a);
	cout << ":";
	cout << factors[i].b << endl;
    }
    cout << "=== factors ===" << endl;
}

static void read_file(GF2X& lcmpoly, long line_no, const string& file)
{
    ifstream ifs(file.c_str());
    string line;
    for (int i = 0; i < line_no; i++) {
	ifs >> line;
	ifs >> line;
    }
    if (ifs) {
	ifs >> line;
	line = "";
	ifs >> line;
    }
    stringtopoly(lcmpoly, line);
}


// log.inl
//
// Copyright (c) 2016 Piotr K. Semenov (piotr.k.semenov at gmail dot com)
// Distributed under the New BSD License. (See accompanying file LICENSE)

/*!
 \file log.inl

 Provides CORDIC for ln function
 \ref see C. Baumann, "A simple and fast look-up table method to compute the
 exp(x) and ln(x) functions", 2004
*/

#ifndef INC_STD_LOG_INL_
#define INC_STD_LOG_INL_

#include <boost/integer/static_min_max.hpp>
#include <boost/integer/static_log2.hpp>

#include <cassert>

namespace libq {
namespace details {
/*!
 \brief
 \note
*/
template<typename T, std::size_t n, std::size_t f, int e, typename op, typename up>  // NOLINT
class log_of
    : public type_promotion_base<
        fixed_point<typename std::make_signed<T>::type, n, f, e, op, up>
        , boost::static_unsigned_max<
                      (f > 0) ? (boost::static_log2<f>::value) : 0,
                      (n > 0) ? (boost::static_log2<n>::value) : 0>::value + 1u
        , 0
        , 0> {
};
}  // namespace details
}  // namespace libq

namespace std {
template<typename T, std::size_t n, std::size_t f, int e, typename op, typename up>  // NOLINT
typename libq::details::log_of<T, n, f, e, op, up>::promoted_type
    log(libq::fixed_point<T, n, f, e, op, up> _val) {
    using Q = libq::fixed_point<T, n, f, e, op, up>;
    using log_type =
        typename libq::details::log_of<T, n, f, e, op, up>::promoted_type;
    using lut = libq::cordic::lut<f, Q>;

    assert(("[std::log] argument is negaitve", _val >= Q(0)));
    if (_val <= Q(0)) {
        throw std::logic_error("[std::log]: argument is negative");
    }

    // one need 1 bit to represent integer part of reals from [1.0, 2.0]
    using work_type = libq::UQ<f + 1u, f, 0, op, up>;

    // reduces argument to interval [1.0, 2.0]
    int power(0);
    Q arg(_val);
    while (arg >= Q(2.0)) {
        libq::lift(arg) >>= 1u;
        power++;
    }
    while (arg < Q(1.0)) {
        libq::lift(arg) <<= 1u;
        power--;
    }

    // one can consider 0 < y = log(2, x) < 1 as x = 2^y
    // so CORDIC rotation is just a multiplication by 2^{1/2^i}:
    // 2^y = 2^{a1/2} * 2^{a2/4} * ... * 2^{ai/2^i}, where ai is from
    // {0, 1}
    static libq::cordic::lut<f, Q> const inv_pow2_lut = lut::inv_pow2();

    work_type result(0);

#ifdef LOOP_UNROLLING
    auto const iteration_body = [&](std::size_t i) {  // NOLINT
#else
    for (std::size_t i = 0; i != f; ++i) {
#endif
        if (work_type(arg * inv_pow2_lut[i]) >= work_type(1.0)) {
            arg = work_type(arg * inv_pow2_lut[i]);

            libq::lift(result) +=
                typename work_type::storage_type(1u) << (f - i - 1u);
        }
    };  // NOLINT
#ifdef LOOP_UNROLLING
    libq::details::unroll(iteration_body, 0u, libq::details::loop_size<f-1>());
#endif

    log_type const r0(log_type(result) + log_type(power));
    log_type const r1(r0 * work_type::CONST_1_LOG2E);

    return r1;
}
}  // namespace std

#endif  // INC_STD_LOG_INL_


#include <iostream>
#include <cassert>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>

typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> GraphTraits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
                              boost::property<boost::edge_capacity_t, long,
                                              boost::property<boost::edge_residual_capacity_t, long,
                                                              boost::property<boost::edge_reverse_t, GraphTraits::edge_descriptor>>>>
    Graph;

const int debug_level = 0;

#define DEBUG(min_level, x)      \
  if (debug_level >= min_level)  \
  {                              \
    std::cerr << x << std::endl; \
  }

struct Limb
{
public:
  int a, b, c;

  Limb(int a, int b, int c) : a(a), b(b), c(c){};
};

class EdgeAdder
{
  Graph &G;

public:
  explicit EdgeAdder(Graph &G) : G(G) {}
  void add_edge(int from, int to, long capacity)
  {
    auto c_map = boost::get(boost::edge_capacity, G);
    auto r_map = boost::get(boost::edge_reverse, G);
    const Graph::edge_descriptor e = boost::add_edge(from, to, G).first;
    const Graph::edge_descriptor rev_e = boost::add_edge(to, from, G).first;
    c_map[e] = capacity;
    c_map[rev_e] = 0; // reverse edge has no capacity!
    r_map[e] = rev_e;
    r_map[rev_e] = e;
  }
};

void testcase()
{
  int n, m;
  std::cin >> n >> m;
  assert(n >= 2 && n <= 200 && m >= 0 && m <= 5000);

  std::vector<std::vector<int>> limb_index_by_a_b(n, std::vector<int>(n, -1));
  std::vector<Limb> limbs;
  for (int i = 0; i < m; i++)
  {
    int a, b, c;
    std::cin >> a >> b >> c;
    assert(a >= 0 && a < n && b >= 0 && b < n && a != b && c >= 1 && c <= 1000);

    int &limb_index = limb_index_by_a_b.at(a).at(b);
    if (limb_index == -1)
    {
      limb_index = limbs.size();
      limbs.emplace_back(a, b, 0);
    }

    Limb &limb = limbs.at(limb_index);
    assert(limb.a == a && limb.b == b);
    limb.c += c;
  }

  int next_free_node = 0;
  const auto get_node_for_figure = [next_free_node, n](int i) {
    assert(i >= 0 && i < n);
    return next_free_node + i;
  };
  next_free_node += n;
  const int num_nodes = next_free_node;

  Graph G(num_nodes);
  EdgeAdder adder(G);
  auto c_map = boost::get(boost::edge_capacity, G);
  auto rc_map = boost::get(boost::edge_residual_capacity, G);

  for (const Limb &limb : limbs)
  {
    adder.add_edge(get_node_for_figure(limb.a), get_node_for_figure(limb.b), limb.c);
  }

  int min_cut_cost_ever = std::numeric_limits<int>::max();
  for (int source_figure = 0; source_figure < n; source_figure++)
  {
    const int target_figure = (source_figure + 1) % n;

    const int node_source = get_node_for_figure(source_figure);
    const int node_target = get_node_for_figure(target_figure);

    boost::push_relabel_max_flow(G, node_source, node_target);

    std::vector<bool> visited_by_node(num_nodes, false);
    visited_by_node.at(node_source) = true;
    std::deque<int> queue{node_source};
    while (!queue.empty())
    {
      const int node = queue.front();
      queue.pop_front();
      for (auto it = boost::out_edges(node, G); it.first != it.second; it.first++)
      {
        const int next_node = boost::target(*it.first, G);
        if (rc_map[*it.first] > 0 && !visited_by_node.at(next_node))
        {
          visited_by_node.at(next_node) = true;
          queue.push_back(next_node);
        }
      }
    }

    int min_cut_cost = 0;
    for (int i = 0; i < num_nodes; i++)
    {
      if (!visited_by_node.at(i))
      {
        continue;
      }
      for (auto it = boost::out_edges(i, G); it.first != it.second; it.first++)
      {
        const int j = boost::target(*it.first, G);
        if (!visited_by_node.at(j))
        {
          min_cut_cost += c_map[*it.first];
        }
      }
    }
    assert(min_cut_cost >= 0);
    min_cut_cost_ever = std::min(min_cut_cost_ever, min_cut_cost);
  }

  std::cout << min_cut_cost_ever << "\n";
}

int main()
{
  std::ios_base::sync_with_stdio(false);

  int t;
  std::cin >> t;
  for (int i = 0; i < t; i++)
  {
    testcase();
  }

  return 0;
}


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

  @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)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_SINC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SINC_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!
  @ingroup group-trigonometric
    Function object implementing sinc capabilities

    Computes the sinus cardinal  value of its parameter that is  \f$sin(x)/x\f$.

    @par Semantic:

    For every parameter of floating type

    @code
    auto r = sinc(x);
    @endcode

    @see sin, sincpi, sinhc

  **/
  Value sinc(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/sinc.hpp>
#include <boost/simd/function/simd/sinc.hpp>

#endif


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

  @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)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_MODF_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_MODF_HPP_INCLUDED

#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/simd/function/trunc.hpp>
#include <boost/simd/function/std.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;

  BOOST_DISPATCH_OVERLOAD ( modf_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_<bd::arithmetic_<A0>>
                          )
  {
    using result_t = std::pair <A0, A0>;
    BOOST_FORCEINLINE result_t operator()(A0 const& a0) const
    {
      A0 rounded = bs::trunc(a0);
      A0 rest    = a0-rounded;
      return result_t(rest, rounded);
    }
   };

  BOOST_DISPATCH_OVERLOAD ( modf_
                          , (typename A0)
                          , bd::cpu_
                          , bs::std_tag
                          , bd::scalar_<bd::arithmetic_<A0>>
                          )
  {
    using result_t = std::pair <A0, A0>;
    BOOST_FORCEINLINE result_t operator()(const std_tag &,A0 const& a0) const
    {
      A0 rounded, rest;
      rest = std::modf(a0,&rounded);
      return result_t(rest, rounded);
    }
   };
} } }

#endif


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/*
 * @file testReferenceFrameFactor.cpp
 * @author Alex Cunningham
 */

#include <iostream>

#include <boost/bind.hpp>

#include <CppUnitLite/TestHarness.h>

#include <gtsam/base/Testable.h>
#include <gtsam/base/numericalDerivative.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/nonlinear/Symbol.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/NonlinearEquality.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>

#include <gtsam/slam/ReferenceFrameFactor.h>

using namespace std;
using namespace boost;
using namespace gtsam;

typedef gtsam::ReferenceFrameFactor<gtsam::Point2, gtsam::Pose2> PointReferenceFrameFactor;
typedef gtsam::ReferenceFrameFactor<gtsam::Pose2, gtsam::Pose2> PoseReferenceFrameFactor;

Key lA1 = symbol_shorthand::L(1), lA2 = symbol_shorthand::L(2), lB1 = symbol_shorthand::L(11), lB2 = symbol_shorthand::L(12);
Key tA1 = symbol_shorthand::T(1), tB1 = symbol_shorthand::T(2);

/* ************************************************************************* */
TEST( ReferenceFrameFactor, equals ) {
  PointReferenceFrameFactor
    c1(lB1, tA1, lA1),
    c2(lB1, tA1, lA1),
    c3(lB1, tA1, lA2);

  EXPECT(assert_equal(c1, c1));
  EXPECT(assert_equal(c1, c2));
  EXPECT(!c1.equals(c3));
}

/* ************************************************************************* */
LieVector evaluateError_(const PointReferenceFrameFactor& c,
    const Point2& global, const Pose2& trans, const Point2& local) {
  return LieVector(c.evaluateError(global, trans, local));
}
TEST( ReferenceFrameFactor, jacobians ) {

  // from examples below
  Point2 local(2.0, 3.0), global(-1.0, 2.0);
  Pose2 trans(1.5, 2.5, 0.3);

  PointReferenceFrameFactor tc(lA1, tA1, lB1);
  Matrix actualDT, actualDL, actualDF;
  tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);

  Matrix numericalDT, numericalDL, numericalDF;
  numericalDF = numericalDerivative31<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);
  numericalDT = numericalDerivative32<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);
  numericalDL = numericalDerivative33<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);

  EXPECT(assert_equal(numericalDF, actualDF));
  EXPECT(assert_equal(numericalDL, actualDL));
  EXPECT(assert_equal(numericalDT, actualDT));
}

/* ************************************************************************* */
TEST( ReferenceFrameFactor, jacobians_zero ) {

  // get values that are ideal
  Pose2 trans(2.0, 3.0, 0.0);
  Point2 global(5.0, 6.0);
  Point2 local = trans.transform_from(global);

  PointReferenceFrameFactor tc(lA1, tA1, lB1);
  Vector actCost = tc.evaluateError(global, trans, local),
      expCost = zero(2);
  EXPECT(assert_equal(expCost, actCost, 1e-5));

  Matrix actualDT, actualDL, actualDF;
  tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);

  Matrix numericalDT, numericalDL, numericalDF;
  numericalDF = numericalDerivative31<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);
  numericalDT = numericalDerivative32<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);
  numericalDL = numericalDerivative33<LieVector,Point2,Pose2,Point2>(
      boost::bind(evaluateError_, tc, _1, _2, _3),
      global, trans, local, 1e-5);

  EXPECT(assert_equal(numericalDF, actualDF));
  EXPECT(assert_equal(numericalDL, actualDL));
  EXPECT(assert_equal(numericalDT, actualDT));
}

/* ************************************************************************* */
TEST_UNSAFE( ReferenceFrameFactor, converge_trans ) {

  // initial points
  Point2 local1(2.0, 2.0), local2(4.0, 5.0),
      global1(-1.0, 5.0), global2(2.0, 3.0);
  Pose2 transIdeal(7.0, 3.0, M_PI/2);

  // verify direction
  EXPECT(assert_equal(local1, transIdeal.transform_from(global1)));
  EXPECT(assert_equal(local2, transIdeal.transform_from(global2)));

  // choose transform
  //  Pose2 trans = transIdeal; // ideal - works
  //  Pose2 trans = transIdeal * Pose2(0.1, 1.0, 0.00);  // translation - works
  //  Pose2 trans = transIdeal * Pose2(10.1, 1.0, 0.00);  // large translation - works
  //  Pose2 trans = transIdeal * Pose2(0.0, 0.0, 0.1);   // small rotation - works
  Pose2 trans = transIdeal * Pose2(-200.0, 100.0, 1.3); // combined - works
  //  Pose2 trans = transIdeal * Pose2(-200.0, 100.0, 2.0); // beyond pi/2 - fails

  NonlinearFactorGraph graph;
  graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
  graph.add(PointReferenceFrameFactor(lB2, tA1, lA2));

  // hard constraints on points
  double error_gain = 1000.0;
  graph.add(NonlinearEquality<gtsam::Point2>(lA1, local1, error_gain));
  graph.add(NonlinearEquality<gtsam::Point2>(lA2, local2, error_gain));
  graph.add(NonlinearEquality<gtsam::Point2>(lB1, global1, error_gain));
  graph.add(NonlinearEquality<gtsam::Point2>(lB2, global2, error_gain));

  // create initial estimate
  Values init;
  init.insert(lA1, local1);
  init.insert(lA2, local2);
  init.insert(lB1, global1);
  init.insert(lB2, global2);
  init.insert(tA1, trans);

  // optimize
  LevenbergMarquardtOptimizer solver(graph, init);
  Values actual = solver.optimize();

  Values expected;
  expected.insert(lA1, local1);
  expected.insert(lA2, local2);
  expected.insert(lB1, global1);
  expected.insert(lB2, global2);
  expected.insert(tA1, transIdeal);

  EXPECT(assert_equal(expected, actual, 1e-4));
}

/* ************************************************************************* */
TEST( ReferenceFrameFactor, converge_local ) {

  // initial points
  Point2 global(-1.0, 2.0);
  //  Pose2 trans(1.5, 2.5, 0.3); // original
  //  Pose2 trans(1.5, 2.5, 1.0); // larger rotation
  Pose2 trans(1.5, 2.5, 3.1); // significant rotation

  Point2 idealLocal = trans.transform_from(global);

  // perturb the initial estimate
  //  Point2 local = idealLocal; // Ideal case - works
  //  Point2 local = idealLocal + Point2(1.0, 0.0); // works
  Point2 local = idealLocal + Point2(-10.0, 10.0); // works

  NonlinearFactorGraph graph;
  double error_gain = 1000.0;
  graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
  graph.add(NonlinearEquality<gtsam::Point2>(lB1, global, error_gain));
  graph.add(NonlinearEquality<gtsam::Pose2>(tA1, trans, error_gain));

  // create initial estimate
  Values init;
  init.insert(lA1, local);
  init.insert(lB1, global);
  init.insert(tA1, trans);

  // optimize
  LevenbergMarquardtOptimizer solver(graph, init);
  Values actual = solver.optimize();

  CHECK(actual.exists(lA1));
  EXPECT(assert_equal(idealLocal, actual.at<Point2>(lA1), 1e-5));
}

/* ************************************************************************* */
TEST( ReferenceFrameFactor, converge_global ) {

  // initial points
  Point2 local(2.0, 3.0);
  //  Pose2 trans(1.5, 2.5, 0.3); // original
  //  Pose2 trans(1.5, 2.5, 1.0); // larger rotation
  Pose2 trans(1.5, 2.5, 3.1); // significant rotation

  Point2 idealForeign = trans.inverse().transform_from(local);

  // perturb the initial estimate
  //  Point2 global = idealForeign; // Ideal - works
  //  Point2 global = idealForeign + Point2(1.0, 0.0); // simple - works
  Point2 global = idealForeign + Point2(10.0, -10.0); // larger - works

  NonlinearFactorGraph graph;
  double error_gain = 1000.0;
  graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
  graph.add(NonlinearEquality<gtsam::Point2>(lA1, local, error_gain));
  graph.add(NonlinearEquality<gtsam::Pose2>(tA1, trans, error_gain));

  // create initial estimate
  Values init;
  init.insert(lA1, local);
  init.insert(lB1, global);
  init.insert(tA1, trans);

  // optimize
  LevenbergMarquardtOptimizer solver(graph, init);
  Values actual = solver.optimize();

  // verify
  CHECK(actual.exists(lB1));
  EXPECT(assert_equal(idealForeign, actual.at<Point2>(lB1), 1e-5));
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
/* ************************************************************************* */


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_TRACE_INCLUDE
#define MTL_TRACE_INCLUDE

#include <boost/numeric/linear_algebra/identity.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/mtl/utility/tag.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/interface/vpt.hpp>

namespace mtl { namespace mat {

template <typename Matrix>
typename Collection<Matrix>::value_type
inline trace(const Matrix& matrix)
{
	vampir_trace<3040> tracer;
    using math::zero;
    typedef typename Collection<Matrix>::value_type value_type;

    MTL_THROW_IF(num_rows(matrix) != num_cols(matrix), matrix_not_square());

    // If matrix is empty then the result is the identity from the default-constructed value
    if (num_rows(matrix) == 0) {
	value_type ref;
	return zero(ref);
    }

    value_type value= matrix[0][0];
    for (unsigned i= 1; i < num_rows(matrix); i++)
	value+= matrix[i][i];
    return value;
}

}} // namespace mtl::matrix

#endif // MTL_TRACE_INCLUDE


/*    Copyright (c) 2010-2018, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 *
 *    References
 *      Anderson Jr., J.D. , Fundamentals of Aerodynamics, 3rd edition, McGraw Hill, 2001.
 *      Anderson Jr. , J.D, Hypersonic and High-Temperature Gas Dynamics, 2nd edition,
 *          AIAA Education Series, 2006.
 *
 */

#define BOOST_TEST_MAIN

#include <cmath>

#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/unit_test.hpp>

#include "Tudat/Mathematics/BasicMathematics/mathematicalConstants.h"

#include "Tudat/Astrodynamics/Aerodynamics/aerodynamics.h"

namespace tudat
{
namespace unit_tests
{

BOOST_AUTO_TEST_SUITE( test_aerodynamics_namespace )

//! Test aerodynamic namespace pressure functions.
BOOST_AUTO_TEST_CASE( testAerodynamicNamespacePressureFunctions )
{
    using namespace tudat;
    using namespace aerodynamics;
    using mathematical_constants::PI;

    // Set default test conditions.
    const double machNumber_ = 12.0;
    const double ratioOfSpecificHeats_ = 1.4;

    // Test local to static pressure ratio.
    const double localToStaticPressureRatio_ = computeLocalToStaticPressureRatio(
           machNumber_, ratioOfSpecificHeats_ );

    const double expectedLocalToStaticPressureRatio_ = 1.0 / 0.1445e6;
    const double toleranceRatioCoefficient_ = 0.1445e6 * 1.0e-8 * 100.0
            / expectedLocalToStaticPressureRatio_;

    // Check if computed local-to-static pressure ratio matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( localToStaticPressureRatio_,
                                expectedLocalToStaticPressureRatio_,
                                toleranceRatioCoefficient_ );

    // Test stagnation pressure coefficient.
    const double stagnationPressureCoefficient_
            = computeStagnationPressure( machNumber_, ratioOfSpecificHeats_ );

    const double expectedStagnationPressureCoefficient_ = 1.83402;
    const double toleranceStagnationPressureCoefficient_ = 1.0e-5 * 100.0
            / expectedStagnationPressureCoefficient_;

    // Check if computed stagnation pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( stagnationPressureCoefficient_,
                                expectedStagnationPressureCoefficient_,
                                toleranceStagnationPressureCoefficient_ );

    // Test modified Newtonian pressure coefficient.
    const double newtonianPressureCoefficient_ = computeModifiedNewtonianPressureCoefficient(
                PI / 2.0, stagnationPressureCoefficient_ );

    const double expectedNewtonianPressureCoefficient_ = stagnationPressureCoefficient_;
    const double toleranceNewtonianPressureCoefficient_ = 1.0e-15 * 100.0
            / expectedNewtonianPressureCoefficient_;

    // Check if computed Newtonian pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( newtonianPressureCoefficient_,
                                expectedNewtonianPressureCoefficient_,
                                toleranceNewtonianPressureCoefficient_ );

    // Test empirical tangent cone pressure coefficient.
    const double empiricalTangentConePressureCoefficient_
            = computeEmpiricalTangentConePressureCoefficient( PI / 2.0, machNumber_ );

    const double expectedEmpiricalTangentConePressureCoefficient_ = 2.08961;
    const double toleranceEmpiricalTangentConePressureCoefficient_ = 1.0e-5 * 100.0
            / expectedEmpiricalTangentConePressureCoefficient_;

    // Check if computed empirical tangent cone pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( empiricalTangentConePressureCoefficient_,
                                expectedEmpiricalTangentConePressureCoefficient_,
                                toleranceEmpiricalTangentConePressureCoefficient_ );

    // Test high Mach base pressure coefficient.
    const double highMachBasePressure_ = computeHighMachBasePressure( machNumber_ );

    const double expectedHighMachBasePressure_ = -1.0 / ( machNumber_ * machNumber_ );
    const double toleranceHighMachBasePressure_ = std::fabs( 1.0e-15 * 100.0 / expectedHighMachBasePressure_ );

    // Check if computed high Mach base pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( highMachBasePressure_,
                                expectedHighMachBasePressure_,
                                toleranceHighMachBasePressure_ );

    // Test empirical tangent wedge pressure coefficient.
    const double empiricalTangentWedgePressureCoefficient_
            = computeEmpiricalTangentWedgePressureCoefficient( PI / 2.0, machNumber_ );

    const double expectedEmpiricalTangentWedgePressureCoefficient_ = 2.38867;
    const double toleranceEmpiricalTangentWedgePressureCoefficient_ = 1.0e-5 * 100.0
            / expectedEmpiricalTangentWedgePressureCoefficient_;

    // Check if computed tangent wedge pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( empiricalTangentWedgePressureCoefficient_,
                                expectedEmpiricalTangentWedgePressureCoefficient_,
                                toleranceEmpiricalTangentWedgePressureCoefficient_ );

    // Test free-stream Prandtl-Meyer function.
    const double freestreamPrandtlMeyerFunction_ = computePrandtlMeyerFunction(
           machNumber_, ratioOfSpecificHeats_ );

    const double expectedFreeStreamPrandtlMeyerFunction_ = 106.9 * PI / 180.0;
    const double toleranceFreeStreamPrandtlMeyerFunction_ = 1.0e-3 * 100.0
            / expectedFreeStreamPrandtlMeyerFunction_;

    // Check if computed free-stream Prandtl-Meyer function matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( freestreamPrandtlMeyerFunction_,
                                expectedFreeStreamPrandtlMeyerFunction_,
                                toleranceFreeStreamPrandtlMeyerFunction_ );

    // Test vacuum pressure coefficient function.
    const double vacuumPressureCoefficient_ = computeVacuumPressureCoefficient(
           machNumber_, ratioOfSpecificHeats_ );

    const double expectedVacuumPressureCoefficient_ = -2.0 / ( ratioOfSpecificHeats_
                                                               * machNumber_ * machNumber_ );
    const double toleranceVacuumPressureCoefficient_ = std::fabs( 1.0e-15 * 100.0
            / expectedVacuumPressureCoefficient_ );

    // Check if computed vacuum pressure coefficient matches expected value.
    BOOST_CHECK_CLOSE_FRACTION( vacuumPressureCoefficient_,
                                expectedVacuumPressureCoefficient_,
                                toleranceVacuumPressureCoefficient_ );
}

//! Test pressure coefficients.
BOOST_AUTO_TEST_CASE( testPressureCoefficients )
{
    using std::sin;
    using namespace tudat;
    using namespace aerodynamics;
    using mathematical_constants::PI;

    // Set default conditions.
    const double machNumber_ = 12.0;
    const double ratioOfSpecificHeats_ = 1.4;

    // Iterate over 10 angles and test pressure coefficients.
    for ( int i = 0; i < 10; i++ )
    {
         // Set angle.
         const double angle_ = static_cast< double > ( i ) * PI / 10.0;

         // Compute and compare Newtonian pressure coefficient.
         const double newtonianPressureCoefficient_
                 = computeNewtonianPressureCoefficient( angle_ );

         const double expectedNewtonianPressureCoefficient_ =  2.0 * sin( angle_ ) * sin( angle_ );
         const double toleranceNewtonianPressureCoefficient_ = 1.0e-15 * 100.0
                 / expectedNewtonianPressureCoefficient_;

         // Check if computed Newtonian pressure coefficient matches expected value.
         BOOST_CHECK_CLOSE_FRACTION( newtonianPressureCoefficient_,
                                     expectedNewtonianPressureCoefficient_,
                                     toleranceNewtonianPressureCoefficient_ );

         // Compute Prandtl-Meyer pressure coefficient and test if it is not
         // lower than vacuum pressure coefficient.
         const double freestreamPrandtlMeyerFunction_ = computePrandtlMeyerFunction(
                     machNumber_, ratioOfSpecificHeats_ );

         const double prandtlMeyerPressureCoefficient_ =
                 computePrandtlMeyerFreestreamPressureCoefficient(
                     -1.0 * angle_, machNumber_, ratioOfSpecificHeats_,
                     freestreamPrandtlMeyerFunction_ );

         const double vacuumPressureCoefficient_ = computeVacuumPressureCoefficient(
                     machNumber_, ratioOfSpecificHeats_ );

         bool isPrandtlMeyerPressureCoefficient = false;

         if ( vacuumPressureCoefficient_ < prandtlMeyerPressureCoefficient_ + 1.0e-15 )
         {
             isPrandtlMeyerPressureCoefficient = true;
         }

         // Check if Prandt-Meyer pressure coefficient is lower than vacuum pressure coefficient.
         BOOST_CHECK( isPrandtlMeyerPressureCoefficient );

         // Test shock pressure ratio.
         const double shockPressureRatio_ = computeShockPressureRatio( machNumber_,
                                                                       ratioOfSpecificHeats_ );

         const double expectedShockPressureRatio_ = 167.8;
         const double toleranceShockPressureRatio_ = 0.1 * 100.0 / expectedShockPressureRatio_;

         // Check if computed shock pressure ratio matches expected value.
         BOOST_CHECK_CLOSE_FRACTION( shockPressureRatio_,
                                     expectedShockPressureRatio_,
                                     toleranceShockPressureRatio_ );

         // Test shock density ratio.
         const double shockDensityRatio_ = computeShockDensityRatio( machNumber_,
                                                                     ratioOfSpecificHeats_ );

         const double expectedShockDensityRatio_ = 5.799;
         const double toleranceShockDensityRatio_ = 0.001 * 100 / expectedShockDensityRatio_;

         // Check if computed shock density ratio matches expected value.
         BOOST_CHECK_CLOSE_FRACTION( shockDensityRatio_,
                                     expectedShockDensityRatio_,
                                     toleranceShockDensityRatio_ );

         // Test shock temperature ratio.
         const double shockTemperatureRatio_ = computeShockTemperatureRatio(
                     machNumber_, ratioOfSpecificHeats_ );

         const double expectedShockTemperatureRatio_ = 28.94;
         const double toleranceShockTemperatureRatio_ = 0.01 * 100.0
                 / expectedShockTemperatureRatio_;

         // Check if shock temperature ratio matches expected value.
         BOOST_CHECK_CLOSE_FRACTION( shockTemperatureRatio_,
                                     expectedShockTemperatureRatio_,
                                     toleranceShockTemperatureRatio_ );

         // Test shock total pressure ratio.
         const double shockTotalPressureRatio_ = computeShockTotalPressureRatio(
                     machNumber_, ratioOfSpecificHeats_, 287.058 );

         const double expectedShockTotalPressureRatio_ = 0.001287;
         const double toleranceShockTotalPressureRatio_ = 1.0e-6 *100.0
                 / expectedShockTotalPressureRatio_;

         // Check if shock total pressure ratio matches expected value.
         BOOST_CHECK_CLOSE_FRACTION( shockTotalPressureRatio_,
                                     expectedShockTotalPressureRatio_,
                                     toleranceShockTotalPressureRatio_ );
    }
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests
} // namespace tudat


#include <CGAL/QP_models.h>
#include <CGAL/QP_functions.h>
#include <CGAL/QP_solution.h>
#include <Eigen/Dense>
#include <iostream>
#include "QPSolver.h"

// choose exact integral type
#ifdef CGAL_USE_GMP
#include <CGAL/Gmpz.h>
typedef CGAL::Gmpz ET;
#else
#include <CGAL/MP_Float.h>
typedef CGAL::MP_Float ET;
#endif

typedef CGAL::Linear_program_from_iterators
<const float**, const float*, CGAL::Comparison_result*, const bool*, const float*, const bool*, const float*, const float*> LpProgram;

typedef CGAL::Quadratic_program_from_iterators
<const float**, const float*, const CGAL::Comparison_result*, const bool*, const float*, const bool*, const float*, const float**, const float*> QpProgram;

typedef CGAL::Quadratic_program_from_iterators
<const int**, const int*, const CGAL::Comparison_result*, const bool*, const int*, const bool*, const int*, const int**, const int*> QpProgram_int;

typedef CGAL::Quadratic_program_solution<ET> Solution;

std::shared_ptr<const int*> Eigen2CgalArray2d(Eigen::MatrixXi const& eigenMat) {
	auto cgalMat = std::shared_ptr<const int*>(new const int* [eigenMat.cols()]);
	//auto cgalMat = std::make_shared<float*>(new float[eigenMat.cols()]);
	for (int i = 0; i < eigenMat.cols(); i++)
	{
		cgalMat.get()[i] = eigenMat.data() + i * eigenMat.rows();
	}
	return cgalMat;
}

std::shared_ptr<const float*> Eigen2CgalArray2d(Eigen::MatrixXf const &eigenMat) {
	auto cgalMat = std::shared_ptr<const float*>(new const float*[eigenMat.cols()]);
	//auto cgalMat = std::make_shared<float*>(new float[eigenMat.cols()]);
	for (int i = 0; i < eigenMat.cols(); i++)
	{
		cgalMat.get()[i] = eigenMat.data() + i * eigenMat.rows();
	}
	return cgalMat;
}

LinearProgram::LinearProgram(
	const Eigen::MatrixXf& A,
	const Eigen::VectorXf& b,
	const Eigen::VectorXf& c,
	const Eigen::VectorXi& equalityConditions,
	const Eigen::VectorXf& lowerbound,
	const Eigen::VectorXf& upperbound,
	float accuracy
):
	A(A),
	b(b),
	c(c),
	equalityConditions(equalityConditions),
	lowerbound(lowerbound),
	upperbound(upperbound),
	accuracy(accuracy)
{}

LinearProgram::Result LinearProgram::solve() {
	auto bound_scale_factor = 0.01f;

	Eigen::MatrixXf A_scaled = A / accuracy * bound_scale_factor;
	Eigen::VectorXf b_scaled = b / accuracy;
	Eigen::VectorXf c_scaled = c / accuracy;
	Eigen::VectorXf lb_scaled = lowerbound / bound_scale_factor;
	Eigen::VectorXf ub_scaled = upperbound / bound_scale_factor;
	auto _A_scaled = Eigen2CgalArray2d(A_scaled);

	CGAL::Comparison_result* r = new CGAL::Comparison_result[A.rows()];
	bool* flb = new bool[A.cols()];
	bool* fub = new bool[A.cols()];

	for (int i = 0; i < A.cols(); i++)
	{
		flb[i] = true;
		fub[i] = true;
	}

	for (int iCond = 0; iCond < A.rows(); iCond++)
	{
		if (equalityConditions[iCond] < 0)
			r[iCond] = CGAL::SMALLER;
		else if (equalityConditions[iCond] > 0)
			r[iCond] = CGAL::LARGER;
		else
			r[iCond] = CGAL::EQUAL;
	}
	LpProgram lp(A.cols(), A.rows(), _A_scaled.get(), b_scaled.data(), r, flb, lb_scaled.data(), fub, ub_scaled.data(), c_scaled.data(), 0.f);
	Solution s = CGAL::solve_linear_program(lp, ET());
	Eigen::VectorXf result(A.cols());
	for (auto itr = s.variable_values_begin(); itr != s.variable_values_end(); itr++)
	{
		int index = std::distance(s.variable_values_begin(), itr);
		result[index] = (*itr).numerator().to_double() / (*itr).denominator().to_double();
	}
	result *= bound_scale_factor;
	delete fub;
	delete flb;
	delete r;
	return LinearProgram::Result{
		s.objective_value().numerator().to_double() / s.objective_value().denominator().to_double() * accuracy,
		result
	};
}

QuadraticProgram::QuadraticProgram(
	const Eigen::MatrixXf& A,
	const Eigen::VectorXf& b,
	const Eigen::MatrixXf& D,
	const Eigen::VectorXf& c,
	const Eigen::VectorXi& equalityConditions,
	const Eigen::VectorXf& lowerbound,
	const Eigen::VectorXf& upperbound,
	float accuracy
) :
	A(A),
	b(b),
	D(D),
	c(c),
	equalityConditions(equalityConditions),
	lowerbound(lowerbound),
	upperbound(upperbound),
	accuracy(accuracy)
{}

QuadraticProgram::Result QuadraticProgram::solve()
{
	constexpr auto bound_scale_factor = 0.01f;
	Eigen::MatrixXf A_scaled = A / accuracy * bound_scale_factor;
	Eigen::VectorXf b_scaled = b / accuracy;
	Eigen::MatrixXf D_scaled = D / accuracy * bound_scale_factor;
	Eigen::VectorXf c_scaled = c / accuracy;
	Eigen::VectorXf lb_scaled = lowerbound / bound_scale_factor;
	Eigen::VectorXf ub_scaled = upperbound / bound_scale_factor;
	auto _A_scaled = Eigen2CgalArray2d(A_scaled);
	auto _D_scaled = Eigen2CgalArray2d(D_scaled);
	CGAL::Comparison_result* r = new CGAL::Comparison_result[A.rows()];
	for (int i_eq = 0; i_eq < A.rows(); i_eq++) {
		if (equalityConditions[i_eq] < 0) {
			r[i_eq] = CGAL::SMALLER;
		}
		else if (equalityConditions[i_eq] > 0)
			r[i_eq] = CGAL::LARGER;
		else
			r[i_eq] = CGAL::EQUAL;
	}
	bool* flb = new bool[A.cols()];
	bool* fub = new bool[A.cols()];
	for (int i = 0; i < A.cols(); i++)
	{
		flb[i] = true;
		fub[i] = true;
	}

	QpProgram qp(A.cols(), A.rows(), _A_scaled.get(), b_scaled.data(), r, flb, lb_scaled.data(), fub, ub_scaled.data(), _D_scaled.get(), c_scaled.data(), 0);
	Solution s = CGAL::solve_quadratic_program(qp, ET());
	Eigen::VectorXf result(A.cols());
	for (auto itr = s.variable_values_begin(); itr != s.variable_values_end(); itr++)
	{
		int index = std::distance(s.variable_values_begin(), itr);
		result[index] = (*itr).numerator().to_double() / (*itr).denominator().to_double();
	}
	result *= bound_scale_factor;
	delete fub;
	delete flb;
	delete r;
	return Result{
		s.objective_value().numerator().to_double() / s.objective_value().denominator().to_double() * accuracy,
		result
	};
}


//property of Alistair child
//alistair@mtoto.org

#define _USE_MATH_DEFINES

#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <typeinfo>
#include <iostream>
#include <cmath>
#include <fstream>
//#include <stdio.h>
#include <algorithm>
#include <complex>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <typeinfo>
#include <string>
//#include <iterator>
using namespace std;

void generic_args(po::options_description &desc,po::positional_options_description &p) {
    desc.add_options()
        ("help,h", "produce help message")
        ("lowfield", po::value<double>()->default_value(0), "lowest field used (default 0)")
        ("highfield", po::value<double>()->default_value(0.15), "highest field used (default 0.15)")
        ("magres", po::value<int>()->default_value(60), "n points per angle (default 100)")
        ("intres", po::value<int>()->default_value(60), "n trapezoid integral! (default 100)")
        ("length", po::value<double>()->default_value(500E-9), "length default (500E-9)")
        ("width", po::value<double>()->default_value(500E-9), "width default (500E-9)")
        ("height", po::value<double>()->default_value(1E-9), "height default (1E-9)")
        ("angleincrement", po::value<double>()->default_value(0.5), "how much to inrement angle by 0-90 (default 1).")
        ("step_height", po::value<double>()->default_value(10), "fraction of max critica current. (default 0.1)")
        ("write_to",   po::value<std::string>()->default_value("tsv"),      "csv or tsv ")
        ("distribution",   po::value<std::string>()->default_value("2d"),      "current density 1d or 2d")
        ("view",   po::value<std::string>()->default_value("critical_current"),      "profile or critical_current ")
        ("step_thickness", po::value<double>()->default_value(0.1), "fraction of length.(default 0.1) ");
    p.add("input", -1);
}

struct Results
{
    vector<vector<double> > critical_current;
    vector<vector<double> > magnetic_flux;
    vector<vector<double> > current_density_profile;
};

struct Options
{
    double lowfield;
    double highfield;
    int N;
    int n;
    double L;
    double w;
    double d;
    double angleincrement;
    double stepheight;
    double stepthickness;
    string write_to;
    string view;
    string distribution;
};

void resolvedfields(const Options &opts, double k, double theta, double field, double *Bx, double *By, double *kx, double *ky)
{

	*Bx = sin((theta * M_PI)/180)*(field);
	*By = cos((theta * M_PI)/180)*(field);
	*kx = k * *By;// + ((4*M_PI)/opts.L)* ((a / (1- (b*exp(-K * (*By - C) ) ) ) ) - (a*0.5));
	*ky = k * *Bx; //+ ((4*M_PI)/opts.w)* ((a / (1- (b*exp(-K * (*Bx - C) ) ) ) ) - (a*0.5));
}

double geometry(const Options &opts, double x, double y, double thickness, double L, double w, double height)
{
    double J0;
    if ((x <= (- L/2 + thickness) || x >= (L/2 - thickness)) && (opts.distribution == "2d"))
    {
        //J0 = height * cos((2 * M_PI * x) / L - M_PI) + height;
        J0 = height;
    }
    else if ((y <= (- w/2 + thickness) || y >= (w/2 - thickness)))
    {
        J0 = height;
        //J0 = height * cos((2 * M_PI * y) / L - M_PI) + height;
    }
    else
    {
        J0 = 0;
        //J0 = height * cos((4 * M_PI * x ) / L) + height;
    }
    return J0;
}

complex<double> current_density(const Options &opts,double Bx,double By,double x, double y, complex<double> Kx, complex<double> Ky, double thickness,double L, double w,double height)
{

    double a =100;
    double b = 0.33341;
    double C = 0.1;
    double K = 0.12089;

    double J0 = geometry(opts, x, y, thickness, L, w, height);
	return 1E7 * J0 * exp( Kx * x - Ky * y);// + ((((4*M_PI)/opts.L)* ((a / (1- (b * exp(-K * (2 - C) ) ) ) ))) - (a*0.5))+ (((4*M_PI)/opts.L)* ((a / (1- (b * exp(-K * (0 - C) ) ) ) ) - (a*0.5))));//complex exponential (Phase relations)
}

double sumdoubleintegral(const Options &opts, double Bx, double By, complex<double> Kx, complex<double> Ky,double lowbound1, double lowbound2 ,int n, double dy, double dx, double thickness,double L, double w,double height)
{
	complex<double> cumbigsum (0,0);//define and initialise a complex cumsum for outer integral

	for(int i=0; i<n ;i++)//set up for loop for outer
	{
		double xi = lowbound1 + i*dx;//define the trapezoid width
		complex<double>  cumsmallsum (0,0);//inner cumsum initialised

		for (int j=0; j<n; j++)//inner loop
		{
			double yi = lowbound2 + dy * j;//define trapeziod width
			complex<double> funvalue = current_density(opts, Bx ,By,xi, yi, Kx, Ky, thickness, L, w, height);//call function to evaluate return complex double
			complex<double> rectanglearea = funvalue * dy;//multiply width by height
			cumsmallsum += rectanglearea;//add to inner cumsum
		}

		complex<double> secondrectanglearea = cumsmallsum*dx;//use total inner cumsum as cross section
		cumbigsum += secondrectanglearea;//add up sliced area to get total
	}

	return abs(cumbigsum);//return the absolute value of complex number
}

void make_profile(const Options &opts, Results &result)
{
    double lowbound1 = -opts.L/2, lowbound2 = -opts.w/2;
	double upbound1 = opts.L/2, upbound2 = opts.w/2;
    double dx = (double)(upbound2-lowbound2)/opts.n;//dx
	double dy = (double)(upbound1-lowbound1)/opts.n;//dy

    for(int xaxis = 0; xaxis<opts.n ; ++xaxis){
        double xi = lowbound1 + xaxis * dx;
        result.current_density_profile.push_back(vector<double>());
        for(int yaxis = 0 ; yaxis<opts.n ; ++yaxis){
            double yi = lowbound2 + dy * yaxis;
            double level = geometry(opts, xi, yi, opts.stepthickness*opts.L, opts.L, opts.w, opts.stepheight);

            result.current_density_profile[xaxis].push_back(level);
        }
    }
}

void write_csv(const vector<string> &labels, const vector< vector<double> > &data)
{
    // output labels
    for(size_t i = 0; i != labels.size(); ++i)
    {
        if (i != 0) cout << ",";
        cout << labels[i];
    }
    cout << endl;

    // output data
    for(const auto& datum: data) {
        for(size_t i = 0; i != datum.size(); ++i)
        {
            if (i != 0) cout << ",";
            cout << datum[i];
        }
        cout << endl;
    }
}

void write_tsv(const vector< vector<double> > &data)
{
    // output data
    for(const auto& datum: data) {
        for(size_t i = 0; i != datum.size(); ++i)
        {
            if (i != 0) cout << "\t";
            cout << datum[i];
        }
        cout << endl;
    }
}

void file_output(const Options &opts, Results &result)
{
    if (opts.view == "profile")
    {
        for(int xaxis = 0; xaxis < opts.n; ++xaxis)
	    {
	        for (int yaxis = 0; yaxis < opts.n; ++yaxis)
	        {
                if (opts.write_to == "csv"){
        	        cout << xaxis << "," << yaxis << "," << result.current_density_profile[xaxis][yaxis]  << endl;
	            }
                else if(opts.write_to == "tsv"){
                    cout << xaxis << "\t" << yaxis << "\t" << result.current_density_profile[xaxis][yaxis]  << endl;
                }
            }
	    }
    }
    else if (opts.view == "critical_current")
    {
        for(int angle = 0; angle <= 90/opts.angleincrement; ++angle)
	    {
            double max = *max_element(result.critical_current[angle].begin(), result.critical_current[angle].end());
            for (int flux=0; flux < opts.N; ++flux)
	        {
                if(opts.write_to == "csv"){
        	        cout << angle*opts.angleincrement << "," << result.magnetic_flux[angle][flux] << "," << result.critical_current[angle][flux]/max << endl;
	            }
                else if(opts.write_to == "tsv"){
                    cout << angle*opts.angleincrement << "\t" << result.magnetic_flux[angle][flux]<< "\t" << result.critical_current[angle][flux]/max << endl;
                }
            }
	    }
    }
    else
    {
        cerr << "Error: i do not understand the option view. Please choose from the view options --help for info" << endl;
    }

}

void getargs(Options &opts, int ac, const char * av[])
{
    //Options opts;
    try {
        po::options_description desc("Allowed options");
        po::positional_options_description p;
        generic_args(desc, p);//calling function
        po::variables_map param;
        po::store(po::command_line_parser(ac, av).options(desc).positional(p).run(), param);
        po::notify(param);

        if (param.count("help")) {
            cout << desc << "\n";
            exit(0);
        }

        opts.lowfield = param["lowfield"].as<double>();
        opts.highfield = param["highfield"].as<double>();
        opts.N = param["magres"].as<int>();
        opts.n = param["intres"].as<int>();
        opts.L = param["length"].as<double>();
        opts.w = param["width"].as<double>();
        opts.d = param["height"].as<double>();
        opts.stepheight = param["step_height"].as<double>();
        opts.stepthickness = param["step_thickness"].as<double>();
        opts.angleincrement = param["angleincrement"].as<double>();
        opts.write_to = param["write_to"].as<string>();
        opts.view = param["view"].as<string>();
        opts.distribution = param["distribution"].as<string>();
    }
    catch(exception &e) {
        cerr << "error: " << e.what() << "\n";
        throw e;
    }
}

void make_results(const Options &opts, Results &result)
{
    //define Physics constants to double precision!
    double fluxquantum = 2.06783383E-15, lambdax = 90E-9;
	complex <double> im(0,1);
    double k = 2 * M_PI * (2 * lambdax + opts.d) / fluxquantum;

    //define the upper/lower bounds of double integral
    double lowbound1 = -opts.L/2, lowbound2 = -opts.w/2;
	double upbound1 = opts.L/2, upbound2 = opts.w/2;
    double dx = (double)(upbound2-lowbound2)/opts.n;//dx
	double dy = (double)(upbound1-lowbound1)/opts.n;//dy
	double step = (opts.highfield - opts.lowfield) / (opts.N-1);
	int angleblock = 90 / opts.angleincrement;

    //create the data.

    for (int angle = 0; angle <= angleblock; ++angle)
	{

        //local values set to global values.
		float loc_lowfield = opts.lowfield;
		float loc_highfield = opts.highfield;
		double loc_step = step;

        result.critical_current.push_back(vector<double>());
        result.magnetic_flux.push_back(vector<double>());

		for (int flux = 0; flux < opts.N; ++flux)
		{
			float field = loc_lowfield;
			loc_lowfield += loc_step;
			//int theta = angle * opts.angleincrement;
            double theta = angle * opts.angleincrement;

			double Bx, By ,kx, ky;
			resolvedfields(opts, k, theta, field, &Bx, &By , &kx , &ky);

            double appendff = sumdoubleintegral(opts, Bx, By,kx * im, ky * im, lowbound1, lowbound2, opts.n, dy, dx,opts.stepthickness*opts.L,opts.L, opts.w, opts.stepheight);
            double mflux =( ( ( (Bx * opts.w) + (By * opts.L) ) * (2 * lambdax + opts.d))  / fluxquantum );//+ ( ((opts.L)* ((a / (1- (b*exp(-K * (2- C) ) ) ) ) - (a*0.5))) / fluxquantum )+ ( ((1)* ((a / (1- (b*exp(-K * (2 - C) ) ) ) ) - (a*0.5))) / fluxquantum );
            result.critical_current[angle].push_back(appendff);
            result.magnetic_flux[angle].push_back(mflux);
		}
	}

}

int main(int ac, const char* av[])
{
    //Options opts;
    try {
        //Define the user options
        Options opts;

        //parse arguments
        getargs(opts, ac, av);

        //Results(opts) output defined;
        Results result;

        //create results call Math
        make_results(opts, result);

        //make the current density profile
        make_profile(opts, result);

        //write results to a file csv, tsv, 1d 2d.
        file_output(opts, result);
    }
    catch(exception &e) {
        cerr << "error: " << e.what() << "\n";
        return 1;
    }

    return 0;
}


// Copyright (c) 2020, Ryohei Sasaki
// All rights reserved.
//
// Software License Agreement (BSD License 2.0)
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//  * Neither the name of {copyright_holder} nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef KALMAN_FILTER_LOCALIZATION__EKF_HPP_
#define KALMAN_FILTER_LOCALIZATION__EKF_HPP_

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <iostream>

class EKFEstimator
{
public:
  EKFEstimator()
  : P_(EigenMatrix9d::Identity() * 100),
    var_imu_w_{0.33},
    var_imu_acc_{0.33},
    tau_gyro_bias_{1.0}
  {
    /* x  = [p v q] = [x y z vx vy vz qx qy qz qw] */
    x_ << 0, 0, 0, 0, 0, 0, 0, 0, 0, 1;
  }

/* state
* x  = [p v q] = [x y z vx vy vz qx qy qz qw]
* dx = [dp dv dth] = [dx dy dz dvx dvy dvz dthx dthy dthz]
*
* pos_k = pos_{k-1} + vel_k * dt + (1/2) * (Rot(q_{k-1}) acc_{k-1}^{imu} - g) *dt^2
* vel_k = vel_{k-1} + (Rot(quat_{k-1})) acc_{k-1}^{imu} - g) *dt
* quat_k = Rot(w_{k-1}^{imu}*dt)*quat_{k-1}
*
* covariance
* P_{k} = F_k P_{k-1} F_k^T + L Q_k L^T
*/
  void predictionUpdate(
    const double current_time_imu,
    const Eigen::Vector3d & gyro,
    const Eigen::Vector3d & linear_acceleration
  )
  {
    double dt_imu = current_time_imu - previous_time_imu_;
    previous_time_imu_ = current_time_imu;
    if (dt_imu > 0.5 /* [sec] */) {
      std::cout << "imu time interval is too large" << std::endl;
      return;
    }

    Eigen::Quaterniond quat_wdt = Eigen::Quaterniond(
      Eigen::AngleAxisd(gyro.x() * dt_imu, Eigen::Vector3d::UnitX()) *
      Eigen::AngleAxisd(gyro.y() * dt_imu, Eigen::Vector3d::UnitY()) *
      Eigen::AngleAxisd(gyro.z() * dt_imu, Eigen::Vector3d::UnitZ()));
    Eigen::Vector3d acc = Eigen::Vector3d(
      linear_acceleration.x(),
      linear_acceleration.y(),
      linear_acceleration.z());

    // state
    Eigen::Quaterniond previous_quat =
      Eigen::Quaterniond(x_(STATE::QW), x_(STATE::QX), x_(STATE::QY), x_(STATE::QZ));
    Eigen::MatrixXd rot_mat = previous_quat.toRotationMatrix();

    // pos
    x_.segment(STATE::X, 3) = x_.segment(STATE::X, 3) + dt_imu * x_.segment(STATE::VX, 3) +
      0.5 * dt_imu * dt_imu * (rot_mat * acc - gravity_);
    // vel
    x_.segment(STATE::VX, 3) = x_.segment(STATE::VX, 3) + dt_imu * (rot_mat * acc - gravity_);
    // quat
    Eigen::Quaterniond predicted_quat = quat_wdt * previous_quat;
    x_.segment(STATE::QX, 4) = Eigen::Vector4d(
      predicted_quat.x(), predicted_quat.y(), predicted_quat.z(), predicted_quat.w());

    // F
    Eigen::MatrixXd F = EigenMatrix9d::Identity();
    F.block<3, 3>(0, 3) = dt_imu * Eigen::Matrix3d::Identity();
    Eigen::Matrix3d acc_skew;
    acc_skew <<
      0, -acc(2), acc(1),
      acc(2), 0, -acc(0),
      -acc(1), acc(0), 0;
    F.block<3, 3>(3, 6) = rot_mat * (-acc_skew) * dt_imu;

    // Q
    Eigen::MatrixXd Q = Eigen::Matrix<double, 6, 6>::Identity();
    Q.block<3, 3>(0, 0) = var_imu_acc_ * Q.block<3, 3>(0, 0);
    Q.block<3, 3>(3, 3) = var_imu_w_ * Q.block<3, 3>(3, 3);
    Q = Q * (dt_imu * dt_imu);

    // L
    Eigen::MatrixXd L = Eigen::Matrix<double, num_error_state_, 6>::Zero();
    L.block<3, 3>(3, 0) = Eigen::Matrix3d::Identity();
    L.block<3, 3>(6, 3) = Eigen::Matrix3d::Identity();

    P_ = F * P_ * F.transpose() + L * Q * L.transpose();
  }

/*
* y = pobs = [xobs yobs zobs]
*
* K = P_k H^T (H P_k H^T + R)^{-1}
*
* dx = K (y_k - p_k )
*
* p_x = p_{k-1} + dp_k
* v_k = v_{k-1} + dv_k
* q_k = Rot(dth) q_{k-1}
*
* P_k = (I - KH)*P_{k-1}
*/
  void observationUpdate(
    const Eigen::Vector3d & y,
    const Eigen::Vector3d & variance
  )
  {
    // error state
    Eigen::Matrix3d R;
    R <<
      variance.x(), 0, 0,
      0, variance.y(), 0,
      0, 0, variance.z();
    Eigen::MatrixXd H = Eigen::Matrix<double, 3, num_error_state_>::Zero();
    H.block<3, 3>(0, 0) = Eigen::Matrix3d::Identity();
    Eigen::MatrixXd K = P_ * H.transpose() * (H * P_ * H.transpose() + R).inverse();
    Eigen::VectorXd dx = K * (y - x_.segment(STATE::X, 3));

    // state
    x_.segment(STATE::X, 3) = x_.segment(STATE::X, 3) + dx.segment(ERROR_STATE::DX, 3);
    x_.segment(STATE::VX, 3) = x_.segment(STATE::VX, 3) + dx.segment(ERROR_STATE::DVX, 3);
    double norm_quat = sqrt(
      pow(dx(ERROR_STATE::DTHX), 2) +
      pow(dx(ERROR_STATE::DTHY), 2) +
      pow(dx(ERROR_STATE::DTHZ), 2));

    if (norm_quat < 1e-10) {
      Eigen::Quaterniond dq = Eigen::Quaterniond(0, 0, 0, cos(norm_quat / 2));
      Eigen::Quaterniond q = Eigen::Quaterniond(x_(STATE::QW), x_(STATE::QX), x_(STATE::QY), x_(STATE::QZ));
      Eigen::Quaterniond q_new = q * dq;
      x_.segment(STATE::QX, 4) = Eigen::Vector4d(q_new.x(), q_new.y(), q_new.z(), q_new.w());
    } else {
      Eigen::Quaterniond dq = Eigen::Quaterniond(
        sin(norm_quat / 2) * dx(ERROR_STATE::DTHX) / norm_quat,
        sin(norm_quat / 2) * dx(ERROR_STATE::DTHY) / norm_quat,
        sin(norm_quat / 2) * dx(ERROR_STATE::DTHZ) / norm_quat,
        cos(norm_quat / 2));
      Eigen::Quaterniond q = Eigen::Quaterniond(x_(STATE::QW), x_(STATE::QX), x_(STATE::QY), x_(STATE::QZ));
      Eigen::Quaterniond q_new = q * dq;
      x_.segment(STATE::QX, 4) = Eigen::Vector4d(q_new.x(), q_new.y(), q_new.z(), q_new.w());
    }

    P_ = (EigenMatrix9d::Identity() - K * H) * P_;
  }

  void setTauGyroBias(const double tau_gyro_bias)
  {
    tau_gyro_bias_ = tau_gyro_bias;
  }

  void setVarImuGyro(const double var_imu_w)
  {
    var_imu_w_ = var_imu_w;
  }

  void setVarImuAcc(const double var_imu_acc)
  {
    var_imu_acc_ = var_imu_acc;
  }

  void setInitialX(Eigen::VectorXd x)
  {
    x_ = x;
  }

  Eigen::VectorXd getX()
  {
    return x_;
  }

  Eigen::MatrixXd getCoveriance()
  {
    return P_;
  }

  int getNumState()
  {
    return num_state_;
  }

private:
  double previous_time_imu_;
  double var_imu_w_;
  double var_imu_acc_;

  static const int num_state_{10};
  static const int num_error_state_{9};

  typedef Eigen::Matrix<double, num_error_state_, num_error_state_> EigenMatrix9d;

  Eigen::Matrix<double, num_state_, 1> x_;
  EigenMatrix9d P_;

  const Eigen::Vector3d gravity_{0, 0, 9.80665};

  double tau_gyro_bias_;

  enum STATE
  {
    X  = 0, Y = 1, Z = 2,
    VX = 3, VY = 4, VZ = 5,
    QX = 6, QY = 7, QZ = 8, QW = 9,
  };
  enum ERROR_STATE
  {
    DX   = 0, DY = 1, DZ = 2,
    DVX  = 3, DVY = 4, DVZ = 5,
    DTHX = 6, DTHY = 7, DTHZ = 8,
  };
};

#endif  // KALMAN_FILTER_LOCALIZATION__EKF_HPP_


#include <iostream>
#include <Eigen/Dense>
#include <Eigen/MatrixFunctions>
#include "DIIS.hh"

void DIIS::step(Eigen::MatrixXd& F, const Eigen::MatrixXd& D,
	const Eigen::MatrixXd& S, const Eigen::MatrixXd& X, double Etot)
{
	if (_err_vecs_used >= _err_vecs.cols())
	{
		if (_err_vecs_used == 0)
		{
			_size = F.rows();
			_err_vecs.resize((_size-1)*_size/2, 10);
		}
		else
		{
			Eigen::MatrixXd new_err_vecs(_err_vecs.rows(), 2*_err_vecs.cols());
			new_err_vecs.leftCols(_err_vecs_used) = _err_vecs;
			_err_vecs.swap(new_err_vecs);
		}
	}

        // The error matrix is X(FDS - SDF)X = XFDX^-1 - h.c, which since S, P,
        // F and X are all symmetric, is antisymmetric. Therefore we only store
        // the upper triangle in vector err.
	Eigen::MatrixXd FDS = X*F*D*S*X;
	int idx = 0;
	for (int i = 1; i < _size; ++i)
		for (int j = 0; j < i; ++j, ++idx)
			_err_vecs(idx, _err_vecs_used) = FDS(i,j) - FDS(j,i);

	_max_err = _err_vecs.col(_err_vecs_used).lpNorm<Eigen::Infinity>();
	if (_max_err > std::abs(0.5*Etot))
		// This Fock matrix will not have a meaningful contribution
		// to convergence. Ignore it.
		return;

	++_err_vecs_used;
	_values.push_back(F);
	if (!_started)
	{
		if (_max_err >= std::abs(0.1*Etot))
			return;
		std::cout << "Starting DIIS\n";
		_started = true;
	}

	auto e = _err_vecs.leftCols(_err_vecs_used);
	Eigen::MatrixXd B(_err_vecs_used+1, _err_vecs_used+1);
	B.topLeftCorner(_err_vecs_used, _err_vecs_used) = e.transpose() * e * 2;
	B.rightCols(1).fill(-1);
	B.bottomRows(1).fill(-1);

	Eigen::VectorXd y = Eigen::VectorXd::Zero(_err_vecs_used+1);
	y[_err_vecs_used] = -1;

	Eigen::VectorXd coefs = B.colPivHouseholderQr().solve(y);
	F *= coefs[_err_vecs_used-1];
	for (int i = 0; i < _err_vecs_used-1; ++i)
		F += coefs[i] * _values[i];
}

void DIIS::step(Eigen::MatrixXd& Fa, const Eigen::MatrixXd& Da,
	Eigen::MatrixXd& Fb, const Eigen::MatrixXd& Db,
	const Eigen::MatrixXd& S, const Eigen::MatrixXd& X, double Etot)
{
	if (_err_vecs_used >= _err_vecs.cols())
	{
		if (_err_vecs_used == 0)
		{
			_size = Fa.rows();
			_err_vecs.resize((_size-1)*_size, 10);
		}
		else
		{
			Eigen::MatrixXd new_err_vecs(_err_vecs.rows(), 2*_err_vecs.cols());
			new_err_vecs.leftCols(_err_vecs_used) = _err_vecs;
			_err_vecs.swap(new_err_vecs);
		}
	}

	Eigen::MatrixXd FDS = X*Fa*Da*S*X;
	int idx = 0;
	for (int i = 1; i < _size; ++i)
		for (int j = 0; j < i; ++j, ++idx)
			_err_vecs(idx, _err_vecs_used) = FDS(i,j) - FDS(j,i);
	FDS = X*Fb*Db*S*X;
	for (int i = 1; i < _size; ++i)
		for (int j = 0; j < i; ++j, ++idx)
			_err_vecs(idx, _err_vecs_used) = FDS(i,j) - FDS(j,i);

	_max_err = _err_vecs.col(_err_vecs_used).lpNorm<Eigen::Infinity>();
	if (_max_err > std::abs(0.5*Etot))
		// This Fock matrix will not have a meaningful contribution
		// to convergence. Ignore it.
		return;

	++_err_vecs_used;
	Eigen::MatrixXd F(_size, 2*_size);
	F.leftCols(_size) = Fa;
	F.rightCols(_size) = Fb;
	_values.push_back(F);
	if (!_started)
	{
		if (_max_err >= std::abs(0.1*Etot))
			return;
		std::cout << "Starting DIIS\n";
		_started = true;
	}

	auto e = _err_vecs.leftCols(_err_vecs_used);
	Eigen::MatrixXd B(_err_vecs_used+1, _err_vecs_used+1);
	B.topLeftCorner(_err_vecs_used, _err_vecs_used) = e.transpose() * e * 2;
	B.rightCols(1).fill(-1);
	B.bottomRows(1).fill(-1);

	Eigen::VectorXd y = Eigen::VectorXd::Zero(_err_vecs_used+1);
	y[_err_vecs_used] = -1;

	Eigen::VectorXd coefs = B.colPivHouseholderQr().solve(y);
	F *= coefs[_err_vecs_used-1];
	for (int i = 0; i < _err_vecs_used-1; ++i)
		F += coefs[i] * _values[i];

	Fa = F.leftCols(_size);
	Fb = F.rightCols(_size);
}


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

#include <votca/ctp/overlap.h>
#include <votca/ctp/linalg.h>

#include <votca/tools/constants.h>
#include <boost/format.hpp>
#include <boost/progress.hpp>

namespace votca { namespace ctp {

namespace ub = boost::numeric::ublas;

double inv_sqrt(double x) { return 1./sqrt(x); }
using boost::format;

/*
 * Calculates S^{-1/2}
 */
void Overlap::SQRTOverlap(ub::symmetric_matrix<double> &S,
                          ub::matrix<double> &S2 ) {

    double (*_inv_sqrt)(double);
    _inv_sqrt = &inv_sqrt;

    ub::vector<double>                  _eigenvalues;
    ub::matrix<double>                  _eigenvectors;

    int _size = S.size1();

    _eigenvalues.resize( _size );
    _eigenvectors.resize( _size, _size );

    //convert from symmetric to matrix for faster evaluation
    ub::matrix<double> temp=S;

    // TEST CASE FOR EIGEN
       ub::matrix<double> m(3,3);
       /*
       m(0,0) = -2;
       m(0,1) = -4;
       m(0,2) =  2;
       m(1,0) = -2;
       m(1,1) =  1;
       m(1,2) =  2;
       m(2,0) =  4;
       m(2,1) =  2;
       m(2,2) =  5;
       */

       /* 4 -2 -2
       m(0,0) =  1;
       m(0,1) = -3;
       m(0,2) =  3;
       m(1,0) =  3;
       m(1,1) = -5;
       m(1,2) =  3;
       m(2,0) =  6;
       m(2,1) = -6;
       m(2,2) =  4;
       */

       /*symmetric, -1, -1, 8
       m(0,0) =    2;
       m(0,1) =    1;
       m(0,2) =    -2;
       m(1,0) =    1;
       m(1,1) =    0;
       m(1,2) =    1;
       m(2,0) =    -2;
       m(2,1) =    1;
       m(2,2) =    0;

    _eigenvalues.resize( 3 );
    _eigenvectors.resize( 3, 3 );

    linalg::eigenvalues_symm(m, _eigenvalues, _eigenvectors);

    std::cout << "\nEigenvalues" << std::endl << _eigenvalues;
    std::cout << "\nEigenvectors" << std::endl << _eigenvectors << std::endl;
    exit(0);
    */

    linalg::eigenvalues_symm(temp, _eigenvalues, _eigenvectors);

    // compute inverse sqrt of all eigenvalues
    std::transform(_eigenvalues.begin(), _eigenvalues.end(), _eigenvalues.begin(),  _inv_sqrt );

    // form a diagonal matrix S^{-1/2}
    ub::diagonal_matrix<double> _diagS2( _eigenvalues.size(), _eigenvalues.data() );

    // multiply from the left on the U
    ub::matrix<double> _temp = ub::prod( _eigenvectors, _diagS2 );

    // multiply from the right on the transpose U
    S2 = ub::prod( _temp, ub::trans( _eigenvectors ) );
    return;
 }

double Overlap::getCouplingElement( int levelA, int levelB,  Orbitals* _orbitalsA,
    Orbitals* _orbitalsB, ub::matrix<double>* _JAB, double  _energy_difference ) {

    int _levelsA = _orbitalsA->getNumberOfLevels();

    if ( _energy_difference != 0 ) {
        std::vector<int> list_levelsA = *_orbitalsA->getDegeneracy( levelA, _energy_difference );
        std::vector<int> list_levelsB = *_orbitalsA->getDegeneracy( levelB, _energy_difference );

        double _JAB_sq = 0; double _JAB_one_level;

        for (std::vector<int>::iterator iA = list_levelsA.begin()++; iA != list_levelsA.end(); iA++) {
                for (std::vector<int>::iterator iB = list_levelsB.begin()++; iB != list_levelsB.end(); iB++) {
                    //cout << *iA << ':' << *iB << endl;
                    _JAB_one_level = _JAB->at_element( *iA - 1  , *iB -1 + _levelsA );
                    _JAB_sq +=  _JAB_one_level*_JAB_one_level ;
                }
        }

        return sqrt(_JAB_sq / ( list_levelsA.size() * list_levelsB.size() ) ) * tools::conv::hrt2ev ;

    } else {

        return _JAB->at_element( levelA - 1  , levelB -1 + _levelsA ) * tools::conv::hrt2ev;

    }
    // the  matrix should be symmetric, could also return this element
    // _JAB.at_element( _levelsA + levelB - 1  , levelA - 1 );
}

/**
 * \brief evaluates electronic couplings
 *
 * This is a slow version with a rather large block matrix AxB
 *
 * @param _orbitalsA molecular orbitals of molecule A
 * @param _orbitalsB molecular orbitals of molecule B
 * @param _orbitalsAB molecular orbitals of the dimer AB
 * @param _JAB matrix with electronic couplings
 * @return false if failed
 */
bool Overlap::CalculateIntegrals(Orbitals* _orbitalsA, Orbitals* _orbitalsB,
    Orbitals* _orbitalsAB, ub::matrix<double>* _JAB) {

    CTP_LOG(logDEBUG,*_pLog) << "Calculating electronic couplings" << std::flush;

    const std::vector<QMAtom*> atomsA=_orbitalsA->QMAtoms();
    const std::vector<QMAtom*> atomsB=_orbitalsB->QMAtoms();
    const std::vector<QMAtom*> atomsAB=_orbitalsAB->QMAtoms();

  for (unsigned i=0;i<atomsAB.size();i++){
        QMAtom* dimer=atomsAB[i];
        QMAtom* monomer=NULL;
        if (i<atomsA.size()){
            monomer=atomsA[i];
        }
        else if (i<atomsB.size()+atomsA.size() ){
            monomer=atomsB[i-atomsA.size()];
        }
        else{
	  throw std::runtime_error((format("Number of Atoms in dimer %3i and the two monomers A:%3i B:%3i does not agree") %atomsAB.size() %atomsA.size() %atomsB.size()).str());
        }

        if(monomer->type != dimer->type){
	  throw std::runtime_error("\nERROR: Atom types do not agree in dimer and monomers\n");
        }
        if(std::abs(monomer->x-dimer->x)>0.001 || std::abs(monomer->y-dimer->y)>0.001 || std::abs(monomer->z-dimer->z)>0.001){
            CTP_LOG(logERROR,*_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << std::flush;
            break;
        }

    }

    // constructing the direct product orbA x orbB
    int _basisA = _orbitalsA->getBasisSetSize();
    int _basisB = _orbitalsB->getBasisSetSize();

    if ( ( _basisA == 0 ) || ( _basisB == 0 ) ) {
        CTP_LOG(logERROR,*_pLog) << "Basis set size is not stored in monomers" << std::flush;
        return false;
    }

    int _levelsA = _orbitalsA->getNumberOfLevels();
    int _levelsB = _orbitalsB->getNumberOfLevels();

    //boost::timer t; // start timing
    //double _st = t.elapsed();

    CTP_LOG(logDEBUG,*_pLog) << "Levels:Basis A[" << _levelsA << ":" << _basisA << "]"
                                     << " B[" << _levelsB << ":" << _basisB << "]" << std::flush;

    if ( ( _levelsA == 0 ) || (_levelsB == 0) ) {
        CTP_LOG(logERROR,*_pLog) << "No information about number of occupied/unoccupied levels is stored" << std::flush;
        return false;
    }

    //       | Orbitals_A          0 |      | Overlap_A |
    //       | 0          Orbitals_B |  X   | Overlap_B |  X  Transpose( Orbitals_AB )
    ub::zero_matrix<double> zeroB( _levelsA, _basisB ) ;
    ub::zero_matrix<double> zeroA( _levelsB, _basisA ) ;
    ub::matrix<double> _psi_AxB ( _levelsA + _levelsB, _basisA + _basisB  );

    CTP_LOG(logDEBUG,*_pLog) << "BOOST: Constructing direct product AxB ["
            << _psi_AxB.size1() << "x"
            << _psi_AxB.size2() << "]" << std::flush;

/*
    // TEST CASE FOR EIGEN
       ub::matrix<double> m1_(2,3);
       m1_(0,0) = 1;
       m1_(0,1) = 2;
       m1_(0,2) = 3;
       m1_(1,0) = 4;
       m1_(1,1) = 5;
       m1_(1,2) = 6;
       //std::cout << std::endl << m1_ << std::endl ;

       ub::matrix<double> m2_(3,2);
       m2_(0,0) = 7;
       m2_(0,1) = 8;
       m2_(1,0) = 9;
       m2_(1,1) = 10;
       m2_(2,0) = 11;
       m2_(2,1) = 12;
       //std::cout << std::endl << m2_ << std::endl ;

       ub::prod(m1_, m2_);
 */

    ub::project( _psi_AxB, ub::range (0, _levelsA ), ub::range ( _basisA, _basisA +_basisB ) ) = zeroB;
    ub::project( _psi_AxB, ub::range (_levelsA, _levelsA + _levelsB ), ub::range ( 0, _basisA ) ) = zeroA;
    ub::project( _psi_AxB, ub::range (0, _levelsA ), ub::range ( 0, _basisA ) ) = *_orbitalsA->getOrbitals();
    ub::project( _psi_AxB, ub::range (_levelsA, _levelsA + _levelsB ), ub::range ( _basisA, _basisA + _basisB ) ) = *_orbitalsB->getOrbitals();

    // psi_AxB * S_AB * psi_AB
    CTP_LOG(logDEBUG,*_pLog) << "Projecting dimer onto monomer orbitals" << std::flush;
    ub::matrix<double> _orbitalsAB_Transposed = ub::trans( *_orbitalsAB->getOrbitals() );
    if ( (*_orbitalsAB->getOverlap()).size1() == 0 ) {
            CTP_LOG(logERROR,*_pLog) << "Overlap matrix is not stored";
            return false;
    }

    ub::matrix<double> _psi_AB = ub::prod( *_orbitalsAB->getOverlap(), _orbitalsAB_Transposed );
    ub::matrix<double> _psi_AxB_dimer_basis = ub::prod( _psi_AxB, _psi_AB );
    _psi_AB.clear();

    //check to see if projection quality is sufficient
    for (unsigned i=0;i<_psi_AxB_dimer_basis.size1();i++){
        double mag=0.0;
        for (unsigned j=0;j<_psi_AxB_dimer_basis.size2();j++){
            mag+=_psi_AxB_dimer_basis(i,j)*_psi_AxB_dimer_basis(i,j);

    }
        if (mag<0.95){
	  throw std::runtime_error("\nERROR: Projection of monomer orbitals on dimer is insufficient, increase dimer basis.\n");
        }
    }

    // J = psi_AxB_dimer_basis * FAB * psi_AxB_dimer_basis^T
    CTP_LOG(logDEBUG,*_pLog) << "Projecting the Fock matrix onto the dimer basis" << std::flush;
    ub::diagonal_matrix<double> _fock_AB( _orbitalsAB->getNumberOfLevels(), (*_orbitalsAB->getEnergies()).data() );
    ub::matrix<double> _temp = ub::prod( _fock_AB, ub::trans( _psi_AxB_dimer_basis ) ) ;
    ub::matrix<double> JAB_dimer = ub::prod( _psi_AxB_dimer_basis, _temp);

    // S = psi_AxB_dimer_basis * psi_AxB_dimer_basis^T
    CTP_LOG(logDEBUG,*_pLog) << "Constructing Overlap matrix" << std::flush;
    ub::symmetric_matrix<double> _S_AxB = ub::prod( _psi_AxB_dimer_basis, ub::trans( _psi_AxB_dimer_basis ));
    ub::matrix<double> _S_AxB_2(_S_AxB.size1(), _S_AxB.size1() );
    ub::trans( _S_AxB );

    // Square root of the overlap matrix
    CTP_LOG(logDEBUG,*_pLog) << "Calculating square root of the overlap matrix" << std::flush;
    SQRTOverlap( _S_AxB , _S_AxB_2 );

   CTP_LOG(logDEBUG,*_pLog) << "Calculating the effective overlap JAB ["
              << JAB_dimer.size1() << "x"
              << JAB_dimer.size2() << "]" << std::flush;

    ub::matrix<double> JAB_temp( _levelsA + _levelsB, _levelsA + _levelsB );
    ub::noalias(JAB_temp) = ub::prod( JAB_dimer, _S_AxB_2 );
    (*_JAB) = ub::prod( _S_AxB_2, JAB_temp );

    CTP_LOG(logDEBUG,*_pLog) << "Done with electronic couplings" << std::flush;
    return true;

}

/**
 * \brief evaluates electronic couplings
 *
 * This is a different version with block matrices [slower]
 *
 * @param _orbitalsA molecular orbitals of molecule A
 * @param _orbitalsB molecular orbitals of molecule B
 * @param _orbitalsAB molecular orbitals of the dimer AB
 * @param _JAB matrix with electronic couplings
 * @return false if failed
 */
bool Overlap::CalculateIntegralsOptimized(Orbitals* _orbitalsA, Orbitals* _orbitalsB,
    Orbitals* _orbitalsAB, ub::matrix<double>* _JAB) {

    CTP_LOG(logDEBUG,*_pLog) << "Calculating electronic couplings" << std::flush;

    // constructing the direct product orbA x orbB
    int _basisA = _orbitalsA->getBasisSetSize();
    int _basisB = _orbitalsB->getBasisSetSize();

    if ( ( _basisA == 0 ) || ( _basisB == 0 ) ) {
        CTP_LOG(logERROR,*_pLog) << "Basis set size is not stored in monomers" << std::flush;
        return false;
    }

    int _levelsA = _orbitalsA->getNumberOfLevels();
    int _levelsB = _orbitalsB->getNumberOfLevels();

    boost::timer t; // start timing
    double _st = t.elapsed();

    CTP_LOG(logDEBUG,*_pLog) << "Levels:Basis A[" << _levelsA << ":" << _basisA << "]"
                                     << " B[" << _levelsB << ":" << _basisB << "]" << std::flush;

    if ( ( _levelsA == 0 ) || (_levelsB == 0) ) {
        CTP_LOG(logERROR,*_pLog) << "No information about number of occupied/unoccupied levels is stored" << std::flush;
        return false;
    }

    // these flags should be set before any ublas header is called
    // #define NDEBUG
    // otherwise the code is very inefficient

    //       | Orbitals_A          0 |      | Overlap_A |
    //       | 0          Orbitals_B |  X   | Overlap_B |  X  Transpose( Orbitals_AB )
    //constructing a slice of the Overlap matrix
    ub::matrix_range< ub::symmetric_matrix<double> > Overlap_A = ub::project( *_orbitalsAB->getOverlap(), ub::range ( 0, _basisA), ub::range (0, _basisA +_basisB) );
    ub::matrix_range< ub::symmetric_matrix<double> > Overlap_B = ub::project( *_orbitalsAB->getOverlap(), ub::range ( _basisA, _basisA +_basisB ), ub::range (0, _basisA +_basisB) );

    CTP_LOG(logDEBUG,*_pLog) << "Projecting the monomer onto dimer orbitals [" << _levelsA + _levelsB << "x" << _basisA + _basisB << "]";
    ub::matrix<double> _psi_AB ( _levelsA + _levelsB, _basisA + _basisB  );

    ub::matrix_range< ub::matrix<double> > _psi_AB_A = ub::project( _psi_AB, ub::range (0, _levelsA ), ub::range ( 0, _basisA +_basisB ) ) ;
    ub::noalias(_psi_AB_A) = ub::prod(*_orbitalsA->getOrbitals(), Overlap_A);

    ub::matrix_range< ub::matrix<double> > _psi_AB_B = ub::project( _psi_AB, ub::range (_levelsA, _levelsA + _levelsB ), ub::range ( 0, _basisA +_basisB ) ) ;
    ub::noalias(_psi_AB_B) = ub::prod(*_orbitalsB->getOrbitals(), Overlap_B );
    CTP_LOG(logDEBUG,*_pLog)  << " (" << t.elapsed() - _st << "s) " << std::flush; _st = t.elapsed();

    ub::matrix<double> _psi_AxB_dimer_basis (_levelsA + _levelsB, _basisA + _basisB );
    ub::matrix<double> OrbAB_Transp = ub::trans( *_orbitalsAB->getOrbitals() );
    CTP_LOG(logDEBUG,*_pLog)  << "Transposing OrbitalsAB (" << t.elapsed() - _st << "s)" << "\x1b[0;39m" << std::flush; _st = t.elapsed();
    ub::noalias(_psi_AxB_dimer_basis) = ub::prod( _psi_AB,  OrbAB_Transp );

    CTP_LOG(logDEBUG,*_pLog)  << "Multiplying PsiAB x OrbitalsAB (" << t.elapsed() - _st << "s)" << "\x1b[0;39m" << std::flush; _st = t.elapsed();

    _psi_AB.resize(0,0,false); OrbAB_Transp.resize(0,0,false);

    //   _psi_AxB_dimer_basis * F  * _psi_AxB_dimer_basis^T
    CTP_LOG(logDEBUG,*_pLog) << "Projecting the Fock matrix onto the dimer basis";
    ub::zero_matrix<double> _zero ( _levelsA + _levelsB, _levelsA + _levelsB );
    ub::matrix<double> JAB_dimer( _zero ) ;
    ub::vector<double> energies = (*_orbitalsAB->getEnergies());

    for ( int i1 = 0; i1 < _levelsA + _levelsB ; i1++ ) {
    for ( int i2 = i1; i2 < _levelsA + _levelsB; i2++ ) {
        for ( int k = 0; k < _basisA + _basisB; k++  ) {
                JAB_dimer(i1,i2) += _psi_AxB_dimer_basis.at_element(i1, k) * _psi_AxB_dimer_basis.at_element(i2, k) * energies(k);
        }
        JAB_dimer(i2,i1) = JAB_dimer(i1,i2);
    }}
    energies.clear();
    CTP_LOG(logDEBUG,*_pLog)  << " (" << t.elapsed() - _st << "s)" << "\x1b[0;39m" << std::flush; _st = t.elapsed();

    // S = psi_AxB_dimer_basis * psi_AxB_dimer_basis^T
    ub::symmetric_matrix<double> _S_AxB = ub::prod( _psi_AxB_dimer_basis, ub::trans( _psi_AxB_dimer_basis ));
    _psi_AxB_dimer_basis.resize(0,0,false);

    ub::matrix<double> _S_AxB_2(_S_AxB.size1(), _S_AxB.size1() );

     ub::trans( _S_AxB );
     CTP_LOG(logDEBUG,*_pLog) << "Calculating square root of the overlap matrix ["
             << _S_AxB.size1() << "x"
             << _S_AxB.size2() << "]";
     SQRTOverlap( _S_AxB , _S_AxB_2 );
     _S_AxB.resize(0,0,false);
     CTP_LOG(logDEBUG,*_pLog)  << " (" << t.elapsed() - _st << "s)" << "\x1b[0;39m" << std::flush; _st = t.elapsed();

     CTP_LOG(logDEBUG,*_pLog) << "Calculating the effective overlap JAB ["
             << JAB_dimer.size1() << "x"
             << JAB_dimer.size2() << "]";

    ub::matrix<double> JAB_temp( _levelsA + _levelsB, _levelsA + _levelsB );

    ub::noalias(JAB_temp) = ub::prod( JAB_dimer, _S_AxB_2 );
    (*_JAB) = ub::prod( _S_AxB_2, JAB_temp );

    // cleanup
    JAB_dimer.resize(0,0,false); JAB_temp.resize(0,0,false); _S_AxB_2.resize(0,0,false);
    CTP_LOG(logDEBUG,*_pLog)  << " (" << t.elapsed() - _st << "s)" << "\x1b[0;39m" << std::flush; _st = t.elapsed();

    CTP_LOG(logDEBUG,*_pLog) << "Done with electronic couplings" << std::flush;
    return true;
};

}}


#include <boost/math/constants/constants.hpp>
#include "TypesFunctions.hh"

#include <TMath.h>

#include "ReactorNorm.hh"

const double pi = boost::math::constants::pi<double>();

ReactorNormAbsolute::ReactorNormAbsolute(const std::vector<std::string> &isonames)
{
  variable_(&m_norm, "Norm");
  auto norm = transformation_("isotopes")
    .types(TypesFunctions::ifSame, [](TypesFunctionArgs fargs) {
        for (size_t i = 0; i < fargs.rets.size(); ++i) {
          fargs.rets[i] = DataType().points().shape(1);
        }
      })
    .func([](ReactorNormAbsolute *obj, FunctionArgs fargs) {
        auto& args=fargs.args;
        auto& rets=fargs.rets;
        for (size_t i = 0; i < rets.size(); ++i) {
          rets[i].x[0] = obj->m_norm*args[i].x.sum();
        }
      })
  ;
  for (const std::string &isoname: isonames) {
    norm.input("fission_fraction_"+isoname);
    norm.output("norm_"+isoname);
  }
}

ReactorNorm::ReactorNorm(const std::vector<std::string> &isonames)
  : m_ePerFission(isonames.size())
{
  variable_(&m_thermalPower, "ThermalPower");
  for (size_t i = 0; i < isonames.size(); ++i) {
    variable_(&m_ePerFission[i], "EnergyPerFission_"+isonames[i]);
  }
  variable_(&m_targetProtons, "TargetProtons");
  variable_(&m_L, "L");
  auto norm = transformation_("isotopes")
    .types(TypesFunctions::ifSame, [](TypesFunctionArgs fargs) {
        for (size_t i = 0; i < fargs.rets.size(); ++i) {
          fargs.rets[i] = DataType().points().shape(1);
        }
      })
    .func(&ReactorNorm::calcIsotopeNorms)
  ;
  for (const std::string &isoname: isonames) {
    norm.input("fission_fraction_"+isoname);
    norm.output("norm_"+isoname);
  }
  norm.input("livetime");
  norm.input("power_rate");
}

void ReactorNorm::calcIsotopeNorms(FunctionArgs fargs) {
  auto& args=fargs.args;
  auto& rets=fargs.rets;
  const auto &livetime = args[rets.size()+0].x;
  const auto &power_rate = args[rets.size()+1].x;
  static double conversionFactor = 1.0e-7/TMath::Qe();
  auto distanceWeight = (conversionFactor / (4*pi*std::pow(m_L, 2)));
  auto coeff = m_targetProtons*m_thermalPower*distanceWeight;

  double ePerFission_avg=0.0;
  for (size_t i = 0; i < rets.size(); ++i) {
    ePerFission_avg+=args[i].x(0)*m_ePerFission[i].value();
  }
  for (size_t i = 0; i < rets.size(); ++i) {
    rets[i].x[0] = (coeff*livetime*power_rate*args[i].x/ePerFission_avg).sum();
  }
}


#pragma once

#include <skynet/ublas.hpp>
#include <skynet/utility/algorithm.hpp>

#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/numeric/ublas/banded.hpp>

#include <random>
#include <type_traits>

namespace skynet{namespace nn{

	template <typename T>
	class cam{
	public:
		static_assert(std::is_integral<T>::value, "The T should be integral.");

		typedef ublas::matrix<T>					matrix;
		typedef ublas::symmetric_matrix<T>			symmetric_matrix;
		typedef ublas::diagonal_matrix<T>			diagonal_matrix;
		typedef ublas::vector<T>					vector;

		cam(matrix input){
			auto size = input.size1();
			_W.resize(size);
			_W = ublas::prod(input, ublas::trans(input));
			diagonal_matrix I(size);
			for (size_t i = 0; i < size; ++i){
				I(i,i) = input.size2();
			}

			_W -= I;

		}

		symmetric_matrix weights() const { return _W; }

		template <typename VE>
		vector decode(const ublas::vector_expression<VE> &code){
			vector state(code().size());
			copy(code(), state);
			std::uniform_int_distribution<size_t> rand_index(0, _W.size2()-1);
			std::mt19937						  mt_index;
			mt_index.seed(unsigned long(std::time(nullptr)));
			while(true){
				size_t variation_count = 0;
				for (size_t j = 0; j < (_W.size2()*5); ++j){
					auto index = rand_index(mt_index);
					auto weights = ublas::row(_W, index);
					auto re = ublas::inner_prod(weights, state);
					if (re == 0)
						continue;
					else if (re > 0 && state[index] == -1){
						state[index] = 1;
						variation_count++;
					}
					else if(re < 0 && state[index] == 1){
						state[index] = -1;
						variation_count++;
					}
				}

				if (variation_count == 0)
					return state;
			}
		}

	private:
		symmetric_matrix		_W;
	};

	template <typename T>
	class bam{
	public:
		static_assert(std::is_integral<T>::value, "The T should be integral.");

		typedef ublas::matrix<T>					matrix;
		typedef ublas::vector<T>					vector;

		bam(matrix input, matrix output)
			: _W(input.size1(), output.size1()), _X(input.size1()), _Y(output.size1())
		{
			_W = ublas::prod(input, ublas::trans(output));
		}

		template <typename E>
		vector operator()(const ublas::vector_expression<E> &ve) const{
#ifdef	ENABLE_ASSERT
			for (auto e : ve()){
				ASSERT(e == -1 || e == 1, "The input should be bipolar.");
			}
#endif
			vector pre_X = ve();
			vector pre_Y = ublas::inner_prod(pre_X, _W);
			bipolarize(pre_Y);

			while(true){
				vector X = ublas::inner_prod(_W, pre_Y);
				bipolarize(X);
				vector Y = ublas::inner_prod(X, _W);
				bipolarize(Y);

				//TODO: the check is expensive.
				if (ublas::norm_1(X-pre_X) == 0 && ublas::norm1(Y-pre_Y) == 0)
					return X;
			}
		}

	private:
		matrix			_W;
	};

}}


#ifndef BART_SRC_CALCULATOR_TWO_GRID_SPECTRAL_SHAPE_I_HPP_
#define BART_SRC_CALCULATOR_TWO_GRID_SPECTRAL_SHAPE_I_HPP_

#include <deal.II/lac/full_matrix.h>

#include "utility/has_description.h"

//! Classes for calculating the two-grid spectral shape function
namespace bart::acceleration::two_grid::spectral_shape {

/*! \brief Interface for classes that calculate the spectral shape function for the two-grid acceleration method.
 *
 * The spectral shape function is defined for the two-grid method of Adams and Morel (2017).
 * This function must be calculated in each homogenous material region, and is calculated by solving
 * for the eigenvector \f$\xi \in \mathbb{R}^{(G \times 1)}\f$ in the following equation:
 * \f[
 * \left(\bf{T} - \bf{S}_{D0}\right)^{-1}\bf{S}_{U0}\vec{\xi} = \rho\vec{\xi}\;,
 * \f]
 * where \f$\bf{T}\f$ is the total cross-section matrix, \f$\bf{S}_{D0}\f$ is the isotropic downscatter matrix,
 * \f$\bf{S}_{U0}\f$ is the isotropic upscatter matrix, and \f$\rho\f$ is the spectral radius. The spectral shape
 * function is normalized such that
 * \f[
 * \sum_{g = 0}^{G - 1}\xi_g = 1\;.
 * \f]
 *
 */
class SpectralShapeI : public utility::HasDescription {
 public:
  using DealiiMatrix = dealii::FullMatrix<double>;
  virtual ~SpectralShapeI() = default;
  /*! \brief Calculate the spectral shape.
   *
   * @param sigma_t total cross-section matrix.
   * @param sigma_s full scattering matrix.
   * @return spectral shape function
   */
  virtual auto CalculateSpectralShape(const DealiiMatrix& sigma_t,
                                      const DealiiMatrix& sigma_s) -> std::vector<double> = 0;
};

} // namespace bart::acceleration::two_grid::spectral_shape

#endif //BART_SRC_CALCULATOR_TWO_GRID_SPECTRAL_SHAPE_I_HPP_