/******************************************************************************
 * Copyright (C) 2013 by Jerome Maye                                          *
 * jerome.maye@gmail.com                                                      *
 ******************************************************************************/

#include "aslam/calibration/2dlrf/ErrorTermObservation.h"

#include <Eigen/Dense>

#include <sm/kinematics/rotations.hpp>

#include <aslam/calibration/data-structures/VectorDesignVariable.h>

namespace aslam {
  namespace calibration {

/******************************************************************************/
/* Constructors and Destructor                                                */
/******************************************************************************/

    ErrorTermObservation::ErrorTermObservation(VectorDesignVariable<3>* xk,
        VectorDesignVariable<2>* xl, VectorDesignVariable<3>* Theta, double r,
        double b, const Covariance& R) :
        _xk(xk),
        _xl(xl),
        _Theta(Theta),
        _r(r),
        _b(b),
        _R(R) {
      setInvR(_R.inverse());
      setDesignVariables(xk, xl, Theta);
    }

    ErrorTermObservation::ErrorTermObservation(
        const ErrorTermObservation& other) :
        ErrorTermFs<2>(other),
        _xk(other._xk),
        _xl(other._xl),
        _Theta(other._Theta),
        _r(other._r),
        _b(other._b),
        _R(other._R) {
    }

    ErrorTermObservation& ErrorTermObservation::operator =
        (const ErrorTermObservation& other) {
      if (this != &other) {
        ErrorTermFs<2>::operator=(other);
       _xk = other._xk;
       _xl = other._xl;
       _Theta = other._Theta;
       _r = other._r;
       _b = other._b;
       _R = other._R;
      }
      return *this;
    }

    ErrorTermObservation::~ErrorTermObservation() {
    }

/******************************************************************************/
/* Accessors                                                                  */
/******************************************************************************/

    double ErrorTermObservation::getRange() const {
      return _r;
    }

    void ErrorTermObservation::setRange(double r) {
      _r = r;
    }

    double ErrorTermObservation::getBearing() const {
      return _b;
    }

    void ErrorTermObservation::setBearing(double b) {
      _b = b;
    }

    const ErrorTermObservation::Covariance&
        ErrorTermObservation::getCovariance() const {
      return _R;
    }

    ErrorTermObservation::Covariance& ErrorTermObservation::getCovariance() {
      return _R;
    }

    void ErrorTermObservation::setCovariance(const Covariance& R) {
      _R = R;
    }

/******************************************************************************/
/* Methods                                                                    */
/******************************************************************************/

    double ErrorTermObservation::evaluateErrorImplementation() {
      const double ct = cos((_xk->getValue())(2));
      const double st = sin((_xk->getValue())(2));
      const double dxct = (_Theta->getValue())(0) * ct;
      const double dxst = (_Theta->getValue())(0) * st;
      const double dyct = (_Theta->getValue())(1) * ct;
      const double dyst = (_Theta->getValue())(1) * st;
      const double aa = (_xl->getValue())(0) - (_xk->getValue())(0) - dxct
        + dyst;
      const double bb = (_xl->getValue())(1) - (_xk->getValue())(1) - dxst
        - dyct;
      const double temp1 = aa * aa + bb * bb;
      const double temp2 = sqrt(temp1);
      error_t error;
      error(0) = _r - temp2;
      error(1) = sm::kinematics::angleMod(_b - (atan2(bb, aa) -
        (_xk->getValue())(2) - (_Theta->getValue())(2)));
      setError(error);
      return evaluateChiSquaredError();
    }

    void ErrorTermObservation::evaluateJacobiansImplementation(
        aslam::backend::JacobianContainer& jacobians) {
      const double ct = cos((_xk->getValue())(2));
      const double st = sin((_xk->getValue())(2));
      const double dxct = (_Theta->getValue())(0) * ct;
      const double dxst = (_Theta->getValue())(0) * st;
      const double dyct = (_Theta->getValue())(1) * ct;
      const double dyst = (_Theta->getValue())(1) * st;
      const double aa = (_xl->getValue())(0) - (_xk->getValue())(0) - dxct
        + dyst;
      const double bb = (_xl->getValue())(1) - (_xk->getValue())(1) - dxst
        - dyct;
      const double temp1 = aa * aa + bb * bb;
      const double temp2 = sqrt(temp1);
      Eigen::Matrix<double, 2, 3> Gxk = Eigen::Matrix<double, 2, 3>::Zero();
      Gxk(0, 0) = -aa / temp2;
      Gxk(0, 1) = -bb / temp2;
      Gxk(0, 2) = (aa * (dxst + dyct) + bb * (-dxct + dyst)) / temp2;
      Gxk(1, 0) = bb / temp1;
      Gxk(1, 1) = -aa / temp1;
      Gxk(1, 2) = (aa * (-dxct + dyst) - bb * (dxst + dyct)) / temp1 - 1;
      Eigen::Matrix<double, 2, 2> Glk = Eigen::Matrix<double, 2, 2>::Zero();
      Glk(0, 0) = aa / temp2;
      Glk(0, 1) = bb / temp2;
      Glk(1, 0) = -bb / temp1;
      Glk(1, 1) = aa / temp1;
      Eigen::Matrix<double, 2, 3> Gtk = Eigen::Matrix<double, 2, 3>::Zero();
      Gtk(0, 0) = -(aa * ct + bb * st) / temp2;
      Gtk(0, 1) = (aa * st - bb * ct) / temp2;
      Gtk(1, 0) = (-aa * st + bb * ct) / temp1;
      Gtk(1, 1) = -(aa * ct + bb * st) / temp1;
      Gtk(1, 2) = -1;
      jacobians.add(_xk, -Gxk);
      jacobians.add(_xl, -Glk);
      jacobians.add(_Theta, -Gtk);
    }

  }
}


/* This module defines overall interface to the generalized Ising solver.
 * See the README for the meaning of input flags, input files, and installation
 *
 * Author: Wenxuan Huang
 * Maintainer: Wenxuan Huang, Daniil Kitchaev
 * Date: 15 March, 2015
 *
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <streambuf>
#include <string>
#include <stdio.h>
#include <vector>
#include <set>
#include <map>
#include "gurobi_c++.h"
#include "boost/tuple/tuple.hpp"
#include "boost/tuple/tuple_comparison.hpp"
#include "boost/tuple/tuple_io.hpp"
#include <boost/lexical_cast.hpp>
#include "solver.h"

using namespace std;
using namespace ::boost::tuples;
using namespace ::boost;

void read_from_file(std::string &id,
                     int &max_sites,
                     map< set<tuple<int,int,int,int,int> >, double> &J,
                     double &prec,
                     int &num_loops,
                     bool &translation_algorithm,
                     bool &basic_exact_mode,
                     bool &pseudo_mode,
                     bool &pseudo_mode_with_proof,
                     bool &verbose,
                     bool &very_verbose,
                     bool &obscenely_verbose);

int main (void) {
    std::string id="IS0";
    int max_sites = 50;
    int num_loops = 4;
    double prec = 0.00001;
    bool translation_algorithm = false;
    bool basic_exact_mode = false;
    bool pseudo_mode = true;
    bool pseudo_mode_with_proof = false;
    bool verbose = true;
    bool very_verbose = false;
    bool obscenely_verbose = false;

    double lower_bound, upper_bound, exact_lowerbound;

    map< set<tuple<int,int,int,int,int> >, double> J, lowerboundclustertype, upperboundclustertype, J_for_proof;
    map< tuple<int,int,int,int>,int> unitcell;
    tuple<int,int,int,int,int,int> periodicity;
    map< tuple<int,int,int,int>, int> cellrepresentation;

    read_from_file(id, max_sites, J, prec, num_loops, translation_algorithm,
                   basic_exact_mode, pseudo_mode, pseudo_mode_with_proof,
                   verbose, very_verbose, obscenely_verbose);

    run_solver(max_sites, J,
               lowerboundclustertype, upperboundclustertype, cellrepresentation,
               lower_bound, upper_bound, exact_lowerbound, unitcell, periodicity,
               J_for_proof,
               id,
               prec, num_loops,
               basic_exact_mode, pseudo_mode, pseudo_mode_with_proof,
               verbose, very_verbose, obscenely_verbose);

    return 0;
}

void read_from_file(std::string &id,
                     int &max_sites,
                     map< set<tuple<int,int,int,int,int> >, double> &J,
                     double &prec,
                     int &num_loops,
                     bool &translation_algorithm,
                     bool &basic_exact_mode,
                     bool &pseudo_mode,
                     bool &pseudo_mode_with_proof,
                     bool &verbose,
                     bool &very_verbose,
                     bool &obscenely_verbose) {
	map< set<tuple<int,int,int,int,int> >, double> J_negative1_positive1;
	set< tuple<int,int,int,int,int> >setoftupletemp;
	double constant_term = 0;
	int solver_mode = 1;
	int verbosity = 1;

	std::ifstream t2("J_config.in");
	std::stringstream buffer2;
	buffer2 << t2.rdbuf();
	string J_config_file=buffer2.str();

	vector<string> J_config_line;
	split_is(J_config_file, '\n', J_config_line);
	for (vector<string>::iterator it=J_config_line.begin(); it!=J_config_line.end(); it++) {
		vector<string> J_config_line_segment;
		string temp=*it;
		split_is(temp, ' ', J_config_line_segment);
		vector<string>::iterator segment_iterator=J_config_line_segment.begin();
		string first_segment=*segment_iterator;
		vector<string> equal_left_right;
		split_is(first_segment, '=', equal_left_right);
		vector<string>::iterator equal_iterator=equal_left_right.begin();
		string equal_left=*equal_iterator;
		equal_iterator++;
		string equal_right=*equal_iterator;

		if (equal_left=="NSITES") {
			max_sites=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="NLOOPS") {
			num_loops=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="LABEL") {
			id=equal_right;
		}else if (equal_left=="PREC") {
			prec=lexical_cast<double>(equal_right) ;
		}else if (equal_left=="MODE_JPLUSMINUS") {
			translation_algorithm=lexical_cast<bool>(equal_right) ;
		}else if (equal_left=="MODE_SOLVER") {
			solver_mode=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="MODE_VERBOSITY") {
			verbosity = lexical_cast<int>(equal_right) ;
		}
	}

	if (solver_mode == 0) {
		basic_exact_mode = true;
		pseudo_mode = false;
		pseudo_mode = false;
	}else if (solver_mode == 1) {
		basic_exact_mode = false;
		pseudo_mode = true;
		pseudo_mode_with_proof = false;
	}else if (solver_mode == 2) {
		basic_exact_mode = false;
		pseudo_mode = true;
		pseudo_mode_with_proof = true;
	}else{
		cout << "Invalid solver mode given. Exiting." << endl;
		exit(1);
	}

	if (verbosity == 0) {
		verbose = false;
		very_verbose = false;
		obscenely_verbose = false;
	}else if (verbosity == 1) {
		verbose = true;
		very_verbose = false;
		obscenely_verbose = false;
	}else if (verbosity == 2) {
		verbose = true;
		very_verbose = true;
		obscenely_verbose = false;
	}else if (verbosity == 3) {
		verbose = true;
		very_verbose = true;
		obscenely_verbose = true;
	}else {
		cout << "Invalid verbosity mode given. Exiting." << endl;
		exit(1);
	}

	std::ifstream t1("J_in.in");
	std::stringstream buffer1;
	buffer1 << t1.rdbuf();
	string J_in_file=buffer1.str();

	vector<string> J_in_line;
	split_is(J_in_file, '\n', J_in_line);
	int line_format_indicator=0;
	for (vector<string>::iterator it=J_in_line.begin(); it!=J_in_line.end(); it++){
		string this_line=*it;
		if (it==J_in_line.begin()){
			vector<string> segment;
			split_is(this_line, ' ', segment);
			vector<string>::iterator segment_iterator=segment.begin();
			string temp_segment=*segment_iterator;
			if (temp_segment=="Constant"){
				segment_iterator++;
				constant_term=lexical_cast<double>(*segment_iterator);
			}
		}else{
			if (line_format_indicator==0) {
				vector<string> segment;
				split_is(this_line, ' ', segment);
				vector<string>::iterator segment_iterator=segment.begin();
				string temp_segment=*segment_iterator;
				if (temp_segment=="Cluster") {
					line_format_indicator=1;
				}
			}else if (line_format_indicator==1){
				setoftupletemp.clear();
				vector<string> segment;
				split_is(this_line, ' ', segment);

				for (vector<string>::iterator it2=segment.begin(); it2!=segment.end(); it2++) {
					string this_segment=*it2;
					vector<string> number_vector;
					split_is(this_segment, ',', number_vector);
					int l1,l2,l3,l4,l5;
					l1=lexical_cast<int>(number_vector[0]);
					l2=lexical_cast<int>(number_vector[1]);
					l3=lexical_cast<int>(number_vector[2]);
					l4=lexical_cast<int>(number_vector[3]);
					l5=lexical_cast<int>(number_vector[4]);
					setoftupletemp.insert(make_tuple(l1,l2,l3,l4,l5));
				}

				line_format_indicator=2;
			}else if (line_format_indicator==2) {
				vector<string> segment;
				split_is(this_line, '=', segment);

				if (translation_algorithm){
					J_negative1_positive1[setoftupletemp]=lexical_cast<double>(segment[1]);
				}else{
					J[setoftupletemp]=lexical_cast<double>(segment[1]);
				}

				line_format_indicator=0;
			}
		}
	}

	if(translation_algorithm){
		convertJ_neg1_pos1toJ(J_negative1_positive1, J, constant_term);
	}
}


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

#include <boost/simd/ieee/functions/exponent.hpp>
#include <boost/simd/include/functions/simd/shri.hpp>
#include <boost/simd/include/functions/simd/exponentbits.hpp>
#include <boost/simd/include/functions/simd/is_nez.hpp>
#include <boost/simd/include/functions/simd/is_invalid.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/if_else_zero.hpp>
#include <boost/simd/include/functions/simd/if_zero_else.hpp>
#include <boost/simd/include/constants/maxexponent.hpp>
#include <boost/simd/include/constants/nbmantissabits.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/sdk/meta/scalar_of.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::exponent_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<arithmetic_<A0>,X>))
                            )
  {

    typedef typename dispatch::meta::as_integer<A0, signed>::type result_type;

    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      typedef typename meta::scalar_of<A0>::type             s_type;
      typedef typename meta::scalar_of<result_type>::type sint_type;
      const int nmb= int(Nbmantissabits<s_type>());
      const result_type x = shri(exponentbits(a0), nmb);
      return if_zero_else( is_invalid(a0), x-if_else_zero(is_nez(a0), Maxexponent<A0>()));
    }
  };
} } }

#endif


/*=============================================================================
    Copyright (c) 2001-2019 Joel de Guzman
    Copyright (c) 2001-2011 Hartmut Kaiser
    http://spirit.sourceforge.net/

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

#if defined(_MSC_VER)
#pragma once
#endif

#include <cmath>
#include <boost/limits.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/qi/detail/attributes.hpp>
#include <boost/spirit/home/support/detail/pow10.hpp>
#include <boost/spirit/home/support/detail/sign.hpp>
#include <boost/integer.hpp>
#include <boost/assert.hpp>

#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(push)
# pragma warning(disable: 4100)   // 'p': unreferenced formal parameter
# pragma warning(disable: 4127)   // conditional expression is constant
#endif

namespace boost { namespace spirit { namespace traits
{
    using spirit::traits::pow10;

    namespace detail
    {
        template <typename T, typename AccT>
        void compensate_roundoff(T& n, AccT acc_n, mpl::true_)
        {
            // at the lowest extremes, we compensate for floating point
            // roundoff errors by doing imprecise computation using T
            int const comp = 10;
            n = T((acc_n / comp) * comp);
            n += T(acc_n % comp);
        }

        template <typename T, typename AccT>
        void compensate_roundoff(T& n, AccT acc_n, mpl::false_)
        {
            // no need to compensate
            n = acc_n;
        }

        template <typename T, typename AccT>
        void compensate_roundoff(T& n, AccT acc_n)
        {
            compensate_roundoff(n, acc_n, is_integral<AccT>());
        }
    }

    template <typename T, typename AccT>
    inline bool
    scale(int exp, T& n, AccT acc_n)
    {
        if (exp >= 0)
        {
            int const max_exp = std::numeric_limits<T>::max_exponent10;

            // return false if exp exceeds the max_exp
            // do this check only for primitive types!
            if (is_floating_point<T>() && (exp > max_exp))
                return false;
            n = acc_n * pow10<T>(exp);
        }
        else
        {
            if (exp < std::numeric_limits<T>::min_exponent10)
            {
                int const min_exp = std::numeric_limits<T>::min_exponent10;
                detail::compensate_roundoff(n, acc_n);
                n /= pow10<T>(-min_exp);

                // return false if exp still exceeds the min_exp
                // do this check only for primitive types!
                exp += -min_exp;
                if (is_floating_point<T>() && exp < min_exp)
                    return false;

                n /= pow10<T>(-exp);
            }
            else
            {
                n = T(acc_n) / pow10<T>(-exp);
            }
        }
        return true;
    }

    inline bool
    scale(int /*exp*/, unused_type /*n*/, unused_type /*acc_n*/)
    {
        // no-op for unused_type
        return true;
    }

    template <typename T, typename AccT>
    inline bool
    scale(int exp, int frac, T& n, AccT acc_n)
    {
        return scale(exp - frac, n, acc_n);
    }

    inline bool
    scale(int /*exp*/, int /*frac*/, unused_type /*n*/)
    {
        // no-op for unused_type
        return true;
    }

    inline float
    negate(bool neg, float n)
    {
        return neg ? spirit::detail::changesign(n) : n;
    }

    inline double
    negate(bool neg, double n)
    {
        return neg ? spirit::detail::changesign(n) : n;
    }

    inline long double
    negate(bool neg, long double n)
    {
        return neg ? spirit::detail::changesign(n) : n;
    }

    template <typename T>
    inline T
    negate(bool neg, T const& n)
    {
        return neg ? -n : n;
    }

    inline unused_type
    negate(bool /*neg*/, unused_type n)
    {
        // no-op for unused_type
        return n;
    }

    template <typename T>
    struct real_accumulator : mpl::identity<T> {};

    template <>
    struct real_accumulator<float>
        : mpl::identity<uint_t<(sizeof(float)*CHAR_BIT)>::least> {};

    template <>
    struct real_accumulator<double>
        : mpl::identity<uint_t<(sizeof(double)*CHAR_BIT)>::least> {};
}}}

namespace boost { namespace spirit { namespace qi  { namespace detail
{
    BOOST_MPL_HAS_XXX_TRAIT_DEF(version)

    template <typename T, typename RealPolicies>
    struct real_impl
    {
        template <typename Iterator>
        static std::size_t
        ignore_excess_digits(Iterator& /* first */, Iterator const& /* last */, mpl::false_)
        {
            return 0;
        }

        template <typename Iterator>
        static std::size_t
        ignore_excess_digits(Iterator& first, Iterator const& last, mpl::true_)
        {
            return RealPolicies::ignore_excess_digits(first, last);
        }

        template <typename Iterator>
        static std::size_t
        ignore_excess_digits(Iterator& first, Iterator const& last)
        {
            typedef mpl::bool_<has_version<RealPolicies>::value> has_version;
            return ignore_excess_digits(first, last, has_version());
        }

        template <typename Iterator, typename Attribute>
        static bool
        parse(Iterator& first, Iterator const& last, Attribute& attr,
            RealPolicies const& p)
        {
            if (first == last)
                return false;
            Iterator save = first;

            // Start by parsing the sign. neg will be true if
            // we got a "-" sign, false otherwise.
            bool neg = p.parse_sign(first, last);

            // Now attempt to parse an integer
            T n;

            typename traits::real_accumulator<T>::type acc_n = 0;
            bool got_a_number = p.parse_n(first, last, acc_n);
            int excess_n = 0;

            // If we did not get a number it might be a NaN, Inf or a leading
            // dot.
            if (!got_a_number)
            {
                // Check whether the number to parse is a NaN or Inf
                if (p.parse_nan(first, last, n) ||
                    p.parse_inf(first, last, n))
                {
                    // If we got a negative sign, negate the number
                    traits::assign_to(traits::negate(neg, n), attr);
                    return true;    // got a NaN or Inf, return early
                }

                // If we did not get a number and our policies do not
                // allow a leading dot, fail and return early (no-match)
                if (!p.allow_leading_dot)
                {
                    first = save;
                    return false;
                }
            }
            else
            {
                // We got a number and we still see digits. This happens if acc_n (an integer)
                // exceeds the integer's capacity. Collect the excess digits.
                excess_n = static_cast<int>(ignore_excess_digits(first, last));
            }

            bool e_hit = false;
            Iterator e_pos;
            int frac_digits = 0;

            // Try to parse the dot ('.' decimal point)
            if (p.parse_dot(first, last))
            {
                // We got the decimal point. Now we will try to parse
                // the fraction if it is there. If not, it defaults
                // to zero (0) only if we already got a number.
                if (excess_n != 0)
                {
                    // We skip the fractions if we already exceeded our digits capacity
                    ignore_excess_digits(first, last);
                }
                else if (p.parse_frac_n(first, last, acc_n, frac_digits))
                {
                    BOOST_ASSERT(frac_digits >= 0);
                }
                else if (!got_a_number || !p.allow_trailing_dot)
                {
                    // We did not get a fraction. If we still haven't got a
                    // number and our policies do not allow a trailing dot,
                    // return no-match.
                    first = save;
                    return false;
                }

                // Now, let's see if we can parse the exponent prefix
                e_pos = first;
                e_hit = p.parse_exp(first, last);
            }
            else
            {
                // No dot and no number! Return no-match.
                if (!got_a_number)
                {
                    first = save;
                    return false;
                }

                // If we must expect a dot and we didn't see an exponent
                // prefix, return no-match.
                e_pos = first;
                e_hit = p.parse_exp(first, last);
                if (p.expect_dot && !e_hit)
                {
                    first = save;
                    return false;
                }
            }

            if (e_hit)
            {
                // We got the exponent prefix. Now we will try to parse the
                // actual exponent.
                int exp = 0;
                if (p.parse_exp_n(first, last, exp))
                {
                    // Got the exponent value. Scale the number by
                    // exp + excess_n - frac_digits.
                    if (!traits::scale(exp + excess_n, frac_digits, n, acc_n))
                        return false;
                }
                else
                {
                    // If there is no number, disregard the exponent altogether.
                    // by resetting 'first' prior to the exponent prefix (e|E)
                    first = e_pos;
                    // Scale the number by -frac_digits.
                    bool r = traits::scale(-frac_digits, n, acc_n);
                    BOOST_VERIFY(r);
                }
            }
            else if (frac_digits)
            {
                // No exponent found. Scale the number by -frac_digits.
                bool r = traits::scale(-frac_digits, n, acc_n);
                BOOST_VERIFY(r);
            }
            else
            {
                if (excess_n)
                {
                    if (!traits::scale(excess_n, n, acc_n))
                        return false;
                }
                else
                {
                    n = static_cast<T>(acc_n);
                }
            }

            // If we got a negative sign, negate the number
            traits::assign_to(traits::negate(neg, n), attr);

            // Success!!!
            return true;
        }
    };

#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(pop)
#endif

}}}}

#endif


/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/
#include "itkGibbsTrackingFilter.h"

// MITK
#include <itkOrientationDistributionFunction.h>
#include <itkDiffusionOdfGeneralizedFaImageFilter.h>
#include <mitkStandardFileLocations.h>
#include <mitkFiberBuilder.h>
#include <mitkMetropolisHastingsSampler.h>
//#include <mitkEnergyComputer.h>
#include <itkTensorImageToOdfImageFilter.h>
#include <mitkGibbsEnergyComputer.h>

// ITK
#include <itkImageDuplicator.h>
#include <itkResampleImageFilter.h>
#include <itkTimeProbe.h>

// MISC
#include <fstream>
// #include <QFile>
#include <tinyxml.h>
#include <boost/progress.hpp>
#include <mitkLexicalCast.h>
#include <boost/algorithm/string.hpp>

namespace itk{

template< class ItkOdfImageType >
GibbsTrackingFilter< ItkOdfImageType >::GibbsTrackingFilter():
  m_StartTemperature(0.1),
  m_EndTemperature(0.001),
  m_Iterations(1e9),
  m_CurrentIteration(0.0),
  m_CurrentStep(0),
  m_ParticleWeight(0),
  m_ParticleWidth(0),
  m_ParticleLength(0),
  m_ConnectionPotential(10),
  m_InexBalance(0),
  m_ParticlePotential(0.2),
  m_MinFiberLength(10),
  m_AbortTracking(false),
  m_NumAcceptedFibers(0),
  m_BuildFibers(false),
  m_ProposalAcceptance(0),
  m_CurvatureThreshold(0.7),
  m_DuplicateImage(true),
  m_NumParticles(0),
  m_NumConnections(0),
  m_RandomSeed(-1),
  m_LoadParameterFile(""),
  m_LutPath(""),
  m_IsInValidState(true)
{

}

template< class ItkOdfImageType >
GibbsTrackingFilter< ItkOdfImageType >::~GibbsTrackingFilter()
{

}

// fill output fiber bundle datastructure
template< class ItkOdfImageType >
typename GibbsTrackingFilter< ItkOdfImageType >::FiberPolyDataType GibbsTrackingFilter< ItkOdfImageType >::GetFiberBundle()
{
  if (!m_AbortTracking)
  {
    m_BuildFibers = true;
    while (m_BuildFibers){}
  }

  return m_FiberPolyData;
}

template< class ItkOdfImageType >
void
GibbsTrackingFilter< ItkOdfImageType >
::EstimateParticleWeight()
{
  MITK_INFO << "GibbsTrackingFilter: estimating particle weight";

  float minSpacing;
  if(m_OdfImage->GetSpacing()[0]<m_OdfImage->GetSpacing()[1] && m_OdfImage->GetSpacing()[0]<m_OdfImage->GetSpacing()[2])
    minSpacing = m_OdfImage->GetSpacing()[0];
  else if (m_OdfImage->GetSpacing()[1] < m_OdfImage->GetSpacing()[2])
    minSpacing = m_OdfImage->GetSpacing()[1];
  else
    minSpacing = m_OdfImage->GetSpacing()[2];
  float m_ParticleLength = 1.5*minSpacing;
  float m_ParticleWidth = 0.5*minSpacing;

  // seed random generators
  Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New();
  if (m_RandomSeed>-1)
    randGen->SetSeed(m_RandomSeed);
  else
    randGen->SetSeed();

  // instantiate all necessary components
  SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);
  // handle lookup table not found cases
  if( !interpolator->IsInValidState() )
  {
    m_IsInValidState = false;
    m_AbortTracking = true;
    m_BuildFibers = false;
    mitkThrow() << "Unable to load lookup tables.";
  }
  ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity);
  GibbsEnergyComputer* encomp = new GibbsEnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen);

  //    EnergyComputer* encomp = new EnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen);
  MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);

  float alpha = log(m_EndTemperature/m_StartTemperature);
  m_ParticleWeight = 0.01;
  int ppv = 0;
  // main loop
  int neededParts = 3000;
  while (ppv<neededParts)
  {
    if (ppv<1000)
      m_ParticleWeight /= 2;
    else
      m_ParticleWeight = ppv*m_ParticleWeight/neededParts;

    encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);
    for( int step = 0; step < 10; step++ )
    {
      // update temperatur for simulated annealing process
      float temperature = m_StartTemperature * exp(alpha*(((1.0)*step)/((1.0)*10)));
      sampler->SetTemperature(temperature);

      for (unsigned long i=0; i<10000; i++)
        sampler->MakeProposal();
    }
    ppv = particleGrid->m_NumParticles;
    particleGrid->ResetGrid();
  }
  delete sampler;
  delete encomp;
  delete particleGrid;
  delete interpolator;

  MITK_INFO << "GibbsTrackingFilter: finished estimating particle weight";
}

// perform global tracking
template< class ItkOdfImageType >
void GibbsTrackingFilter< ItkOdfImageType >::GenerateData()
{
  TimeProbe preClock; preClock.Start();
  // check if input is Odf or tensor image and generate Odf if necessary
  if (m_OdfImage.IsNull() && m_TensorImage.IsNotNull())
  {
    TensorImageToOdfImageFilter<float,float>::Pointer filter = TensorImageToOdfImageFilter<float,float>::New();
    filter->SetInput( m_TensorImage );
    filter->Update();
    m_OdfImage = filter->GetOutput();
  }
  else if (m_DuplicateImage) // generate local working copy of Odf image (if not disabled)
  {
    typedef itk::ImageDuplicator< ItkOdfImageType > DuplicateFilterType;
    typename DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New();
    duplicator->SetInputImage( m_OdfImage );
    duplicator->Update();
    m_OdfImage = duplicator->GetOutput();
  }

  // perform mean subtraction on odfs
  typedef ImageRegionIterator< ItkOdfImageType > InputIteratorType;
  InputIteratorType it(m_OdfImage, m_OdfImage->GetLargestPossibleRegion() );
  it.GoToBegin();
  while (!it.IsAtEnd())
  {
    itk::OrientationDistributionFunction<float, ODF_SAMPLING_SIZE> odf(it.Get().GetDataPointer());
    float mean = odf.GetMeanValue();
    odf -= mean;
    it.Set(odf.GetDataPointer());
    ++it;
  }

  // check if mask image is given if it needs resampling
  PrepareMaskImage();

  // load parameter file
  LoadParameters();

  // prepare parameters
  float minSpacing;
  if(m_OdfImage->GetSpacing()[0]<m_OdfImage->GetSpacing()[1] && m_OdfImage->GetSpacing()[0]<m_OdfImage->GetSpacing()[2])
    minSpacing = m_OdfImage->GetSpacing()[0];
  else if (m_OdfImage->GetSpacing()[1] < m_OdfImage->GetSpacing()[2])
    minSpacing = m_OdfImage->GetSpacing()[1];
  else
    minSpacing = m_OdfImage->GetSpacing()[2];

  if(m_ParticleLength == 0)
    m_ParticleLength = 1.5*minSpacing;
  if(m_ParticleWidth == 0)
    m_ParticleWidth = 0.5*minSpacing;

  if(m_ParticleWeight == 0)
    EstimateParticleWeight();

  float alpha = log(m_EndTemperature/m_StartTemperature);

  if (m_CurvatureThreshold < mitk::eps)
    m_CurvatureThreshold = 0;

  // seed random generators
  Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New();
  if (m_RandomSeed>-1)
    randGen->SetSeed(m_RandomSeed);
  else
    randGen->SetSeed();

  // load sphere interpolator to evaluate the ODFs
  SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);

  // handle lookup table not found cases
  if( !interpolator->IsInValidState() )
  {
    m_IsInValidState = false;
    m_AbortTracking = true;
    m_BuildFibers = false;
    mitkThrow() << "Unable to load lookup tables.";
  }
  // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer)
  ParticleGrid* particleGrid;
  GibbsEnergyComputer* encomp;
  MetropolisHastingsSampler* sampler;
  try{
    particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity);
    encomp = new GibbsEnergyComputer(m_OdfImage, m_MaskImage, particleGrid, interpolator, randGen);
    encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);
    sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);
  }
  catch(...)
  {
    MITK_ERROR  << "Particle grid allocation failed. Not enough memory? Try to increase the particle length.";
    m_IsInValidState = false;
    m_AbortTracking = true;
    m_BuildFibers = false;
    return;
  }

  MITK_INFO << "----------------------------------------";
  MITK_INFO << "Iterations: " << m_Iterations;
  MITK_INFO << "Particle length: " << m_ParticleLength;
  MITK_INFO << "Particle width: " << m_ParticleWidth;
  MITK_INFO << "Particle weight: " << m_ParticleWeight;
  MITK_INFO << "Start temperature: " << m_StartTemperature;
  MITK_INFO << "End temperature: " << m_EndTemperature;
  MITK_INFO << "In/Ex balance: " << m_InexBalance;
  MITK_INFO << "Min. fiber length: " << m_MinFiberLength;
  MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold;
  MITK_INFO << "Random seed: " << m_RandomSeed;
  MITK_INFO << "----------------------------------------";

  // main loop
  preClock.Stop();
  TimeProbe clock; clock.Start();
  m_NumAcceptedFibers = 0;
  m_CurrentIteration = 0;
  bool just_built_fibers = false;
  boost::progress_display disp(m_Iterations);
  if (!m_AbortTracking)
    while (m_CurrentIteration<m_Iterations)
    {
      just_built_fibers = false;
      ++disp;
      m_CurrentIteration++;
      if (m_AbortTracking)
        break;

      // update temperatur for simulated annealing process
      float temperature = m_StartTemperature * exp(alpha*m_CurrentIteration/m_Iterations);
      sampler->SetTemperature(temperature);
      sampler->MakeProposal();

      m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/m_CurrentIteration;
      m_NumParticles = particleGrid->m_NumParticles;
      m_NumConnections = particleGrid->m_NumConnections;

      if (m_AbortTracking)
        break;

      if (m_BuildFibers)
      {
        FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
        m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
        m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
        m_BuildFibers = false;
        just_built_fibers = true;
      }
    }
  if (!just_built_fibers)
  {
    FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
    m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
    m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
  }
  clock.Stop();

  delete sampler;
  delete encomp;
  delete interpolator;
  delete particleGrid;
  m_AbortTracking = true;
  m_BuildFibers = false;

  int h = clock.GetTotal()/3600;
  int m = ((int)clock.GetTotal()%3600)/60;
  int s = (int)clock.GetTotal()%60;
  MITK_INFO << "GibbsTrackingFilter: finished gibbs tracking in " << h << "h, " << m << "m and " << s << "s";
  m = (int)preClock.GetTotal()/60;
  s = (int)preClock.GetTotal()%60;
  MITK_INFO << "GibbsTrackingFilter: preparation of the data took " << m << "m and " << s << "s";
  MITK_INFO << "GibbsTrackingFilter: " << m_NumAcceptedFibers << " fibers accepted";

  //    sampler->PrintProposalTimes();

  SaveParameters();
}

template< class ItkOdfImageType >
void GibbsTrackingFilter< ItkOdfImageType >::PrepareMaskImage()
{
  if(m_MaskImage.IsNull())
  {
    MITK_INFO << "GibbsTrackingFilter: generating default mask image";
    m_MaskImage = ItkFloatImageType::New();
    m_MaskImage->SetSpacing( m_OdfImage->GetSpacing() );
    m_MaskImage->SetOrigin( m_OdfImage->GetOrigin() );
    m_MaskImage->SetDirection( m_OdfImage->GetDirection() );
    m_MaskImage->SetRegions( m_OdfImage->GetLargestPossibleRegion() );
    m_MaskImage->Allocate();
    m_MaskImage->FillBuffer(1.0);
  }
  else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[0] ||
            m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[1] ||
            m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_OdfImage->GetLargestPossibleRegion().GetSize()[2] ||
            m_MaskImage->GetSpacing()[0]!=m_OdfImage->GetSpacing()[0] ||
            m_MaskImage->GetSpacing()[1]!=m_OdfImage->GetSpacing()[1] ||
            m_MaskImage->GetSpacing()[2]!=m_OdfImage->GetSpacing()[2] )
  {
    MITK_INFO << "GibbsTrackingFilter: resampling mask image";
    typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType;
    ResamplerType::Pointer resampler = ResamplerType::New();
    resampler->SetOutputSpacing( m_OdfImage->GetSpacing() );
    resampler->SetOutputOrigin( m_OdfImage->GetOrigin() );
    resampler->SetOutputDirection( m_OdfImage->GetDirection() );
    resampler->SetSize( m_OdfImage->GetLargestPossibleRegion().GetSize() );

    resampler->SetInput( m_MaskImage );
    resampler->SetDefaultPixelValue(0.0);
    resampler->Update();
    m_MaskImage = resampler->GetOutput();
    MITK_INFO << "GibbsTrackingFilter: resampling finished";
  }
}

// load tracking paramters from xml file (.gtp)
template< class ItkOdfImageType >
bool GibbsTrackingFilter< ItkOdfImageType >::LoadParameters()
{
  m_AbortTracking = true;
  try
  {
    if( m_LoadParameterFile.length()==0 )
    {
      m_AbortTracking = false;
      return true;
    }

    MITK_INFO << "GibbsTrackingFilter: loading parameter file " << m_LoadParameterFile;

    TiXmlDocument doc( m_LoadParameterFile );
    doc.LoadFile();

    TiXmlHandle hDoc(&doc);
    TiXmlElement* pElem;
    TiXmlHandle hRoot(nullptr);

    pElem = hDoc.FirstChildElement().Element();
    hRoot = TiXmlHandle(pElem);
    pElem = hRoot.FirstChildElement("parameter_set").Element();

    std::string iterations(pElem->Attribute("iterations"));
    m_Iterations = boost::lexical_cast<double>(iterations);

    std::string particleLength(pElem->Attribute("particle_length"));
    m_ParticleLength = boost::lexical_cast<float>(particleLength);

    std::string particleWidth(pElem->Attribute("particle_width"));
    m_ParticleWidth = boost::lexical_cast<float>(particleWidth);

    std::string partWeight(pElem->Attribute("particle_weight"));
    m_ParticleWeight = boost::lexical_cast<float>(partWeight);

    std::string startTemp(pElem->Attribute("temp_start"));
    m_StartTemperature = boost::lexical_cast<float>(startTemp);

    std::string endTemp(pElem->Attribute("temp_end"));
    m_EndTemperature = boost::lexical_cast<float>(endTemp);

    std::string inExBalance(pElem->Attribute("inexbalance"));
    m_InexBalance = boost::lexical_cast<float>(inExBalance);

    std::string fiberLength(pElem->Attribute("fiber_length"));
    m_MinFiberLength = boost::lexical_cast<float>(fiberLength);

    std::string curvThres(pElem->Attribute("curvature_threshold"));
    m_CurvatureThreshold = cos(boost::lexical_cast<float>(curvThres)*itk::Math::pi/180);
    m_AbortTracking = false;
    MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully";
    return true;
  }
  catch(...)
  {
    MITK_INFO << "GibbsTrackingFilter: could not load parameter file";
    return false;
  }
}

// save current tracking paramters to xml file (.gtp)
template< class ItkOdfImageType >
bool GibbsTrackingFilter< ItkOdfImageType >::SaveParameters()
{
  try
  {
    if( m_SaveParameterFile.length()==0 )
    {
      MITK_INFO << "GibbsTrackingFilter: no filename specified to save parameters";
      return true;
    }

    MITK_INFO << "GibbsTrackingFilter: saving parameter file " << m_SaveParameterFile;

    TiXmlDocument documentXML;
    TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" );
    documentXML.LinkEndChild( declXML );

    TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file");
    mainXML->SetAttribute("file_version",  "0.1");
    documentXML.LinkEndChild(mainXML);

    TiXmlElement* paramXML = new TiXmlElement("parameter_set");
    paramXML->SetAttribute("iterations", boost::lexical_cast<std::string>(m_Iterations));
    paramXML->SetAttribute("particle_length", boost::lexical_cast<std::string>(m_ParticleLength));
    paramXML->SetAttribute("particle_width", boost::lexical_cast<std::string>(m_ParticleWidth));
    paramXML->SetAttribute("particle_weight", boost::lexical_cast<std::string>(m_ParticleWeight));
    paramXML->SetAttribute("temp_start", boost::lexical_cast<std::string>(m_StartTemperature));
    paramXML->SetAttribute("temp_end", boost::lexical_cast<std::string>(m_EndTemperature));
    paramXML->SetAttribute("inexbalance", boost::lexical_cast<std::string>(m_InexBalance));
    paramXML->SetAttribute("fiber_length", boost::lexical_cast<std::string>(m_MinFiberLength));
    paramXML->SetAttribute("curvature_threshold", boost::lexical_cast<std::string>(m_CurvatureThreshold));
    mainXML->LinkEndChild(paramXML);

    if(!boost::algorithm::ends_with(m_SaveParameterFile, ".gtp"))
      m_SaveParameterFile.append(".gtp");
    documentXML.SaveFile( m_SaveParameterFile );

    MITK_INFO << "GibbsTrackingFilter: parameter file saved successfully";
    return true;
  }
  catch(...)
  {
    MITK_INFO << "GibbsTrackingFilter: could not save parameter file";
    return false;
  }
}

template< class ItkOdfImageType >
void GibbsTrackingFilter< ItkOdfImageType >::SetDicomProperties(mitk::FiberBundle::Pointer fib)
{
  std::string model_code_value = "-";
  std::string model_code_meaning = "-";
  std::string algo_code_value = "sup181_ee03";
  std::string algo_code_meaning = "Global";

  fib->SetProperty("DICOM.anatomy.value", mitk::StringProperty::New("T-A0095"));
  fib->SetProperty("DICOM.anatomy.meaning", mitk::StringProperty::New("White matter of brain and spinal cord"));

  fib->SetProperty("DICOM.algo_code.value", mitk::StringProperty::New(algo_code_value));
  fib->SetProperty("DICOM.algo_code.meaning", mitk::StringProperty::New(algo_code_meaning));

  fib->SetProperty("DICOM.model_code.value", mitk::StringProperty::New(model_code_value));
  fib->SetProperty("DICOM.model_code.meaning", mitk::StringProperty::New(model_code_meaning));
}

}


// Copyright (C) 2009-2012 Lorenzo Caminiti
// Distributed under the Boost Software License, Version 1.0
// (see accompanying file LICENSE_1_0.txt or a copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Home at http://www.boost.org/libs/local_function

#include <boost/local_function.hpp>
#include <boost/typeof/typeof.hpp>
#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
#include <boost/detail/lightweight_test.hpp>
#include <algorithm>
#include <vector>

struct calculator;
BOOST_TYPEOF_REGISTER_TYPE(calculator) // Register before `bind this_` below.

struct calculator {
    std::vector<int> results;

    void factorials(const std::vector<int>& nums) {
        int BOOST_LOCAL_FUNCTION( (bind this_) (int num)
                (bool recursion)(default false) ) {
            int result = 0;

            if(num <= 0) result = 1;
            else result = num * factorial(num - 1, true);

            if(!recursion) this_->results.push_back(result);
            return result;
        } BOOST_LOCAL_FUNCTION_NAME(recursive factorial)

        std::for_each(nums.begin(), nums.end(), factorial);
    }
};

int main(void) {
    std::vector<int> v(3);
    v[0] = 1; v[1] = 3; v[2] = 4;

    calculator calc;
    calc.factorials(v);
    BOOST_TEST(calc.results[0] == 1);
    BOOST_TEST(calc.results[1] == 6);
    BOOST_TEST(calc.results[2] == 24);
    return boost::report_errors();
}


#include <iostream>
#include "../include/Constants.h"
#include "../include/Constants_wrapper.h"
#include "../include/Commons.h"
#include "../include/Being.h"
#include "../include/Chromo.h"
#include "../include/Food.h"
#include "../include/DNA.h"
#include "../include/World.h"
#include "../include/AutoRNG.h"
#include <vector>
#include <random>
#include <chrono>
#include <fstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

int main() {

   constants_wrapper cfg;
   cfg.show();
   InitRNG RNG;
   RNG.seed(cfg.SEED);

   //food_point fp2(4.1, 4.2, cfg.default_nutrival);
   //food_point fp3(1.1, 2.2, cfg.default_nutrival);

   // create and open a character archive for output
   std::ofstream ofs("./my_food_points.txt");
   boost::archive::text_oarchive oa(ofs);

   std::uniform_real_distribution<float> food_distribution_x(cfg.X_MIN , cfg.X_MAX / 4 );
   std::uniform_real_distribution<float> food_distribution_y(cfg.Y_MIN , cfg.Y_MAX);
   for (int i = 0; i < cfg.N_FOOD_POINT_AT_START / 2 ; ++i) {
      food_point fpx( food_distribution_x(RNG.generator) , food_distribution_y(RNG.generator) , cfg.default_nutrival );

   // save data to archive
   // write class instance to archive
      oa << fpx;
   // archive and stream closed when destructors are called
    };
   float new_min_x = cfg.X_MAX - (cfg.X_MAX / 4);
   float new_max_x = cfg.X_MAX;
   float new_min_y = cfg.Y_MIN;
   float new_max_y = cfg.Y_MAX;
   std::cout << new_min_x << " ; " << new_max_x << " ; " << new_min_y << " ; " << new_max_y << std::endl;
   std::uniform_real_distribution<float> food_distribution_x_2(new_min_x , new_max_x );
   std::uniform_real_distribution<float> food_distribution_y_2(new_min_y , new_max_y );

   for (int i = cfg.N_FOOD_POINT_AT_START / 2; i < cfg.N_FOOD_POINT_AT_START; ++i) {
      food_point fpx( food_distribution_x_2(RNG.generator) , food_distribution_y_2(RNG.generator) , cfg.default_nutrival );

   // save data to archive
   // write class instance to archive
      oa << fpx;
   // archive and stream closed when destructors are called
    };

   return 0;
};


/*

Copyright (c) 2005-2022, University of Oxford.
All rights reserved.

University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.

This file is part of Chaste.

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 the University of Oxford 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 HOLDER 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.

*/

#include <cmath>
#include <iostream>
#include <boost/math/tools/roots.hpp>
#include <boost/bind.hpp>

#include "BoostTolerance.hpp"
#include "HiornsAirwayWall.hpp"
#include "MathsCustomFunctions.hpp"
#include "Exception.hpp"

HiornsAirwayWall::HiornsAirwayWall() : mTargetPressure(0),
                                       mRIn(0),
                                       mROut(0),
                                       mmu(0),
                                       mphi1(0),
                                       mphi2(0),
                                       mC1(0),
                                       mC2(0),
                                       mA(0)
{
}

HiornsAirwayWall::~HiornsAirwayWall() {}

void HiornsAirwayWall::SetTimestep(double dt) {}

double HiornsAirwayWall::CalculatePressureRadiusResidual(double radius)
{

    mTargetPressure = mAirwayPressure - mPleuralPressure;

    double rin = radius;

    double areaOfAirwayWall = M_PI*(mROut*mROut - mRIn*mRIn);
    double rout = sqrt(rin*rin + areaOfAirwayWall/M_PI);
    double pressure;

    pressure = mmu*log((rin*mROut)/(rout*mRIn)) + mmu*((rin*rin - mRIn*mRIn)/2.)*((1./rin*rin) - (1./rout*rout)) + 2*cos(mphi2)*cos(mphi2)*mA*log(mROut/mRIn);

    if (rin - mRIn > 0.)
        {

        //  THE COMMENTED OUT CODE CORRESPONDS TO THE PRELIMINARY VERSION OF THE HIORNS QUASI-STATIC
        //  AIRWAY WALL MODEL (I.E. EQUATION 1.1 IN THE HIORNS LOOKUP TABLES NOTES). THE VERSION THAT
        //  WE USE (FOLLOWING THE COMMENTED SECTION) INSTEAD IS MODIFIED SO THAT THE CONTRIBUTION OF
        //  THE COLLAGEN TO THE STRAIN-ENERGY LAW SATISFIES THE MODEL OF HOLZAPFEL ET AL.

        //  const int numberTrap = 10000;
        //  double integrandVals[numberTrap];
        //  double tVals[numberTrap];
        //  double integralStuff = 0.;

        //  double upperlimit = -1.;
        //  double lowerlimit = -1.;

        //  lowerlimit = ((sqrt(mC2)*(rout*rout - mROut*mROut)*cos(mphi1)*cos(mphi1))/((mROut)*(mROut)));
        //  upperlimit = ((sqrt(mC2)*(rin*rin - mRIn*mRIn)*cos(mphi1)*cos(mphi1))/((mRIn)*(mRIn)));

        //  for (int it = 0; it < numberTrap; it++)
        //  {

            //  double tVal = lowerlimit + ((double)it/((double)numberTrap - 1.))*(upperlimit - lowerlimit);
            //  tVals[it] = tVal;
            //  integrandVals[it] = (2.*exp(tVal*tVal)/sqrt(M_PI));

        //  }

        //  integralStuff = (0.5*(integrandVals[0] + integrandVals[numberTrap - 1]));
        //  integralStuff = integralStuff*(tVals[1] - tVals[0]);
        //  for (int i = 1; i < (numberTrap - 1); i++)
        //  {
            //  integralStuff = integralStuff + integrandVals[i]*(tVals[1] - tVals[0]);
        //  }

        //  pressure = pressure + mC1*sqrt(M_PI/mC2)*cos(mphi1)*cos(mphi1)*integralStuff;

            double integralStuff = exp(mC2*((rin*rin - mRIn*mRIn)/(mRIn*mRIn))*((rin*rin - mRIn*mRIn)/(mRIn*mRIn))*cos(mphi1)*cos(mphi1)*cos(mphi1)*cos(mphi1)) - exp(mC2*((rout*rout - mROut*mROut)/(mROut*mROut))*((rout*rout - mROut*mROut)/(mROut*mROut))*cos(mphi1)*cos(mphi1)*cos(mphi1)*cos(mphi1));
            pressure = pressure + (mC1/mC2)*cos(mphi1)*cos(mphi1)*integralStuff;

        }

    double residual = mTargetPressure - pressure;

    return residual;

}

void HiornsAirwayWall::SolveAndUpdateState(double tStart, double tEnd)
{

    double guess = (mRIn + mROut)/2.;
    double factor = 2.;

    Tolerance tol = 0.000001;
    boost::uintmax_t maxIterations = 500u;

    std::pair<double, double> found = boost::math::tools::bracket_and_solve_root(boost::bind(&HiornsAirwayWall::CalculatePressureRadiusResidual, this, _1), guess, factor, false, tol, maxIterations);
    mDeformedAirwayRadius = found.first;
}

void HiornsAirwayWall::SetRIn(double RIn)
{
    assert (RIn >= 0.0);
    mRIn = RIn;
}

void HiornsAirwayWall::SetROut(double ROut)
{
    assert (ROut >= 0.0);
    mROut = ROut;
}

void HiornsAirwayWall::Setmu(double mu)
{
    assert (mu >= 0.0);
    mmu = mu;
}

void HiornsAirwayWall::Setphi1(double phi1)
{
    mphi1 = phi1;
}

void HiornsAirwayWall::Setphi2(double phi2)
{
    mphi2 = phi2;
}

void HiornsAirwayWall::SetC1(double C1)
{
    mC1 = C1;
}

void HiornsAirwayWall::SetC2(double C2)
{
    mC2 = C2;
}

void HiornsAirwayWall::SetA(double A)
{
    mA = A;
}

double HiornsAirwayWall::GetLumenRadius()
{
    return mDeformedAirwayRadius;
}

void HiornsAirwayWall::SetAirwayPressure(double pressure)
{
    mAirwayPressure = pressure;
}

void HiornsAirwayWall::SetPleuralPressure(double pressure)
{
    mPleuralPressure = pressure;
}


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

/*!
Calibration of Heston model on SPX vol surface
*/

#include <ql/quantlib.hpp>
#include "CsvHelper.hpp"
#include "HestonTools.hpp"

#ifdef BOOST_MSVC
/* Uncomment the following lines to unmask floating-point
   exceptions. Warning: unpredictable results can arise...

   See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481
   Is there anyone with a definitive word about this?
*/
// #include <float.h>
// namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); }
#endif

#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>

#define DEBUG_OUT true

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib
{

Integer sessionId()
{
    return 0;
}

}
#endif

int main(int, char* [])
{

    Handle<Quote> s0(boost::shared_ptr<Quote>(new SimpleQuote(1290.59)));

    try
    {

// calibration data

        std::fstream file1("/home/phn/dev/R/ValidationModeles/calibration.csv", ios::in);
        if (!file1.is_open())
        {
            std::cout << "Calibration File not found!\n";
            return 1;
        }

        std::vector<CALIB> calibData;
        readCSV_Calib(file1, calibData);
        file1.close();

// interest rates and dividend yield

        std::fstream file2("/home/phn/dev/R/ValidationModeles/rate_div.csv", ios::in);
        if (!file2.is_open())
        {
            std::cout << "Rate/Yield File not found!\n";
            return 1;
        }

        std::vector<RD> RDData;
        readCSV_RD(file2, RDData);
        file2.close();

        std::vector<double> res;

        res = HestonCalibration(s0, calibData, RDData);

        if (DEBUG_OUT)
            std::cout << "Theta: " << res[0] <<
                      "\nKappa: " << res[1] <<
                      "\nsigma: " << res[2] <<
                      "\nrho: " << res[3] <<
                      "\nv0: " << res[4] <<
                      "\nsse: " << res[5] << std::endl;

    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    catch (...)
    {
        std::cerr << "unknown error" << std::endl;
        return 1;
    }
}


#include <cctbx/boost_python/flex_fwd.h>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>

#if defined(__linux__) && defined(__GNUC__) \
 && __GNUC__ == 2 && __GNUC_MINOR__ == 96
# undef isnan
#endif

#include <clipper/clipper.h>
#include <clipper/clipper-contrib.h>
#include <clipper/cctbx/clipper_cctbx.h>

namespace clipper { namespace boost_python {

  class Compute_phifom_from_abcd_interface
  {
    public:
      Compute_phifom_from_abcd_interface(
        cctbx::uctbx::unit_cell const& unit_cell,
        cctbx::sgtbx::space_group const& space_group,
        af::shared<cctbx::miller::index<> > const& miller_indices,
        af::const_ref<cctbx::hendrickson_lattman<> > const&
          phase_probabilities)
      :
        miller_indices_(miller_indices),
        hkls_(CCTBX::as_HKL_info(
          unit_cell, space_group, miller_indices.const_ref())),
        phiw_(hkls_)
      {
        CCTBX_ASSERT(hkls_.num_reflections() == miller_indices.size());
        HKL_data<data64::ABCD> abcd = CCTBX::as_HKL_data(
          hkls_, miller_indices_.const_ref(), phase_probabilities);
        phiw_.compute(abcd, data64::Compute_phifom_from_abcd());
      }

      af::shared<double>
      centroid_phases() const
      {
        return CCTBX::extract_centroid_phases(
          phiw_, miller_indices_.const_ref());
      }

      af::shared<double>
      figures_of_merit() const
      {
        return CCTBX::extract_figures_of_merit(
          phiw_, miller_indices_.const_ref());
      }

    protected:
      af::shared<cctbx::miller::index<> > miller_indices_;
      HKL_info hkls_;
      HKL_data<data64::Phi_fom> phiw_;
  };

  struct Compute_phifom_from_abcd_interface_wrappers
  {
    typedef Compute_phifom_from_abcd_interface w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("Compute_phifom_from_abcd_interface", no_init)
        .def(init<cctbx::uctbx::unit_cell const&,
                  cctbx::sgtbx::space_group const&,
                  af::shared<cctbx::miller::index<> > const&,
                  af::const_ref<cctbx::hendrickson_lattman<> > const&>((
          arg("unit_cell"),
          arg("space_group"),
          arg("miller_indices"),
          arg("phase_probabilities"))))
        .def("centroid_phases", &w_t::centroid_phases)
        .def("figures_of_merit", &w_t::figures_of_merit)
      ;
    }
  };

  void wrap_hendrickson_lattman()
  {
    Compute_phifom_from_abcd_interface_wrappers::wrap();
  }

}} // namespace clipper::boost_python


// Copyright (C) 2011 by the BEM++ Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "bempp/common/config_ahmed.hpp"

#ifdef WITH_AHMED

#include "../check_arrays_are_close.hpp"
#include "../type_template.hpp"
#include "../random_arrays.hpp"

#include "create_regular_grid.hpp"

#include "assembly/assembly_options.hpp"
#include "assembly/discrete_aca_boundary_operator.hpp"
#include "assembly/discrete_boundary_operator.hpp"
#include "assembly/boundary_operator.hpp"
#include "assembly/context.hpp"
#include "assembly/identity_operator.hpp"
#include "assembly/numerical_quadrature_strategy.hpp"

#include "assembly/laplace_3d_single_layer_boundary_operator.hpp"
#include "assembly/laplace_3d_double_layer_boundary_operator.hpp"
#include "assembly/laplace_3d_hypersingular_boundary_operator.hpp"
#include "assembly/helmholtz_3d_single_layer_boundary_operator.hpp"
#include "assembly/helmholtz_3d_hypersingular_boundary_operator.hpp"
#include "assembly/modified_helmholtz_3d_single_layer_boundary_operator.hpp"

#include "grid/grid.hpp"

#include "space/piecewise_linear_continuous_scalar_space.hpp"
#include "space/piecewise_constant_scalar_space.hpp"

#include <algorithm>
#include "common/armadillo_fwd.hpp"
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/version.hpp>
#include <complex>

// Tests

using namespace Bempp;

namespace
{

template <typename T> T initWaveNumber();
template <> float initWaveNumber() { return 1.2f; }
template <> double initWaveNumber(){ return 1.2; }
template <> std::complex<float> initWaveNumber()
{ return std::complex<float>(1.2f, 0.7f); }
template <> std::complex<double> initWaveNumber()
{ return std::complex<double>(1.2, 0.7); }

template <typename BFT, typename RT>
struct DiscreteAcaBoundaryOperatorFixture
{
    DiscreteAcaBoundaryOperatorFixture()
    {
        grid = createRegularTriangularGrid(4, 7);

        shared_ptr<Space<BFT> > pwiseConstants(
            new PiecewiseConstantScalarSpace<BFT>(grid));
        shared_ptr<Space<BFT> > pwiseLinears(
            new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

        AssemblyOptions assemblyOptions;
        assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
        AcaOptions acaOptions;
        acaOptions.minimumBlockSize = 2;
        assemblyOptions.switchToAcaMode(acaOptions);
        AccuracyOptions accuracyOptions;
        accuracyOptions.doubleRegular.setRelativeQuadratureOrder(4);
        accuracyOptions.doubleSingular.setRelativeQuadratureOrder(4);
        shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
                    new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));

        shared_ptr<Context<BFT, RT> > context(
            new Context<BFT, RT>(quadStrategy, assemblyOptions));

        const RT waveNumber = initWaveNumber<RT>();

        op = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>(
            context, pwiseConstants, pwiseConstants, pwiseLinears, waveNumber);
    }

    shared_ptr<Grid> grid;
    BoundaryOperator<BFT, RT> op;
};

template <typename BFT, typename RT>
struct DiscreteRealSymmetricAcaBoundaryOperatorFixture
{
    DiscreteRealSymmetricAcaBoundaryOperatorFixture()
    {
        grid = createRegularTriangularGrid(4, 7);

        shared_ptr<Space<BFT> > pwiseConstants(
            new PiecewiseConstantScalarSpace<BFT>(grid));

        AssemblyOptions assemblyOptions;
        assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
        AcaOptions acaOptions;
        acaOptions.minimumBlockSize = 2;
        assemblyOptions.switchToAcaMode(acaOptions);
        AccuracyOptions accuracyOptions;
        accuracyOptions.doubleRegular.setRelativeQuadratureOrder(4);
        accuracyOptions.doubleSingular.setRelativeQuadratureOrder(4);
        shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
                    new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));

        shared_ptr<Context<BFT, RT> > context(
            new Context<BFT, RT>(quadStrategy, assemblyOptions));

        op = laplace3dSingleLayerBoundaryOperator<BFT, RT>(
            context, pwiseConstants, pwiseConstants, pwiseConstants, "SLP",
                    SYMMETRIC | HERMITIAN);
    }

    shared_ptr<Grid> grid;
    BoundaryOperator<BFT, RT> op;
};

template <typename BFT, typename RT>
struct DiscreteComplexSymmetricAcaBoundaryOperatorFixture
{
    DiscreteComplexSymmetricAcaBoundaryOperatorFixture()
    {
        grid = createRegularTriangularGrid(4, 7);

        shared_ptr<Space<BFT> > pwiseConstants(
            new PiecewiseConstantScalarSpace<BFT>(grid));

        AssemblyOptions assemblyOptions;
        assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
        AcaOptions acaOptions;
        acaOptions.minimumBlockSize = 2;
        assemblyOptions.switchToAcaMode(acaOptions);
        AccuracyOptions accuracyOptions;
        accuracyOptions.doubleRegular.setRelativeQuadratureOrder(4);
        accuracyOptions.doubleSingular.setRelativeQuadratureOrder(4);
        shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
                    new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));
        shared_ptr<Context<BFT, RT> > context(
            new Context<BFT, RT>(quadStrategy, assemblyOptions));

        RT waveNumber = 1.;
        op = helmholtz3dSingleLayerBoundaryOperator<BFT>(
            context, pwiseConstants, pwiseConstants, pwiseConstants,
                    waveNumber, "SLP", SYMMETRIC);
    }

    shared_ptr<Grid> grid;
    BoundaryOperator<BFT, RT> op;
};

} // namespace

BOOST_AUTO_TEST_SUITE(DiscreteAcaBoundaryOperator)

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_0_and_y_initialized_to_nans, ResultType, result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(0.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->columnCount());
    arma::Col<RT> y(dop->rowCount());
    y.fill(std::numeric_limits<CT>::quiet_NaN());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(y.is_finite());
    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_plus_3j_and_beta_equal_to_0_and_y_initialized_to_nans, ResultType, complex_result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2., 3.);
    RT beta = static_cast<RT>(0.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->columnCount());
    arma::Col<RT> y(dop->rowCount());
    y.fill(std::numeric_limits<CT>::quiet_NaN());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(y.is_finite());
    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3, ResultType, result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->columnCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->rowCount());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x + beta * y;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_plus_3j_and_beta_equal_to_4_minus_5j, ResultType, complex_result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2., 3.);
    RT beta = static_cast<RT>(4., -5.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->columnCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->rowCount());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x + beta * y;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_transpose, ResultType, result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix().st() * x + beta * y;

    dop->apply(TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_plus_3j_and_beta_equal_to_4_minus_5j_and_transpose, ResultType, complex_result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2., 3.);
    RT beta = static_cast<RT>(4., -5.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix().st() * x + beta * y;

    dop->apply(TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_conjugate_transpose, ResultType, result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    // .t() gives conjugate transpose for complex matrices
    arma::Col<RT> expected = alpha * dop->asMatrix().t() * x + beta * y;

    dop->apply(CONJUGATE_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_plus_3j_and_beta_equal_to_4_minus_5j_and_conjugate_transpose, ResultType, complex_result_types)
{
    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2., 3.);
    RT beta = static_cast<RT>(4., -5.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    // .t() gives conjugate transpose for complex matrices
    arma::Col<RT> expected = alpha * dop->asMatrix().t() * x + beta * y;

    dop->apply(CONJUGATE_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_real_symmetric_operator, ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteRealSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x + beta * y;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_transpose_and_real_symmetric_operator, ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteRealSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix().st() * x + beta * y;

    dop->apply(TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_conjugate_transpose_and_real_symmetric_operator, ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteRealSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    // .t() gives conjugate transpose for complex matrices
    arma::Col<RT> expected = alpha * dop->asMatrix().t() * x + beta * y;

    dop->apply(CONJUGATE_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_complex_symmetric_operator,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteComplexSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix() * x + beta * y;

    dop->apply(NO_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_transpose_and_complex_symmetric_operator,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteComplexSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    arma::Col<RT> expected = alpha * dop->asMatrix().st() * x + beta * y;

    dop->apply(TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(builtin_apply_works_correctly_for_alpha_equal_to_2_and_beta_equal_to_3_and_conjugate_transpose_and_complex_symmetric_operator,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    std::srand(1);

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    DiscreteComplexSymmetricAcaBoundaryOperatorFixture<BFT, RT> fixture;
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = fixture.op.weakForm();

    RT alpha = static_cast<RT>(2.);
    RT beta = static_cast<RT>(3.);

    arma::Col<RT> x = generateRandomVector<RT>(dop->rowCount());
    arma::Col<RT> y = generateRandomVector<RT>(dop->columnCount());

    // .t() gives conjugate transpose for complex matrices
    arma::Col<RT> expected = alpha * dop->asMatrix().t() * x + beta * y;

    dop->apply(CONJUGATE_TRANSPOSE, x, y, alpha, beta);

    BOOST_CHECK(check_arrays_are_close<RT>(y, expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(acaOperatorSum_works_correctly_for_nonsymmetric_operators, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseConstants(
        new PiecewiseConstantScalarSpace<BFT>(grid));
    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    const RT waveNumber = initWaveNumber<RT>();

    BoundaryOperator<BFT, RT> op =
            modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>(
        context, pwiseConstants, pwiseConstants, pwiseLinears, waveNumber);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    BoundaryOperator<BFT, RT> op2 =
            laplace3dSingleLayerBoundaryOperator<BFT, RT>(
                context, pwiseConstants, pwiseConstants, pwiseLinears);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop2 = op2.weakForm();

    arma::Mat<RT> expected = dop->asMatrix() + dop2->asMatrix();

    const double eps = 1e-4;
    shared_ptr<const DiscreteBoundaryOperator<RT> > acaSum =
            acaOperatorSum(dop,
                           dop2,
                           eps, UINT_MAX);

    BOOST_CHECK(check_arrays_are_close<RT>(acaSum->asMatrix(), expected,
                                           eps));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(acaOperatorSum_works_correctly_for_real_symmetric_operators,
                              ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    BoundaryOperator<BFT, RT> op =
            laplace3dSingleLayerBoundaryOperator<BFT, RT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, "SLP",
                SYMMETRIC | HERMITIAN);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    BoundaryOperator<BFT, RT> op2 =
            laplace3dHypersingularBoundaryOperator<BFT, RT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, "Hyp",
                SYMMETRIC | HERMITIAN);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop2 = op2.weakForm();

    arma::Mat<RT> expected = dop->asMatrix() + dop2->asMatrix();

    const double eps = 1e-4;
    shared_ptr<const DiscreteBoundaryOperator<RT> > acaSum =
            acaOperatorSum(dop,
                           dop2,
                           eps, UINT_MAX);

    BOOST_CHECK(check_arrays_are_close<RT>(acaSum->asMatrix(), expected,
                                           eps));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(acaOperatorSum_works_correctly_for_complex_symmetric_operators,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    RT waveNumber = 1.;
    BoundaryOperator<BFT, RT> op =
            helmholtz3dSingleLayerBoundaryOperator<BFT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, waveNumber,
                "SLP", SYMMETRIC);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    // Note: here it is crucial that the wavenumber is real; otherwise
    // the hypersingular operator wouldn't be symmetric.
    BoundaryOperator<BFT, RT> op2 =
            helmholtz3dHypersingularBoundaryOperator<BFT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, waveNumber,
                "Hyp", SYMMETRIC);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop2 = op2.weakForm();

    arma::Mat<RT> expected = dop->asMatrix() + dop2->asMatrix();

    const double eps = 1e-4;
    shared_ptr<const DiscreteBoundaryOperator<RT> > acaSum =
            acaOperatorSum(dop,
                           dop2,
                           eps, UINT_MAX);

    BOOST_CHECK(check_arrays_are_close<RT>(acaSum->asMatrix(), expected,
                                           eps));
}

//BOOST_AUTO_TEST_CASE_TEMPLATE(acaOperatorSum_works_correctly_for_sum_of_real_symmetric_and_nonsymmetric_operators,
//                              ResultType, complex_result_types)
//{
//    if (boost::is_same<ResultType, std::complex<float> >())
//        return; // this type is not supported because of a deficiency in AHMED

//    typedef ResultType RT;
//    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
//    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

//    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

//    shared_ptr<Space<BFT> > pwiseLinears(
//        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

//    AssemblyOptions assemblyOptions;
//    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
//    assemblyOptions.switchToAcaMode(AcaOptions());
//    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
//        new NumericalQuadratureStrategy<BFT, RT>);
//    shared_ptr<Context<BFT, RT> > context(
//        new Context<BFT, RT>(quadStrategy, assemblyOptions));

//    BoundaryOperator<BFT, RT> op =
//            laplace3dSingleLayerBoundaryOperator<BFT, RT>(
//                context, pwiseLinears, pwiseLinears, pwiseLinears, "SLP",
//                SYMMETRIC | HERMITIAN);
//    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

//    BoundaryOperator<BFT, RT> op2 =
//            laplace3dDoubleLayerBoundaryOperator<BFT, RT>(
//                context, pwiseLinears, pwiseLinears, pwiseLinears, "DLP");
//    shared_ptr<const DiscreteBoundaryOperator<RT> > dop2 = op2.weakForm();

//    arma::Mat<RT> expected = dop->asMatrix() + dop2->asMatrix();

//    const double eps = 1e-4;
//    shared_ptr<const DiscreteBoundaryOperator<RT> > acaSum =
//            acaOperatorSum(dop,
//                           dop2,
//                           eps, UINT_MAX);

//    BOOST_CHECK(check_arrays_are_close<RT>(acaSum->asMatrix(), expected,
//                                           eps));
//}

BOOST_AUTO_TEST_CASE_TEMPLATE(scaledAcaOperator_works_correctly_for_nonsymmetric_operators,
                              ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseConstants(
        new PiecewiseConstantScalarSpace<BFT>(grid));
    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    const RT waveNumber = initWaveNumber<RT>();

    BoundaryOperator<BFT, RT> op =
            modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>(
        context, pwiseConstants, pwiseConstants, pwiseLinears, waveNumber);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    arma::Mat<RT> expected = waveNumber * dop->asMatrix();

    shared_ptr<const DiscreteBoundaryOperator<RT> > scaled =
            scaledAcaOperator(dop,
                              waveNumber);

    BOOST_CHECK(check_arrays_are_close<RT>(scaled->asMatrix(), expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(scaledAcaOperator_works_correctly_for_real_symmetric_operators,
                              ResultType, result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    BoundaryOperator<BFT, RT> op =
            laplace3dSingleLayerBoundaryOperator<BFT, RT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, "SLP",
                SYMMETRIC | HERMITIAN);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    const RT multiplier = 5.25;
    arma::Mat<RT> expected = multiplier * dop->asMatrix();

    shared_ptr<const DiscreteBoundaryOperator<RT> > scaled =
            scaledAcaOperator(dop,
                              multiplier);

    BOOST_CHECK(check_arrays_are_close<RT>(scaled->asMatrix(), expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(scaledAcaOperator_works_correctly_for_complex_symmetric_operators_and_real_scalar,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    RT waveNumber = 1.;
    BoundaryOperator<BFT, RT> op =
            helmholtz3dSingleLayerBoundaryOperator<BFT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, waveNumber,
                "SLP", SYMMETRIC);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    const RT multiplier = 5.25;
    arma::Mat<RT> expected = multiplier * dop->asMatrix();

    shared_ptr<const DiscreteBoundaryOperator<RT> > scaled =
            scaledAcaOperator(dop,
                              multiplier);

    BOOST_CHECK(check_arrays_are_close<RT>(scaled->asMatrix(), expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(scaledAcaOperator_works_correctly_for_complex_symmetric_operators_and_complex_scalar,
                              ResultType, complex_result_types)
{
    if (boost::is_same<ResultType, std::complex<float> >())
        return; // this type is not supported because of a deficiency in AHMED

    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    shared_ptr<Grid> grid = createRegularTriangularGrid(10, 3);

    shared_ptr<Space<BFT> > pwiseLinears(
        new PiecewiseLinearContinuousScalarSpace<BFT>(grid));

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    assemblyOptions.switchToAcaMode(AcaOptions());
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    RT waveNumber = 1.;
    BoundaryOperator<BFT, RT> op =
            helmholtz3dSingleLayerBoundaryOperator<BFT>(
                context, pwiseLinears, pwiseLinears, pwiseLinears, waveNumber,
                "SLP", SYMMETRIC);
    shared_ptr<const DiscreteBoundaryOperator<RT> > dop = op.weakForm();

    const RT multiplier(5.25, 4.);
    arma::Mat<RT> expected = multiplier * dop->asMatrix();

    shared_ptr<const DiscreteBoundaryOperator<RT> > scaled =
            scaledAcaOperator(dop,
                              multiplier);

    BOOST_CHECK(check_arrays_are_close<RT>(scaled->asMatrix(), expected,
                                           10. * std::numeric_limits<CT>::epsilon()));
}

BOOST_AUTO_TEST_SUITE_END()

#endif // WITH_AHMED