/*

Copyright (c) 2007-2015, Un Shyam, Arvid Norberg, Steven Siloti
All rights reserved.

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 author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)

#include <boost/cstdint.hpp>
#include <algorithm>

extern "C" {
#include "libtorrent/tommath.h"
}

#include "libtorrent/random.hpp"
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	namespace
	{
		const unsigned char dh_prime[96] = {
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
			0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
			0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
			0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
			0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
			0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
			0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
			0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63
		};
	}

	struct mp_bigint
	{
		mp_bigint()
		{ mp_init(&v); }
		mp_int* operator&() { return &v; }
		~mp_bigint() { mp_clear(&v); }
	private:
		// non-copyable
		mp_bigint(mp_bigint const&);
		mp_bigint const& operator=(mp_bigint const&);
		mp_int v;
	};

	// Set the prime P and the generator, generate local public key
	dh_key_exchange::dh_key_exchange()
	{
		// create local key
		for (int i = 0; i < int(sizeof(m_dh_local_secret)); ++i)
			m_dh_local_secret[i] = random() & 0xff;

		mp_bigint prime;
		mp_bigint secret;
		mp_bigint key;

		if (mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)))
			return;
		if (mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)))
			return;

		// generator is 2
		mp_set_int(&key, 2);
		// key = (2 ^ secret) % prime
		if (mp_exptmod(&key, &secret, &prime, &key))
			return;

		// key is now our local key
		int size = mp_unsigned_bin_size(&key);
		memset(m_dh_local_key, 0, sizeof(m_dh_local_key) - size);
		mp_to_unsigned_bin(&key, (unsigned char*)m_dh_local_key + sizeof(m_dh_local_key) - size);
	}

	char const* dh_key_exchange::get_local_key() const
	{
		return m_dh_local_key;
	}

	// compute shared secret given remote public key
	int dh_key_exchange::compute_secret(char const* remote_pubkey)
	{
		TORRENT_ASSERT(remote_pubkey);
		mp_bigint prime;
		mp_bigint secret;
		mp_bigint remote_key;

		if (mp_read_unsigned_bin(&prime, dh_prime, sizeof(dh_prime)))
			return -1;
		if (mp_read_unsigned_bin(&secret, (unsigned char*)m_dh_local_secret, sizeof(m_dh_local_secret)))
			return -1;
		if (mp_read_unsigned_bin(&remote_key, (unsigned char*)remote_pubkey, 96))
			return -1;

		if (mp_exptmod(&remote_key, &secret, &prime, &remote_key))
			return -1;

		// remote_key is now the shared secret
		int size = mp_unsigned_bin_size(&remote_key);
		memset(m_dh_shared_secret, 0, sizeof(m_dh_shared_secret) - size);
		mp_to_unsigned_bin(&remote_key, (unsigned char*)m_dh_shared_secret + sizeof(m_dh_shared_secret) - size);

		// calculate the xor mask for the obfuscated hash
		hasher h;
		h.update("req3", 4);
		h.update(m_dh_shared_secret, sizeof(m_dh_shared_secret));
		m_xor_mask = h.final();
		return 0;
	}

	int encryption_handler::encrypt(std::vector<asio::mutable_buffer>& iovec)
	{
		TORRENT_ASSERT(!m_send_barriers.empty());
		TORRENT_ASSERT(m_send_barriers.front().enc_handler);

		int to_process = m_send_barriers.front().next;

		if (to_process != INT_MAX)
		{
			for (std::vector<asio::mutable_buffer>::iterator i = iovec.begin();
				to_process >= 0; ++i)
			{
				if (to_process == 0)
				{
					iovec.erase(i, iovec.end());
					break;
				}
				else if (to_process < asio::buffer_size(*i))
				{
					*i = asio::mutable_buffer(asio::buffer_cast<void*>(*i), to_process);
					iovec.erase(++i, iovec.end());
					to_process = 0;
					break;
				}
				to_process -= asio::buffer_size(*i);
			}
			TORRENT_ASSERT(to_process == 0);
		}

#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
		to_process = 0;
		for (std::vector<asio::mutable_buffer>::iterator i = iovec.begin();
			i != iovec.end(); ++i)
			to_process += asio::buffer_size(*i);
#endif

		int next_barrier = 0;
		if (iovec.empty() || (next_barrier = m_send_barriers.front().enc_handler->encrypt(iovec)))
		{
			if (m_send_barriers.front().next != INT_MAX)
			{
				if (m_send_barriers.size() == 1)
					// transitioning back to plaintext
					next_barrier = INT_MAX;
				m_send_barriers.pop_front();
			}

#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
			if (next_barrier != INT_MAX)
			{
				int overhead = 0;
				for (std::vector<asio::mutable_buffer>::iterator i = iovec.begin();
					i != iovec.end(); ++i)
					overhead += asio::buffer_size(*i);
				TORRENT_ASSERT(overhead + to_process == next_barrier);
			}
#endif
		}
		else
		{
			iovec.clear();
		}
		return next_barrier;
	}

	int encryption_handler::decrypt(crypto_receive_buffer& recv_buffer, std::size_t& bytes_transferred)
	{
		TORRENT_ASSERT(!is_recv_plaintext());
		int consume = 0;
		if (recv_buffer.crypto_packet_finished())
		{
			std::vector<asio::mutable_buffer> wr_buf;
			recv_buffer.mutable_buffers(wr_buf, bytes_transferred);
			int packet_size = 0;
			int produce = bytes_transferred;
			m_dec_handler->decrypt(wr_buf, consume, produce, packet_size);
			TORRENT_ASSERT(packet_size || produce);
			TORRENT_ASSERT(packet_size >= 0);
			bytes_transferred = produce;
			if (packet_size)
				recv_buffer.crypto_cut(consume, packet_size);
		}
		else
			bytes_transferred = 0;
		return consume;
	}

	bool encryption_handler::switch_send_crypto(boost::shared_ptr<crypto_plugin> crypto
		, int pending_encryption)
	{
		bool place_barrier = false;
		if (!m_send_barriers.empty())
		{
			std::list<barrier>::iterator end = m_send_barriers.end(); --end;
			for (std::list<barrier>::iterator b = m_send_barriers.begin();
				b != end; ++b)
				pending_encryption -= b->next;
			TORRENT_ASSERT(pending_encryption >= 0);
			m_send_barriers.back().next = pending_encryption;
		}
		else if (crypto)
			place_barrier = true;

		if (crypto)
			m_send_barriers.push_back(barrier(crypto, INT_MAX));

		return place_barrier;
	}

	void encryption_handler::switch_recv_crypto(boost::shared_ptr<crypto_plugin> crypto
		, crypto_receive_buffer& recv_buffer)
	{
		m_dec_handler = crypto;
		int packet_size = 0;
		if (crypto)
		{
			int consume = 0;
			int produce = 0;
			std::vector<asio::mutable_buffer> wr_buf;
			crypto->decrypt(wr_buf, consume, produce, packet_size);
			TORRENT_ASSERT(wr_buf.empty());
			TORRENT_ASSERT(consume == 0);
			TORRENT_ASSERT(produce == 0);
		}
		recv_buffer.crypto_reset(packet_size);
	}

	rc4_handler::rc4_handler()
		: m_encrypt(false)
		, m_decrypt(false)
	{
		m_rc4_incoming.x = 0;
		m_rc4_incoming.y = 0;
		m_rc4_outgoing.x = 0;
		m_rc4_outgoing.y = 0;
	}

	void rc4_handler::set_incoming_key(unsigned char const* key, int len)
	{
		m_decrypt = true;
		rc4_init(key, len, &m_rc4_incoming);
		// Discard first 1024 bytes
		char buf[1024];
		std::vector<boost::asio::mutable_buffer> vec(1, boost::asio::mutable_buffer(buf, 1024));
		int consume = 0;
		int produce = 0;
		int packet_size = 0;
		decrypt(vec, consume, produce, packet_size);
	}

	void rc4_handler::set_outgoing_key(unsigned char const* key, int len)
	{
		m_encrypt = true;
		rc4_init(key, len, &m_rc4_outgoing);
		// Discard first 1024 bytes
		char buf[1024];
		std::vector<boost::asio::mutable_buffer> vec(1, boost::asio::mutable_buffer(buf, 1024));
		encrypt(vec);
	}

	int rc4_handler::encrypt(std::vector<boost::asio::mutable_buffer>& buf)
	{
		if (!m_encrypt) return 0;
		if (buf.empty()) return 0;

		int bytes_processed = 0;
		for (std::vector<boost::asio::mutable_buffer>::iterator i = buf.begin();
			i != buf.end(); ++i)
		{
			char* pos = boost::asio::buffer_cast<char*>(*i);
			int len = boost::asio::buffer_size(*i);

			TORRENT_ASSERT(len >= 0);
			TORRENT_ASSERT(pos);

			bytes_processed += len;
			rc4_encrypt((unsigned char*)pos, len, &m_rc4_outgoing);
		}
		buf.clear();
		return bytes_processed;
	}

	void rc4_handler::decrypt(std::vector<boost::asio::mutable_buffer>& buf
		, int& consume
		, int& produce
		, int& packet_size)
	{
		// these are out-parameters that are not set
		TORRENT_UNUSED(consume);
		TORRENT_UNUSED(packet_size);

		if (!m_decrypt) return;

		int bytes_processed = 0;
		for (std::vector<boost::asio::mutable_buffer>::iterator i = buf.begin();
			i != buf.end(); ++i)
		{
			char* pos = boost::asio::buffer_cast<char*>(*i);
			int len = boost::asio::buffer_size(*i);

			TORRENT_ASSERT(len >= 0);
			TORRENT_ASSERT(pos);

			bytes_processed += len;
			rc4_encrypt(reinterpret_cast<unsigned char*>(pos), len, &m_rc4_incoming);
		}
		buf.clear();
		produce = bytes_processed;
	}

} // namespace libtorrent

// All this code is based on libTomCrypt (http://www.libtomcrypt.com/)
// this library is public domain and has been specially
// tailored for libtorrent by Arvid Norberg

void rc4_init(const unsigned char* in, unsigned long len, rc4 *state)
{
	unsigned char key[256], tmp, *s;
	int keylen, x, y, j;

	TORRENT_ASSERT(state != 0);
	TORRENT_ASSERT(len <= 256);

	state->x = 0;
	while (len--) {
		state->buf[state->x++] = *in++;
	}

	/* extract the key */
	s = state->buf;
	memcpy(key, s, 256);
	keylen = state->x;

	/* make RC4 perm and shuffle */
	for (x = 0; x < 256; x++) {
		s[x] = x;
	}

	for (j = x = y = 0; x < 256; x++) {
		y = (y + state->buf[x] + key[j++]) & 255;
		if (j == keylen) {
			j = 0;
		}
		tmp = s[x]; s[x] = s[y]; s[y] = tmp;
	}
	state->x = 0;
	state->y = 0;
}

unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state)
{
	unsigned char x, y, *s, tmp;
	unsigned long n;

	TORRENT_ASSERT(out != 0);
	TORRENT_ASSERT(state != 0);

	n = outlen;
	x = state->x;
	y = state->y;
	s = state->buf;
	while (outlen--) {
		x = (x + 1) & 255;
		y = (y + s[x]) & 255;
		tmp = s[x]; s[x] = s[y]; s[y] = tmp;
		tmp = (s[x] + s[y]) & 255;
		*out++ ^= s[tmp];
	}
	state->x = x;
	state->y = y;
	return n;
}

#endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)


/**
 * @file rnn.hpp
 * @author Marcus Edel
 *
 * Definition of the RNN class, which implements recurrent neural networks.
 */
#ifndef __MLPACK_METHODS_ANN_RNN_HPP
#define __MLPACK_METHODS_ANN_RNN_HPP

#include <mlpack/core.hpp>

#include <boost/ptr_container/ptr_vector.hpp>

#include <mlpack/methods/ann/network_util.hpp>
#include <mlpack/methods/ann/layer/layer_traits.hpp>
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
#include <mlpack/methods/ann/performance_functions/cee_function.hpp>
#include <mlpack/core/optimizers/sgd/sgd.hpp>

namespace mlpack {
namespace ann /** Artificial Neural Network. */ {

/**
 * Implementation of a standard recurrent neural network.
 *
 * @tparam LayerTypes Contains all layer modules used to construct the network.
 * @tparam OutputLayerType The outputlayer type used to evaluate the network.
 * @tparam InitializationRuleType Rule used to initialize the weight matrix.
 * @tparam PerformanceFunction Performance strategy used to calculate the error.
 */
template <
  typename LayerTypes,
  typename OutputLayerType,
  typename InitializationRuleType = NguyenWidrowInitialization,
  class PerformanceFunction = CrossEntropyErrorFunction<>
>
class RNN
{
 public:
  //! Convenience typedef for the internal model construction.
  using NetworkType = RNN<LayerTypes,
                          OutputLayerType,
                          InitializationRuleType,
                          PerformanceFunction>;

  /**
   * Create the RNN object with the given predictors and responses set (this is
   * the set that is used to train the network) and the given optimizer.
   * Optionally, specify which initialize rule and performance function should
   * be used.
   *
   * @param network Network modules used to construct the network.
   * @param outputLayer Outputlayer used to evaluate the network.
   * @param predictors Input training variables.
   * @param responses Outputs resulting from input training variables.
   * @param optimizer Instantiated optimizer used to train the model.
   * @param initializeRule Optional instantiated InitializationRule object
   *        for initializing the network paramter.
   * @param performanceFunction Optional instantiated PerformanceFunction
   *        object used to claculate the error.
   */
  template<typename LayerType,
           typename OutputType,
           template<typename> class OptimizerType>
  RNN(LayerType &&network,
      OutputType &&outputLayer,
      const arma::mat& predictors,
      const arma::mat& responses,
      OptimizerType<NetworkType>& optimizer,
      InitializationRuleType initializeRule = InitializationRuleType(),
      PerformanceFunction performanceFunction = PerformanceFunction());

  /**
   * Create the RNN object with the given predictors and responses set (this is
   * the set that is used to train the network). Optionally, specify which
   * initialize rule and performance function should be used.
   *
   * @param network Network modules used to construct the network.
   * @param outputLayer Outputlayer used to evaluate the network.
   * @param predictors Input training variables.
   * @param responses Outputs resulting from input training variables.
   * @param initializeRule Optional instantiated InitializationRule object
   *        for initializing the network paramter.
   * @param performanceFunction Optional instantiated PerformanceFunction
   *        object used to claculate the error.
   */
  template<typename LayerType, typename OutputType>
  RNN(LayerType &&network,
      OutputType &&outputLayer,
      const arma::mat& predictors,
      const arma::mat& responses,
      InitializationRuleType initializeRule = InitializationRuleType(),
      PerformanceFunction performanceFunction = PerformanceFunction());

  /**
   * Create the RNN object with an empty predictors and responses set and
   * default optimizer. Make sure to call Train(predictors, responses) when
   * training.
   *
   * @param network Network modules used to construct the network.
   * @param outputLayer Outputlayer used to evaluate the network.
   * @param initializeRule Optional instantiated InitializationRule object
   *        for initializing the network paramter.
   * @param performanceFunction Optional instantiated PerformanceFunction
   *        object used to claculate the error.
   */
  template<typename LayerType, typename OutputType>
  RNN(LayerType &&network,
      OutputType &&outputLayer,
      InitializationRuleType initializeRule = InitializationRuleType(),
      PerformanceFunction performanceFunction = PerformanceFunction());

  /**
   * Train the recurrent neural network on the given input data. By default, the
   * SGD optimization algorithm is used, but others can be specified
   * (such as mlpack::optimization::RMSprop).
   *
   * This will use the existing model parameters as a starting point for the
   * optimization. If this is not what you want, then you should access the
   * parameters vector directly with Parameters() and modify it as desired.
   *
   * @tparam OptimizerType Type of optimizer to use to train the model.
   * @param predictors Input training variables.
   * @param responses Outputs results from input training variables.
   */
  template<
      template<typename> class OptimizerType = mlpack::optimization::SGD
  >
  void Train(const arma::mat& predictors, const arma::mat& responses);

  /**
   * Train the recurrent neural network with the given instantiated optimizer.
   * Using this overload allows configuring the instantiated optimizer before
   * training is performed.
   *
   * This will use the existing model parameters as a starting point for the
   * optimization. If this is not what you want, then you should access the
   * parameters vector directly with Parameters() and modify it as desired.
   *
   * @param optimizer Instantiated optimizer used to train the model.
   */
  template<
      template<typename> class OptimizerType = mlpack::optimization::SGD
  >
  void Train(OptimizerType<NetworkType>& optimizer);

  /**
   * Train the recurrent neural network on the given input data using the given
   * optimizer.
   *
   * This will use the existing model parameters as a starting point for the
   * optimization. If this is not what you want, then you should access the
   * parameters vector directly with Parameters() and modify it as desired.
   *
   * @tparam OptimizerType Type of optimizer to use to train the model.
   * @param predictors Input training variables.
   * @param responses Outputs results from input training variables.
   * @param optimizer Instantiated optimizer used to train the model.
   */
  template<
      template<typename> class OptimizerType = mlpack::optimization::SGD
  >
  void Train(const arma::mat& predictors,
             const arma::mat& responses,
             OptimizerType<NetworkType>& optimizer);

  /**
   * Predict the responses to a given set of predictors. The responses will
   * reflect the output of the given output layer as returned by the
   * OutputClass() function.
   *
   * @param predictors Input predictors.
   * @param responses Matrix to put output predictions of responses into.
   */
  void Predict(arma::mat& predictors, arma::mat& responses);

  /**
   * Evaluate the recurrent neural network with the given parameters. This
   * function is usually called by the optimizer to train the model.
   *
   * @param parameters Matrix model parameters.
   * @param i Index of point to use for objective function evaluation.
   * @param deterministic Whether or not to train or test the model. Note some
   * layer act differently in training or testing mode.
   */
  double Evaluate(const arma::mat& parameters,
                  const size_t i,
                  const bool deterministic = true);

  /**
   * Evaluate the gradient of the recurrent neural network with the given
   * parameters, and with respect to only one point in the dataset. This is
   * useful for optimizers such as SGD, which require a separable objective
   * function.
   *
   * @param parameters Matrix of the model parameters to be optimized.
   * @param i Index of points to use for objective function gradient evaluation.
   * @param gradient Matrix to output gradient into.
   */
  void Gradient(const arma::mat& parameters,
                const size_t i,
                arma::mat& gradient);

  //! Return the number of separable functions (the number of predictor points).
  size_t NumFunctions() const { return numFunctions; }

  //! Return the initial point for the optimization.
  const arma::mat& Parameters() const { return parameter; }
  //! Modify the initial point for the optimization.
  arma::mat& Parameters() { return parameter; }

  //! Serialize the model.
  template<typename Archive>
  void Serialize(Archive& ar, const unsigned int /* version */);

 private:
  /*
   * Predict the response of the given input matrix.
   */
  template <typename DataType>
  void SinglePredict(const DataType& input, DataType& output)
  {
    deterministic = true;
    seqLen = input.n_rows / inputSize;
    ResetParameter(network);

    // Iterate through the input sequence and perform the feed forward pass.
    for (seqNum = 0; seqNum < seqLen; seqNum++)
    {
      // Perform the forward pass and save the activations.
      Forward(input.rows(seqNum * inputSize, (seqNum + 1) * inputSize - 1),
          network);
      SaveActivations(network);

      // Retrieve output of the subsequence.
      if (seqOutput)
      {
        DataType seqOutput;
        OutputPrediction(seqOutput, network);
        output = arma::join_cols(output, seqOutput);
      }
    }

    // Retrieve output of the complete sequence.
    if (!seqOutput)
      OutputPrediction(output, network);
  }

  /**
   * Reset the network by clearing the layer activations and by setting the
   * layer status.
   */
  template<size_t I = 0, typename... Tp>
  typename std::enable_if<I == sizeof...(Tp), void>::type
  ResetParameter(std::tuple<Tp...>& /* unused */)
  {
    activations.clear();
  }

  template<size_t I = 0, typename... Tp>
  typename std::enable_if<I < sizeof...(Tp), void>::type
  ResetParameter(std::tuple<Tp...>& network)
  {
    ResetDeterministic(std::get<I>(network));
    ResetSeqLen(std::get<I>(network));
    ResetRecurrent(std::get<I>(network), std::get<I>(network).InputParameter());
    std::get<I>(network).Delta().zeros();

    ResetParameter<I + 1, Tp...>(network);
  }

  /**
   * Reset the layer status by setting the current deterministic parameter
   * for all layer that implement the Deterministic function.
   */
  template<typename T>
  typename std::enable_if<
      HasDeterministicCheck<T, bool&(T::*)(void)>::value, void>::type
  ResetDeterministic(T& layer)
  {
    layer.Deterministic() = deterministic;
  }

  template<typename T>
  typename std::enable_if<
      !HasDeterministicCheck<T, bool&(T::*)(void)>::value, void>::type
  ResetDeterministic(T& /* unused */) { /* Nothing to do here */ }

  /**
   * Reset the layer sequence length by setting the current seqLen parameter
   * for all layer that implement the SeqLen function.
   */
  template<typename T>
  typename std::enable_if<
      HasSeqLenCheck<T, size_t&(T::*)(void)>::value, void>::type
  ResetSeqLen(T& layer)
  {
    layer.SeqLen() = seqLen;
  }

  template<typename T>
  typename std::enable_if<
      !HasSeqLenCheck<T, size_t&(T::*)(void)>::value, void>::type
  ResetSeqLen(T& /* unused */) { /* Nothing to do here */ }

  /**
   * Distinguish between recurrent layer and non-recurrent layer when resetting
   * the recurrent parameter.
   */
  template<typename T, typename P>
  typename std::enable_if<
      HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  ResetRecurrent(T& layer, P& /* unused */)
  {
    layer.RecurrentParameter().zeros();
  }

  template<typename T, typename P>
  typename std::enable_if<
      !HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  ResetRecurrent(T& /* unused */, P& /* unused */)
  {
    /* Nothing to do here */
  }

  /**
   * Initialize the network by setting the input size and output size.
   */
  template<size_t I = 0, typename InputDataType, typename TargetDataType,
      typename... Tp>
  typename std::enable_if<I == sizeof...(Tp) - 1, void>::type
  InitLayer(const InputDataType& /* unused */,
            const TargetDataType& target,
            std::tuple<Tp...>& /* unused */)
  {
    seqOutput = outputSize < target.n_elem ? true : false;
  }

  template<size_t I = 0, typename InputDataType, typename TargetDataType,
      typename... Tp>
  typename std::enable_if<I < sizeof...(Tp) - 1, void>::type
  InitLayer(const InputDataType& input,
            const TargetDataType& target,
            std::tuple<Tp...>& network)
  {
    Init(std::get<I>(network), std::get<I>(network).OutputParameter(),
       std::get<I + 1>(network).Delta());

    InitLayer<I + 1, InputDataType, TargetDataType, Tp...>(input, target,
        network);
  }

  /**
   * Retrieve the weight matrix for all layer that implement the Weights
   * function to extract the input size and output size.
   */
  template<typename T, typename P, typename D>
  typename std::enable_if<
      HasGradientCheck<T, void(T::*)(const D&, P&)>::value, void>::type
  Init(T& layer, P& /* unused */, D& /* unused */)
  {
    // Initialize the input size only once.
    if (!inputSize)
      inputSize = layer.Weights().n_cols;

    outputSize = layer.Weights().n_rows;
  }

  template<typename T, typename P, typename D>
  typename std::enable_if<
      !HasGradientCheck<T, void(T::*)(const P&, D&)>::value, void>::type
  Init(T& /* unused */, P& /* unused */, D& /* unused */)
  {
    /* Nothing to do here */
  }

  /**
   * Save the network layer activations.
   */
  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I == Max, void>::type
  SaveActivations(std::tuple<Tp...>& /* unused */)
  {
    Save(I, std::get<I>(network), std::get<I>(network).InputParameter());
    LinkRecurrent(network);
  }

  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I < Max, void>::type
  SaveActivations(std::tuple<Tp...>& network)
  {
    Save(I, std::get<I>(network), std::get<I>(network).InputParameter());
    SaveActivations<I + 1, Max, Tp...>(network);
  }

  /**
   * Distinguish between recurrent layer and non-recurrent layer when storing
   * the activations.
   */
  template<typename T, typename P>
  typename std::enable_if<
      HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  Save(const size_t layerNumber, T& layer, P& /* unused */)
  {
    if (activations.size() == layerNumber)
    {
      activations.push_back(new arma::mat(layer.RecurrentParameter().n_rows,
          seqLen));
    }

    activations[layerNumber].unsafe_col(seqNum) = layer.RecurrentParameter();
  }

  template<typename T, typename P>
  typename std::enable_if<
      !HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  Save(const size_t layerNumber, T& layer, P& /* unused */)
  {
    if (activations.size() == layerNumber)
    {
      activations.push_back(new arma::mat(layer.OutputParameter().n_rows,
          seqLen));
    }

    activations[layerNumber].unsafe_col(seqNum) = layer.OutputParameter();
  }

  /**
   * Load the network layer activations.
   */
  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename DataType, typename... Tp
  >
  typename std::enable_if<I == Max, void>::type
  LoadActivations(DataType& input, std::tuple<Tp...>& network)
  {
    Load(I, std::get<I>(network), std::get<I>(network).InputParameter());
    std::get<0>(network).InputParameter() = input;
  }

  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename DataType, typename... Tp
  >
  typename std::enable_if<I < Max, void>::type
  LoadActivations(DataType& input, std::tuple<Tp...>& network)
  {
    Load(I, std::get<I>(network), std::get<I>(network).InputParameter());
    LoadActivations<I + 1, Max, DataType, Tp...>(input, network);
  }

  /**
   * Distinguish between recurrent layer and non-recurrent layer when storing
   * the activations.
   */
  template<typename T, typename P>
  typename std::enable_if<
      HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  Load(const size_t layerNumber, T& layer, P& /* unused */)
  {
    layer.RecurrentParameter() = activations[layerNumber].unsafe_col(seqNum);
  }

  template<typename T, typename P>
  typename std::enable_if<
      !HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  Load(const size_t layerNumber, T& layer, P& /* unused */)
  {
    layer.OutputParameter() = activations[layerNumber].unsafe_col(seqNum);
  }

  /**
   * Run a single iteration of the feed forward algorithm, using the given
   * input and target vector, store the calculated error into the error
   * vector.
   */
  template<size_t I = 0, typename DataType, typename... Tp>
  void Forward(const DataType& input, std::tuple<Tp...>& network)
  {
    std::get<I>(network).InputParameter() = input;
    std::get<I>(network).Forward(std::get<I>(network).InputParameter(),
        std::get<I>(network).OutputParameter());

    ForwardTail<I + 1, Tp...>(network);
  }

  template<size_t I = 1, typename... Tp>
  typename std::enable_if<I == sizeof...(Tp), void>::type
  ForwardTail(std::tuple<Tp...>& /* unused */) { /* Nothing to do here */ }

  template<size_t I = 1, typename... Tp>
  typename std::enable_if<I < sizeof...(Tp), void>::type
  ForwardTail(std::tuple<Tp...>& network)
  {
    std::get<I>(network).Forward(std::get<I - 1>(network).OutputParameter(),
        std::get<I>(network).OutputParameter());

    ForwardTail<I + 1, Tp...>(network);
  }

  /**
   * Link the calculated activation with the correct layer.
   */
  template<
      size_t I = 1,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I == Max, void>::type
  LinkParameter(std::tuple<Tp ...>& /* unused */)
  {
    if (!LayerTraits<typename std::remove_reference<
        decltype(std::get<I>(network))>::type>::IsBiasLayer)
    {
      std::get<I>(network).InputParameter() = std::get<I - 1>(
          network).OutputParameter();
    }
  }

  template<
      size_t I = 1,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I < Max, void>::type
  LinkParameter(std::tuple<Tp...>& network)
  {
    if (!LayerTraits<typename std::remove_reference<
        decltype(std::get<I>(network))>::type>::IsBiasLayer)
    {
      std::get<I>(network).InputParameter() = std::get<I - 1>(
          network).OutputParameter();
    }

    LinkParameter<I + 1, Max, Tp...>(network);
  }

  /**
   * Link the calculated activation with the correct recurrent layer.
   */
  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I == Max, void>::type
  LinkRecurrent(std::tuple<Tp ...>& /* unused */) { /* Nothing to do here */ }

  template<
      size_t I = 0,
      size_t Max = std::tuple_size<LayerTypes>::value - 1,
      typename... Tp
  >
  typename std::enable_if<I < Max, void>::type
  LinkRecurrent(std::tuple<Tp...>& network)
  {
    UpdateRecurrent(std::get<I>(network), std::get<I>(network).InputParameter(),
        std::get<I + 1>(network).OutputParameter());
    LinkRecurrent<I + 1, Max, Tp...>(network);
  }

  /**
   * Distinguish between recurrent layer and non-recurrent layer when updating
   * the recurrent activations.
   */
  template<typename T, typename P, typename D>
  typename std::enable_if<
      HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  UpdateRecurrent(T& layer, P& /* unused */, D& output)
  {
    layer.RecurrentParameter() = output;
  }

  template<typename T, typename P, typename D>
  typename std::enable_if<
      !HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  UpdateRecurrent(T& /* unused */, P& /* unused */, D& /* unused */)
  {
    /* Nothing to do here */
  }

  /*
   * Calculate the output error and update the overall error.
   */
  template<typename DataType, typename ErrorType, typename... Tp>
  double OutputError(const DataType& target,
                     ErrorType& error,
                     const std::tuple<Tp...>& network)
  {
    // Calculate and store the output error.
    outputLayer.CalculateError(
        std::get<sizeof...(Tp) - 1>(network).OutputParameter(), target, error);

    // Masures the network's performance with the specified performance
    // function.
    return performanceFunc.Error(network, target, error);
  }

  /**
   * Run a single iteration of the feed backward algorithm, using the given
   * error of the output layer. Note that we iterate backward through the
   * layer modules.
   */
  template<size_t I = 1, typename DataType, typename... Tp>
  void Backward(DataType& error, std::tuple<Tp ...>& network)
  {
    std::get<sizeof...(Tp) - I>(network).Backward(
        std::get<sizeof...(Tp) - I>(network).OutputParameter(), error,
        std::get<sizeof...(Tp) - I>(network).Delta());

    BackwardTail<I + 1, DataType, Tp...>(error, network);
  }

  template<size_t I = 1, typename DataType, typename... Tp>
  typename std::enable_if<I == (sizeof...(Tp)), void>::type
  BackwardTail(const DataType& /* unused */, std::tuple<Tp...>& /* unused */)
  {
    /* Nothing to do here */
  }

  template<size_t I = 1, typename DataType, typename... Tp>
  typename std::enable_if<I < (sizeof...(Tp)), void>::type
  BackwardTail(const DataType& error, std::tuple<Tp...>& network)
  {
    BackwardRecurrent(std::get<sizeof...(Tp) - I - 1>(network),
        std::get<sizeof...(Tp) - I - 1>(network).InputParameter(),
        std::get<sizeof...(Tp) - I + 1>(network).Delta());

    std::get<sizeof...(Tp) - I>(network).Backward(
        std::get<sizeof...(Tp) - I>(network).OutputParameter(),
        std::get<sizeof...(Tp) - I + 1>(network).Delta(),
        std::get<sizeof...(Tp) - I>(network).Delta());

    BackwardTail<I + 1, DataType, Tp...>(error, network);
  }

  /*
   * Update the delta of the recurrent layer.
   */
  template<typename T, typename P, typename D>
  typename std::enable_if<
      HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  BackwardRecurrent(T& layer, P& /* unused */, D& delta)
  {
    if (!layer.Delta().is_empty())
      delta += layer.Delta();
  }

  template<typename T, typename P, typename D>
  typename std::enable_if<
      !HasRecurrentParameterCheck<T, P&(T::*)()>::value, void>::type
  BackwardRecurrent(T& /* unused */, P& /* unused */, D& /* unused */)
  {
    /* Nothing to do here */
  }

  /**
   * Iterate through all layer modules and update the the gradient using the
   * layer defined optimizer.
   */
  template<size_t I = 0, size_t Max = std::tuple_size<LayerTypes>::value - 2,
      typename... Tp>
  typename std::enable_if<I == Max, void>::type
  UpdateGradients(std::tuple<Tp...>& network)
  {
    Update(std::get<I>(network), std::get<I>(network).OutputParameter(),
        std::get<I + 1>(network).Delta(), std::get<I + 1>(network),
        std::get<I + 1>(network).InputParameter(),
        std::get<I + 1>(network).Delta());
  }

  template<size_t I = 0, size_t Max = std::tuple_size<LayerTypes>::value - 2,
      typename... Tp>
  typename std::enable_if<I < Max, void>::type
  UpdateGradients(std::tuple<Tp...>& network)
  {
    Update(std::get<I>(network), std::get<I>(network).OutputParameter(),
        std::get<I + 1>(network).Delta(), std::get<I + 1>(network),
        std::get<I + 1>(network).InputParameter(),
        std::get<I + 2>(network).Delta());

    UpdateGradients<I + 1, Max, Tp...>(network);
  }

  template<typename T1, typename P1, typename D1, typename T2, typename P2,
      typename D2>
  typename std::enable_if<
      HasGradientCheck<T1, void(T1::*)(const D1&, P1&)>::value &&
      HasRecurrentParameterCheck<T2, P2&(T2::*)()>::value, void>::type
  Update(T1& layer, P1& /* unused */, D1& /* unused */, T2& /* unused */,
         P2& /* unused */, D2& delta2)
  {
    layer.Gradient(delta2, layer.Gradient());
  }

  template<typename T1, typename P1, typename D1, typename T2, typename P2,
      typename D2>
  typename std::enable_if<
      (!HasGradientCheck<T1, void(T1::*)(const D1&, P1&)>::value &&
      !HasRecurrentParameterCheck<T2, P2&(T2::*)()>::value) ||
      (!HasGradientCheck<T1, void(T1::*)(const D1&, P1&)>::value &&
      HasRecurrentParameterCheck<T2, P2&(T2::*)()>::value), void>::type
  Update(T1& /* unused */, P1& /* unused */, D1& /* unused */, T2& /* unused */,
         P2& /* unused */, D2& /* unused */)
  {
    /* Nothing to do here */
  }

  template<typename T1, typename P1, typename D1, typename T2, typename P2,
      typename D2>
  typename std::enable_if<
      HasGradientCheck<T1, void(T1::*)(const D1&, P1&)>::value &&
      !HasRecurrentParameterCheck<T2, P2&(T2::*)()>::value, void>::type
  Update(T1& layer, P1& /* unused */, D1& delta1, T2& /* unused */,
         P2& /* unused */, D2& /* unused */)
  {
    layer.Gradient(delta1, layer.Gradient());
  }

  /*
   * Calculate and store the output activation.
   */
  template<typename DataType, typename... Tp>
  void OutputPrediction(DataType& output, std::tuple<Tp...>& network)
  {
    // Calculate and store the output prediction.
    outputLayer.OutputClass(std::get<sizeof...(Tp) - 1>(
        network).OutputParameter(), output);
  }

  //! Instantiated recurrent neural network.
  LayerTypes network;

  //! The outputlayer used to evaluate the network
  OutputLayerType& outputLayer;

  //! Performance strategy used to claculate the error.
  PerformanceFunction performanceFunc;

  //! The current evaluation mode (training or testing).
  bool deterministic;

  //! Matrix of (trained) parameters.
  arma::mat parameter;

  //! The matrix of data points (predictors).
  arma::mat predictors;

  //! The matrix of responses to the input data points.
  arma::mat responses;

  //! Locally stored network input size.
  size_t inputSize;

  //! Locally stored network output size.
  size_t outputSize;

  //! The index of the current sequence number.
  size_t seqNum;

  //! Locally stored number of samples in one input sequence.
  size_t seqLen;

  //! Locally stored parameter that indicates if the input is a sequence.
  bool seqOutput;

  //! The activation storage we are using to perform the feed backward pass.
  boost::ptr_vector<arma::mat> activations;

  //! The number of separable functions (the number of predictor points).
  size_t numFunctions;

  //! Locally stored backward error.
  arma::mat error;
}; // class RNN

} // namespace ann
} // namespace mlpack

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

#endif


#pragma once

#include <boost/align/aligned_allocator.hpp>
#include <boost/mpl/at.hpp>
#include <iostream>
#include <unordered_map>
#include "../collision_tensor_galerkin_base.hpp"
#include "spectral/basis/spectral_basis_factory_ks.hpp"
#include "storage/vbcrs_sparsity.hpp"
#include "spectral/basis/toolbox/spectral_basis.hpp"

#ifdef USE_MPI
#include <mpi.h>
#endif

namespace boltzmann {
namespace ct_dense {
// ---------------------------------------------------------------------------
class CollisionTensorDenseBase : public CollisionTensorGalerkinBase
{
 protected:
  typedef double numeric_t;
  // 256bit alignment (assumes 64bit float)
  const int _align = VBCRSSparsity<>::align;
  typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> mat_t;
  typedef Eigen::VectorXd vec_t;

 public:
  typedef typename SpectralBasisFactoryKS::basis_type basis_t;

 public:
  CollisionTensorDenseBase(const basis_t& basis)
      : CollisionTensorGalerkinBase(basis)
  {
    /* empty */
  }

  void import_entries_mpishmem(const std::string& filename,
                               int vbcrs_min_blk_size=1,
                               MPI_Comm comm = MPI_COMM_WORLD);

  unsigned long nnz() const;

 protected:
  virtual void import_entries(const std::vector<std::shared_ptr<Eigen::SparseMatrix<double>> >& slices) = 0;

 protected:
  // allocated by MPI_Win_allocate_shared
  double* entries_ = NULL;
  /// inverse of mass matrix
  std::vector<numeric_t, boost::alignment::aligned_allocator<double, 32> > sinv_;
  std::vector<VBCRSSparsity<>> sparsity_patterns_;

  MPI_Comm shmcomm_;
  MPI_Win win_;
};

}  // end namespace ct_dense

}  // end namespace boltzmann


#pragma once

#include <vector>

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

#include <utils/eigen_ext.hpp>

namespace ipc::rigid {

class OptimizationProblem {
public:
    virtual ~OptimizationProblem() = default;

    /// Compute the objective function f(x)
    virtual double compute_objective(
        const Eigen::VectorXd& x,
        Eigen::VectorXd& grad,
        Eigen::SparseMatrix<double>& hess,
        bool compute_grad = true,
        bool compute_hess = true) = 0;

    // --------------------------------------------------------------------
    // Convience functions
    // --------------------------------------------------------------------

    virtual double compute_objective(const Eigen::VectorXd& x) final
    {
        Eigen::VectorXd grad;
        Eigen::SparseMatrix<double> hess;
        return compute_objective(
            x, grad, hess,
            /*compute_grad=*/false, /*compute_hess=*/false);
    }

    virtual double
    compute_objective(const Eigen::VectorXd& x, Eigen::VectorXd& grad) final
    {
        Eigen::SparseMatrix<double> hess;
        return compute_objective(
            x, grad, hess,
            /*compute_grad=*/true, /*compute_hess=*/false);
    }

    virtual void update_augmented_lagrangian(const Eigen::VectorXd& x) {}
    virtual bool
    are_equality_constraints_satisfied(const Eigen::VectorXd& x) const
    {
        return true;
    }

    /// @returns the number of variables
    virtual int num_vars() const = 0;

    /// @returns A vector of booleans indicating if a DoF is fixed.
    virtual const VectorXb& is_dof_fixed() const = 0;

    virtual Eigen::VectorXi free_dof() const
    {
        const VectorXb& is_fixed = is_dof_fixed();
        Eigen::VectorXi free_dof(is_fixed.size() - is_fixed.count());
        for (int i = 0, j = 0; i < is_fixed.size(); i++) {
            if (!is_fixed(i)) {
                free_dof(j++) = i;
            }
        }
        return free_dof;
    }

    /// Determine if there is a collision between two configurations
    virtual bool
    has_collisions(const Eigen::VectorXd& xi, const Eigen::VectorXd& xj) = 0;

    /// Compute the earliest time of impact between two configurations
    virtual double compute_earliest_toi(
        const Eigen::VectorXd& xi, const Eigen::VectorXd& xj) = 0;

    virtual bool is_ccd_aligned_with_newton_update() = 0;

    /// Compute the minimum distance among geometry
    virtual double compute_min_distance(const Eigen::VectorXd& x) const = 0;

    /// Get the world coordinates of the vertices
    virtual Eigen::MatrixXd world_vertices(const Eigen::VectorXd& x) const = 0;

    /// Get the length of the diagonal of the worlds bounding box
    virtual double world_bbox_diagonal() const = 0;

    /// Get the mass matrix
    virtual DiagonalMatrixXd mass_matrix() const = 0;

    /// Get the average mass
    virtual double average_mass() const = 0;

    /// Get the time-step
    virtual double timestep() const = 0;

    virtual bool is_barrier_problem() const { return false; }
    virtual bool is_constrained_problem() const { return false; }
};

/// Helper Functions for checking finite differences
/// ------------------------------------------------
Eigen::VectorXd eval_grad_objective_approx(
    OptimizationProblem& problem, const Eigen::VectorXd& x);
Eigen::MatrixXd eval_hess_objective_approx(
    OptimizationProblem& problem, const Eigen::VectorXd& x);

} // namespace ipc::rigid


#pragma once

#include <ostream>
#include <vector>
#include <utility>

#include <boost/operators.hpp>

namespace Detail {

template<typename Base>
class [[nodiscard]] Magnitude : public boost::addable<Magnitude<Base>>
  , public boost::subtractable<Magnitude<Base>>
  , public boost::multipliable<Magnitude<Base>>
{
public:
  using Me = Magnitude<Base>;

  Magnitude() { assert(isNormalized()); }// Initialize as 0
  explicit Magnitude(unsigned long v) : value(fromLong(v)) { assert(isNormalized()); }

  Magnitude(std::initializer_list<Base> i) : value(i)
  {
    normalize();
    assert(isNormalized());
  }

  std::ostream &dumpTo(std::ostream &os) const
  {
    auto c = value.begin();
    if (c != value.end()) {
      os << static_cast<unsigned long>(*c);
      c++;
    } else {
      os << 0;
    }
    while (c != value.end()) {
      os << ", " << static_cast<unsigned long>(*c);
      c++;
    }

    return os;
  }

  [[nodiscard]] long operator<=>(const Me &other) const
  {
    return compare(*this, other);
  }

  [[nodiscard]] bool operator==(const Me &other) const
  {
    assert(isNormalized());
    assert(other.isNormalized());

    return value == other.value;
  }

  [[nodiscard]] bool operator!=(const Me &other) const
  {
    return !(other == *this);//NOLINT:(Simplify)
  }

  Me &operator++()
  {
    unsigned long long carry = 1;
    for (auto &v : value) {
      const unsigned long long r = carry + v;
      v = static_cast<Base>(r & mask);
      carry = r >> size;
    }

    // We possibly have a carry
    if (carry != 0U) {
      assert(carry <= mask);
      value.push_back(static_cast<Base>(carry));
    }

    return *this;
  }

  Me &operator--()
  {
    assert(isNormalized() && !value.empty());
    long long carry = -1;
    for (auto &v : value) {
      long long r = carry + v;
      v = static_cast<Base>(r & mask);
      carry = r >> size;
    }

    normalize();

    return *this;
  }

  [[nodiscard]] Me operator++(int) &// NOLINT: cert-dcl21-cpp
  {
    Me result(*this);
    ++*this;
    return result;
  }

  [[nodiscard]] Me operator--(int) &// NOLINT: cert-dcl21-cpp
  {
    Me result(*this);
    --*this;
    return result;
  }

  Me &operator+=(const Me &other)
  {
    value.reserve(std::max(value.size(), 1 + other.value.size()));

    unsigned long long carry = 0;
    auto ileft = value.begin();
    auto iright = other.value.begin();
    for (; ileft != value.end() && iright != other.value.end(); ++ileft, ++iright) {
      const unsigned long long r = carry + *ileft + *iright;
      *ileft = static_cast<Base>(r & mask);
      carry = r >> size;
    }

    // Either ileft or iright is at its end
    for (; ileft != value.end() && carry != 0; ++ileft) {
      const unsigned long long r = carry + *ileft;
      *ileft = static_cast<Base>(r & mask);
      carry = r >> size;
    }
    for (; iright != other.value.end(); ++iright) {
      const unsigned long long r = carry + *iright;
      value.push_back(static_cast<Base>(r & mask));
      carry = r >> size;
    }

    // We possibly have a carry
    if (carry != 0U) {
      assert(carry <= mask);
      value.push_back(static_cast<Base>(carry));
    }

    return *this;
  }

  Me &operator-=(const Me &other)
  {
    assert(other <= *this);

    long long carry = 0;
    auto ileft = value.begin();
    auto iright = other.value.begin();
    for (; iright != other.value.end(); ++ileft, ++iright) {
      assert(ileft != value.end());

      const long long r = *ileft - *iright + carry;
      *ileft = static_cast<Base>(r & mask);
      carry = r >> size;
    }

    for (; ileft != value.end() && carry != 0; ++ileft) {
      const long long r = *ileft + carry;
      *ileft = static_cast<Base>(r & mask);
      carry = r >> size;
    }

    // right <= left, so no carry at this point.
    assert(carry == 0);

    normalize();

    return *this;
  }

  Me &operator*=(const Me &right)
  {
    const Me left(std::move(*this));
    value.clear();
    value.reserve(left.value.size() + right.value.size() + 1);

    for (unsigned i = 0; i < right.value.size(); i++) {
      *this += mul(left, right.value[i], i);
    }

    return *this;
  }

private:
  std::vector<Base> value;
  static constexpr const Base mask = std::numeric_limits<Base>::max();
  static constexpr const Base size = std::numeric_limits<Base>::digits;

  explicit Magnitude(std::vector<Base> &&v) : value(std::move(v)) { assert(isNormalized()); }

  static long compare(const Me &left, const Me &right)
  {
    if (left.value.size() != right.value.size()) {
      return static_cast<long>(left.value.size()) - static_cast<long>(right.value.size());
    }

    auto ileft = left.value.rbegin();
    auto iright = right.value.rbegin();
    while (ileft != left.value.rend() && *ileft == *iright) {
      ++ileft;
      ++iright;
    }

    if (ileft == left.value.rend()) {
      return 0;
    }

    return *ileft - *iright;
  }

  Me &normalize()
  {
    while (!value.empty() && *value.rbegin() == 0) {
      value.pop_back();
    }

    return *this;
  }

  [[nodiscard]] bool isNormalized() const
  {
    return value.empty() || *value.rbegin() != 0;
  }

  [[nodiscard]] static std::vector<Base> fromLong(unsigned long value)
  {
    std::vector<Base> result;
    while (value != 0) {
      result.push_back(toBase(value & mask));
      value >>= size;
    }

    return result;
  }

  template<typename T>
  [[nodiscard]] static Base toBase(const T v)
  {
    return static_cast<Base>(v);
  }

  static Me mul(const Me &left, const Base right, const unsigned offset)
  {
    Me result;

    if (right != 0) {
      result.value.reserve(offset + left.value.size() + 1);
      fill_n(back_inserter(result.value), offset, 0);

      unsigned long long carry = 0;
      for (auto v : left.value) {
        const unsigned long long r = carry + v * right;
        result.value.push_back(r & mask);
        carry = r >> size;
      }
      while (carry != 0U) {
        result.value.push_back(carry & mask);
        carry >>= size;
      }
    }

    return result;
  }
};

}// namespace Detail


/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2009: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#define BOOST_TEST_MODULE icl::interval_set_mixed unit test
#include <libs/icl/test/disable_test_warnings.hpp>
#include <string>
#include <boost/mpl/list.hpp>
#include "../unit_test_unwarned.hpp"

// interval instance types
#include "../test_type_lists.hpp"
#include "../test_value_maker.hpp"

#include <boost/icl/interval_set.hpp>
#include <boost/icl/separate_interval_set.hpp>
#include <boost/icl/split_interval_set.hpp>

using namespace std;
using namespace boost;
using namespace unit_test;
using namespace boost::icl;

#include "../test_interval_set_mixed.hpp"

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_ctor_4_ordered_types, T, ordered_types)
{         interval_set_mixed_ctor_4_ordered_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_equal_4_ordered_types, T, ordered_types)
{         interval_set_mixed_equal_4_ordered_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_assign_4_ordered_types, T, ordered_types)
{         interval_set_mixed_assign_4_ordered_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_ctor_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_ctor_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_assign_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_assign_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_equal_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_equal_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_contains_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_contains_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_add_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_add_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_subtract_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_subtract_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_erase_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_erase_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_basic_intersect_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_basic_intersect_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_disjoint_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_disjoint_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_infix_plus_overload_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_infix_plus_overload_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_infix_pipe_overload_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_infix_pipe_overload_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_infix_minus_overload_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_infix_minus_overload_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_infix_et_overload_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_infix_et_overload_4_bicremental_types<T>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_set_mixed_infix_caret_overload_4_bicremental_types, T, bicremental_types)
{         interval_set_mixed_infix_caret_overload_4_bicremental_types<T>(); }


/*****************************************************************************
*
* Rokko: Integrated Interface for libraries of eigenvalue decomposition
*
* Copyright (C) 2014 by Synge Todo <wistaria@comp-phys.org>
*
* 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)
*
*****************************************************************************/

#include "crs_matrix.hpp"
#include <boost/tuple/tuple.hpp>

crs_matrix::crs_matrix(hamiltonian const& hop) {
  int ibond = hop.num_bonds();
  int ic = ibond + 1;

  // initialization
  if (elemnt_.size1() != hop.dimension() || elemnt_.size2() < ic)
    elemnt_.resize(hop.dimension(), ic);
  if (loc_.size1() != hop.dimension() || loc_.size2() < ic) loc_.resize(hop.dimension(), ic);
  for (int i = 0; i < ic; ++i)
    for (int j = 0; j < hop.dimension(); ++j)
      elemnt_(j, i) = 0;

  // diagonal elements
  for (int k = 0; k < ibond; ++k) {
    int isite1, isite2;
    boost::tie(isite1, isite2) = hop.site_pair(k);
    int is = (1 << isite1) + (1 << isite2);
    double wght = -hop.bond_weight(k) * hop.z_ratio(k) * 0.5;
    for (int i = 0; i < hop.dimension(); ++i) {
      int ibit = hop.config(i) & is;
      elemnt_(i, ic - 1) += (ibit == 0 || ibit == is) ? wght : -wght;
      loc_(i, ic - 1) = i;
    }
  }

  // off-diagonal elements
  for (int k = 0; k < ibond; ++k) {
    int isite1, isite2;
    boost::tie(isite1, isite2) = hop.site_pair(k);
    int is = (1 << isite1) + (1 << isite2);
    double wght = -hop.bond_weight(k);
    for (int i = 0; i < hop.dimension(); ++i) {
      int ibit = hop.config(i) & is;
      if (ibit == 0 || ibit == is) {
        elemnt_(i, k) = 0;
        loc_(i, k) = i;
      } else {
        elemnt_(i, k) = wght;
        loc_(i, k) = hop.config2index(hop.config(i) ^ is);
      }
    }
  }
}

double crs_matrix::multiply(const double *v1, double *v0) const {
  double prdct = 0;
  for (int k = 0; k < elemnt_.size2(); ++k) {
    for (int j = 0; j < dimension(); ++j) {
      double temp = elemnt_(j, k) * v1[loc_(j, k)];
      v0[j] += temp;
      prdct += v1[j] * temp;
    }
  }
  return prdct;
}


#include <stdlib.h>  /* srand, rand */
#include <iostream>
#include <fstream> // creating (.csv) files
#include <vector>
#include <cstring>
#include <string>
#include <functional>
#include <armadillo> // http://arma.sourceforge.net/docs.html
#include <thread>   /* std::this_thread::sleep_for */

#include "EvolutionaryAlgorithm.hpp"

// NOT USED??
// enum events{
//     BASE_RATE = 0,
//     MUTA_RATE,
//     PRED_RATE,
//     PART_RATE,
//     GENO_RATE,
//     EV_A_RATE,
// };

//arma::rowvec semiFinalsAndFinals[SEMI_FINALS][TOURNMENT_RATE];
//arma::rowvec indvFinals[TOURNMENT_RATE];
void EvolutionaryAlgorithm::saveBestIndvParamsCSV(){
    std::fstream myFile;
    myFile.open("constValues.cfg", std::ios_base::app);

    myFile << parametersList[0] << " = " << population(bestFitIndex,0);
    for (size_t i = 1; i < NB_PARAMETERS; i++) myFile << std::endl << parametersList[i] << " = " << population(bestFitIndex,i);
    //printf("k%sk\n", parametersList[i]);
    myFile.close();
}

// 1st step: initialize population
void EvolutionaryAlgorithm::initializePop(int tournmentType) {
    double minVal,maxVal;
    std::cout << "INITIALIZING POPULATION\n";
    // for (int i = 0; i < POPULATION_SIZE; i++) {
    //     for (int j = 0; j < NB_PARAMETERS; j++) {
    //         population[i][j] = (double) (rand() % MAX_PARAM_VALUE); // number range = [0, MAX_PARAM_VALUE[
    //     }
    // }
    population.randu(); // initialize with values between 0 and 1
    if(tournmentType == INITIALS) population *= MAX_PARAM_VALUE; // if a normal EA is taking place, just multiply the population by a givern value
    else{
        int middle = TOURNMENT_RATE + (POPULATION_SIZE - TOURNMENT_RATE)/2;
        fillInitialsWithBests(tournmentType);
        for(int j = 0;j< NB_PARAMETERS;j++){ // Min and max values are defined for each col, and converting the current col values to a number between this constraints.
            // this starts after the first TOURNMENT_RATE individuls, and the remaining is divided into two pars, one with biased rand values, and other without
            minVal = MIN_MULT * bestTournmentIndv[tournmentType].col(j).min();
            maxVal = MAX_MULT * bestTournmentIndv[tournmentType].col(j).max();

            for(int i = TOURNMENT_RATE;i<middle;i++){
                population(i,j) = minVal + (maxVal - minVal)*(population(i,j));
            }
        }
        for(int i = middle;i < POPULATION_SIZE;i++)
            population.row(i) *= MAX_PARAM_VALUE;

    }

    population.print("Population matrix initialized:");

    return;
}

// Function to normalize fitness (values between 0 and 1)
void EvolutionaryAlgorithm::normalizeFitness(double * normalizedFitness) {
    if (normalizedFitness == NULL)
        return;

    for (int i = 0; i < POPULATION_SIZE; i++)
        normalizedFitness[i] = (fitness[i]-bestFitness)/(worstFitness-bestFitness);

    return;
}

// 2nd step: evaluate population (calculate fitness)
void EvolutionaryAlgorithm::evaluatePop() {
    std::cout << "EVALUATING POPULATION\n";

    nbNoImprovementGens++; // we begin considering there was no improvement in the generation
    pthread_mutex_lock(&mutex);
    remainingFitnessToCalc = POPULATION_SIZE;
    pthread_mutex_unlock(&mutex);

    for (int i = 0; i<POPULATION_SIZE; i++){
        m_eaGameControler.startGame(i,arma::conv_to<std::vector<double>>::from(population.row(i)));
    }
    for (int i = 0; i< POPULATION_SIZE; i++){
        pthread_mutex_lock(&mutex2);
    }

    for (int i = 0; i < POPULATION_SIZE; i++) {
        // example of fitness calculation -> CHANGE!!!!
        // fitness[i] = abs(terminoReal - terminoEsperado)/terminoEsperado

        if (fitness[i] < bestFitness){ // searching for the  max fitnnes from new generation
            bestFitness = fitness[i];
            bestFitIndex = i;
            nbNoImprovementGens = 0; // this generation shows improvement -> reset counter
        }
        else if (fitness[i] > worstFitness){
            worstFitness = fitness[i];
            worstFitIndex = i;
        }

        // printf("fitness[%d] %lf\n", i, fitness[i]);
    }

    printf("BEST FITNESS: %lf - INDEX: %d\n", bestFitness, bestFitIndex);
    printf("WORST FITNESS: %lf - INDEX: %d\n", worstFitness, worstFitIndex);

    return;
}

void EvolutionaryAlgorithm::crossover(int indv, arma::rowvec parent1, arma::rowvec parent2) {
    // for(int j = 0; j < NB_PARAMETERS; j++){
    //     population[i][j] = (parent1[j] + parent2[j])/2.0;
    // }
    population.row(indv) = (parent1 + parent2)/2.0;

    return;
}

void EvolutionaryAlgorithm::elitism() {
    std::cout << "ELITISM\n";
    arma::rowvec bestIndv = population.row(bestFitIndex);

    for (int i = 0; i < POPULATION_SIZE; i++) {
        // crossover
        crossover(i, population.row(i), bestIndv);
    }

    return;
}

void EvolutionaryAlgorithm::tournament() {
    std::cout << "TOURNAMENT\n";
    arma::mat::fixed<POPULATION_SIZE, NB_PARAMETERS> oldPopulation;
    int parentIndex[2];

    // copying last population (new one will be different)
    // for (int i = 0; i < POPULATION_SIZE; i++) {
    //     for (int j = 0; j < NB_PARAMETERS; j++) {
    //         oldPopulation[i][j] = population[i][j];
    //     }
    // }
    oldPopulation = population;

    for (int i = 0; i < POPULATION_SIZE; i++) {
        if (i == bestFitIndex)
            continue;

        // chossing parents for new individual
        for (int j = 0; j < 2; j++) {
            int indexIndA = rand() % POPULATION_SIZE; // indv 1 that will "fight" to be parent
            int indexIndB = rand() % POPULATION_SIZE; // indv 2 that will "fight" to be parent

            parentIndex[j] = (fitness[indexIndA] < fitness[indexIndB] ? indexIndA : indexIndB);
        }

        // crossover
        crossover(i, oldPopulation.row(parentIndex[0]), oldPopulation.row(parentIndex[1]));
    }

    return;
}

//TODO: swap priorities. Smaller fitness must be morre relevant
void EvolutionaryAlgorithm::roulette() {
    std::cout << "ROULETTE\n";
    arma::mat::fixed<POPULATION_SIZE, NB_PARAMETERS> oldPopulation;
    double standardizedFitness[POPULATION_SIZE];
    int parentIndex[2], rNb;
    double probSum = 0.0, partialSum = 0.0;

    // copying last population (new one will be different)
    // for (int i = 0; i < POPULATION_SIZE; i++) {
    //     for (int j = 0; j < NB_PARAMETERS; j++) {
    //         oldPopulation[i][j] = population[i][j];
    //     }
    // }
    oldPopulation = population;

    // Standardize fitness (set probabilites that add up to 100%)
    for (int i = 0; i < POPULATION_SIZE; i++)
        probSum += fitness[i];
    for (int i = 0; i < POPULATION_SIZE; i++)
        standardizedFitness[i] = fitness[i]/probSum;

    // Chosing new parents for each individual
    for (int i = 0; i < POPULATION_SIZE; i++) {
        if (i == bestFitIndex) // preserves best individual
            continue;

        for (int k = 0; k < 2; k++) { // chosing 2 parents
            rNb = ((double) rand() / RAND_MAX); // rand between 0 and 1
            partialSum = 0.0;

            for (int j = 0; j < POPULATION_SIZE; j++) { // randomly chosing and individual according to its fitness (+fitness = +probabity)
                partialSum += fitness[j];
                if (partialSum >= rNb) {
                    parentIndex[k] = j; // new parent at index j
                    break;
                }
            }
        }

        // crossover
        crossover(i, oldPopulation.row(parentIndex[0]), oldPopulation.row(parentIndex[1]));
    }
}

void EvolutionaryAlgorithm::mutate(int indIndex) {
    int plusMinusSign = (rand() % 2 ? MUTATE_POSITIVE_MULT : MUTATE_NEGATIVE_MULT); // mutation will increase the param value or decrease it
    int mutateParamIndex = rand() % NB_PARAMETERS; // index of parameter that will be mutated

    population(indIndex, mutateParamIndex) += population(indIndex, mutateParamIndex) * mutationRate * plusMinusSign;

    return;
}

// 3rd step: selection + mutation and crossover
void EvolutionaryAlgorithm::selectionAndMutation() { // tournament, elitism, roulette...
    std::cout << "SELECTION\n";

    // Selection method (+ crossover)
    (this->*(selectionType[selectionMethod]))(); // void (*selectionType[])() => tournament(), elitism() or roulette()

    // Mutation

    // Mutating all params from individuals
    // arma::rowvec bestIndv = population.row(maxFitIndex);

    // arma::mat mutateMatrix(POPULATION_SIZE, NB_PARAMETERS, arma::fill::randu);
    // mutateMatrix = ((mutateMatrix * MAX_PARAM_VALUE) - MAX_PARAM_VALUE/2.0) * mutationRate;

    // population = population + mutateMatrix;

    // population.row(maxFitIndex) = bestIndv;
    // population.transform( [](double x) { return ((x < 0 || x > MAX_PARAM_VALUE) ? abs(MAX_PARAM_VALUE-abs(x)) : x); } );

    // Mutating only one parameter from each individual
    for(int i = 0; i < bestFitIndex; i++) mutate(i);
    // (don't mutate best one)
    for(int i = bestFitIndex+1; i < POPULATION_SIZE; i++) mutate(i);

    return;
}

// Initialize .csv file to sabe data from the EA
// Creates the column's headers
// generation, paramsIndv1, fitnessIndv1 ..., paramsIndvN, fitnessIndvN, bestParams, bestFitness
void EvolutionaryAlgorithm::createCSV(std::string pathPos) {
    std::ofstream csvFileWriter;

    csvFileWriter.open("historyEA-" + pathPos + ".csv");
    if (!csvFileWriter.good()) {
        std::cout << "[!] Error occurred while trying to create historyEA" << pathPos << ".csv!\n";
        return;
    }

    csvFileWriter << "generation,";
    for (int i = 0; i < POPULATION_SIZE; i++)
        csvFileWriter << "paramsIndv" << i << ",fitnessIndv" << i << ",";
    csvFileWriter << "paramsBestIndv,fitnessBestIndv\n";

    return;
}

std::string EvolutionaryAlgorithm::formatParamsString(int indvIndex) {
    std::string paramsFormated = "[ ";
    for (int i = 0; i < NB_PARAMETERS; i++) {
        paramsFormated += std::to_string(population(indvIndex, i));
        paramsFormated += " ";
    }
    paramsFormated += "]";

    return paramsFormated;
}

// Saves information about a generation in a .csv file
// Information: generation, paramsIndv1, fitnessIndv1 ..., paramsIndvN, fitnessIndvN, bestParams, bestFitness
void EvolutionaryAlgorithm::saveGenerationData(int generation) {
    std::ofstream csvFileWriter;

    csvFileWriter.open("historyEA.csv", std::ios_base::app); // append instead of overwrite
    if (!csvFileWriter.good()) {
        std::cout << "[!] Error occurred while trying to open historyEA.csv!\n";
        return;
    }

    csvFileWriter << generation << ",";
    for (int i = 0; i < POPULATION_SIZE; i++) {
        csvFileWriter << formatParamsString(i) << "," << fitness[i] << ",";
    }
    csvFileWriter << formatParamsString(bestFitIndex) << "," << bestFitness << "\n";
}

void EvolutionaryAlgorithm::increaseMutation() {
    if(!mutationRate) {
        mutationRate = INITIAL_MUTATION;
    }
    else {
        mutationRate *= MUTATION_INCREASE_RATIO;

        if (mutationRate > MAX_MUTATION_RATE) // mutation reached its max value
            mutationRate = MAX_MUTATION_RATE;
    }

    return;
}

// Function that will kill the worst individual each "APPLY_PREDATION_INTERVAL" number of generations
void EvolutionaryAlgorithm::predationOfOne() {
    arma::rowvec newInd(NB_PARAMETERS, arma::fill::randu); // creating totally new individual
    newInd = newInd * MAX_PARAM_VALUE;

    population.row(worstFitIndex) = newInd;

    return;
}

// A sequence of selections will run, this method will indicate the selection method that will be used by the next batch of generations
void EvolutionaryAlgorithm::partIncrease(){
    selectionMethod = partsSelectionMethods[++partPos];

    return;
}

// Function that will kill all individuals but the best to reset the population and generate new individuals (without biases)
void EvolutionaryAlgorithm::oneRemainingPopReset() {
    std::cout << "APPLYING POPULATION RESET\n";

    arma::rowvec best = population.row(bestFitIndex);

    initializePop(INITIALS);
    population.row(0) = best;
    partIncrease();

    return;
}

// Ends the batch of generations from current EA
void EvolutionaryAlgorithm::endEABatch() {
    std::cout << "END EA BATCH";

    continueBatch = false;

    return;
}

// Generic function that checks if certain event should happen (predation, population reset, mutation increase...)
bool EvolutionaryAlgorithm::eventHappens(int eventType) {
    if(nbNoImprovementGens % eventTriggerModule[eventType] == 0)
        return true; // the event being verified should happen!
    return false;
}

void EvolutionaryAlgorithm::checkEvents() {
    if(nbNoImprovementGens == 0)
        return;

    for(int i = 5; i > 0; i++){
        if(eventHappens(i)) {
            (this->*(eventTypes[i-1]))(); // calls one of these functions: increaseMutation, predationOfOne, partIncrease, oneRemainingPopReset, fullPopReset
            return;
        }
    }
}

void EvolutionaryAlgorithm::startEventTriggerList(){
    for(int i = 1; i < 6; i++)
        eventTriggerModule[i] *= eventTriggerModule[i-1];
}

void EvolutionaryAlgorithm::evoAlg(std::string csvStr){
    nbNoImprovementGens = 0;
    createCSV(csvStr);
    continueBatch = true;
    partPos = 0; // defines selection method of current batch of generations

    generationIndex = 0;
    while (continueBatch) {
        std::cout << "\n==== Generation " << generationIndex << " ====\n";
        // population.print("Current population:");
        evaluatePop();
        selectionAndMutation();

        saveGenerationData(generationIndex);

        checkEvents(); // checks if mutation should increase, predation or population reset should occur etc.
        // if fullPopReset() is called, continueEA = false

        generationIndex++;
        scanf("%*c");
    }

    return;
}

void EvolutionaryAlgorithm::fillInitialsWithBests(int tournmentType){
    for(int i = 0; i < TOURNMENT_RATE; i++)
        population.row(i) = bestTournmentIndv[tournmentType].row(i);
}

void EvolutionaryAlgorithm::semiFinalsTournment(int semiFinalPos){
    for(int i = 0; i < TOURNMENT_RATE; i++){
        initializePop(INITIALS);
        evoAlg("SF-" + std::to_string(semiFinalPos) + "_EA-" + std::to_string(i));
        bestTournmentIndv[SEMI_FINALS].row(i) = population.row(bestFitIndex); // saves the best individual from current EA
    }
    initializePop(SEMI_FINALS);
    evoAlg("SF-" + std::to_string(semiFinalPos)); // this EA will use the best individuals from each previous EA
    bestTournmentIndv[FINALS].row(semiFinalPos) = population.row(bestFitIndex); // saves the best individual from current EA
    return;
}

void EvolutionaryAlgorithm::finalTournment(){
    for(int i = 0; i < TOURNMENT_RATE; i++)
        semiFinalsTournment(i);

    initializePop(FINALS);
    evoAlg("Main"); // this EA will use the best individuals from each semifinal

    bestIndividual = population.row(bestFitIndex);
    std::cout << "EVOLUTIONARY ALGORITHM FINISHED!" << std::endl;
    //eaFinished = true;
    return;
}

EvolutionaryAlgorithm::EvolutionaryAlgorithm():eaFinished(false){
    srand(time(NULL));
    startEventTriggerList();
    system("clear");
    pthread_create(&scriptThread, NULL, runScript, this);

}

EvolutionaryAlgorithm::~EvolutionaryAlgorithm(){
    pthread_join(scriptThread, NULL);
}

void *runScript(void *scriptFuncObject) {
    EvolutionaryAlgorithm *script = (EvolutionaryAlgorithm*) scriptFuncObject;
    script->finalTournment();
    return NULL;
}

void EvolutionaryAlgorithm::setFitness(int pos,std::vector<std::pair<int,int>> fitnessResults){
    fitness[pos] = 0;
    for(auto fitnessPair : fitnessResults){
        fitness[pos] += std::abs(fitnessPair.first - fitnessPair.second)/fitnessPair.first;
    }
    remainingFitnessToCalc -= 1;
    return;
}


//=======================================================================
// Copyright (c)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file steiner_tree_oracle.hpp
 * @brief
 * @author Maciej Andrejczuk, Piotr Godlewski
 * @version 1.0
 * @date 2013-08-01
 */
#ifndef PAAL_STEINER_TREE_ORACLE_HPP
#define PAAL_STEINER_TREE_ORACLE_HPP

#include "paal/iterative_rounding/min_cut.hpp"
#include "paal/utils/irange.hpp"

#include <boost/optional.hpp>

#include <unordered_map>
#include <vector>

namespace paal {
namespace ir {

/**
 * @class steiner_tree_violation_checker
 * @brief Violations checker for the separation oracle
 *      in the steiner tree problem.
 */
class steiner_tree_violation_checker {
    using AuxEdge = min_cut_finder::Edge;
    using AuxVertex = min_cut_finder::Vertex;
    using AuxEdgeList = std::vector<AuxEdge>;
    using Violation = boost::optional<double>;

  public:
    using Candidate = AuxVertex;
    steiner_tree_violation_checker() : m_current_graph_size(-1) {}

    /**
     * Returns an iterator range of violated constraint candidates.
     */
    template <typename Problem, typename LP>
    auto get_violation_candidates(const Problem &problem, const LP &lp)
        ->decltype(irange(problem.get_terminals().size())) {

        int graph_size = problem.get_terminals().size();
        if (graph_size != m_current_graph_size) {
            // Graph has changed, construct new oracle
            m_current_graph_size = graph_size;
            m_root = select_root(problem.get_terminals());
            create_auxiliary_digraph(problem, lp);
        } else {
            update_auxiliary_digraph(problem, lp);
        }
        //TODO - rethink - why do we return the whole collection instead of returning m_root see check_violation implementation
        return irange(problem.get_terminals().size());
    }

    /**
     * Checks if the given constraint candidate is violated an if it is,
     * returns the violation value and violated constraint ID.
     */
    template <typename Problem>
    Violation check_violation(Candidate candidate, const Problem &problem) {
        if (candidate == m_root) {
            return Violation{};
        }

        double violation = find_violation(candidate);
        if (problem.get_compare().g(violation, 0)) {
            return violation;
        } else {
            return Violation{};
        }
    }

    /**
     * Adds the violated constraint to LP.
     * It contains all the components reachable from a given source,
     * but its sink vertex is not reachable.
     */
    template <typename Problem, typename LP>
    void add_violated_constraint(Candidate violating_terminal,
                                 const Problem &problem, LP &lp) {
        if (std::make_pair(violating_terminal, m_root) !=
            m_min_cut.get_last_cut()) {
            find_violation(violating_terminal);
        }

        auto const &components = problem.get_components();
        lp::linear_expression expr;
        for (int i = 0; i < components.size(); ++i) {
            auto u = m_artif_vertices[i];
            int ver = components.find_version(i);
            auto v = m_terminals_to_aux[problem.get_terminal_idx(components.find(i).get_sink(ver))];
            if (m_min_cut.is_in_source_set(u) &&
                !m_min_cut.is_in_source_set(v)) {
                expr += problem.find_column_lp(i);
            }
        }
        lp.add_row(std::move(expr) >= 1);
    }

  private:

    /**
     * @brief Creates the auxiliary directed graph used for feasibility testing
     *
     * Graph contains a vertex for each component and each terminal
     * Sources of every component have out edges with infinite weight
     * Target has in edge with weight x_i from LP
     */
    template <typename Problem, typename LP>
    void create_auxiliary_digraph(Problem &problem, const LP &lp) {
        m_min_cut.init(0);
        m_artif_vertices.clear();
        m_terminals_to_aux.clear();
        for (auto term : irange(problem.get_terminals().size())) {
            m_terminals_to_aux[term] = m_min_cut.add_vertex_to_graph();
        }
        auto const &components = problem.get_components();

        for (int i = 0; i < components.size(); ++i) {
            AuxVertex new_v = m_min_cut.add_vertex_to_graph();
            m_artif_vertices[i] = new_v;
            int ver = components.find_version(i);
            auto sink = components.find(i).get_sink(ver);
            for (auto w : boost::make_iterator_range(components.find(i)
                                                         .get_terminals())) {
                if (w != sink) {
                    double INF = std::numeric_limits<double>::max();
                    m_min_cut.add_edge_to_graph(m_terminals_to_aux[problem.get_terminal_idx(w)], new_v,
                                                INF);
                } else {
                    lp::col_id x = problem.find_column_lp(i);
                    double col_val = lp.get_col_value(x);
                    m_min_cut.add_edge_to_graph(new_v,
                            m_terminals_to_aux[problem.get_terminal_idx(sink)],
                            col_val);
                }
            }
        }
    }

    /**
     * Updates the auxiliary directed graph. Should be performed after each LP
     * iteration.
     */
    template <typename Problem, typename LP>
    void update_auxiliary_digraph(Problem &problem, const LP &lp) {
        auto const &components = problem.get_components();
        for (int i = 0; i < components.size(); ++i) {
            auto component_v = m_artif_vertices[i];
            int ver = components.find_version(i);
            auto sink = components.find(i).get_sink(ver);
            double col_val = lp.get_col_value(problem.find_column_lp(i));
            m_min_cut.set_capacity(component_v,
                m_terminals_to_aux[problem.get_terminal_idx(sink)], col_val);
        }
    }

    /**
     * Select the root terminal. Max-flow will be directed to that vertex during
     * LP oracle execution.
     */
    template <typename Terminals>
    AuxVertex select_root(const Terminals &terminals) {
        // TODO: Maybe it's better to select random vertex rather than first
        return 0;
    }

    /**
     * Finds the most violated cut containing \c src and returns its violation
     * value.
     */
    double find_violation(AuxVertex src) {
        double min_cut_weight = m_min_cut.find_min_cut(
            m_terminals_to_aux[src], m_terminals_to_aux[m_root]);
        return 1 - min_cut_weight;
    }

    AuxVertex m_root;         // root vertex, sink of all max-flows
    int m_current_graph_size; // size of current graph

    // maps component_id to aux_graph vertex
    std::unordered_map<int, AuxVertex> m_artif_vertices;

    // maps terminals to aux_graph vertices
    std::unordered_map<AuxVertex, AuxVertex> m_terminals_to_aux;

    min_cut_finder m_min_cut;
};

} //! ir
} //! paal
#endif // PAAL_STEINER_TREE_ORACLE_HPP


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   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
//==============================================================================
#define NT2_UNIT_MODULE "nt2 boost.simd.swar toolbox - shuffle/simd Mode"

#include <boost/simd/swar/include/functions/shuffle.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>

struct identity_
{
  template<class Index, class Cardinal> struct apply : Index {};
};

struct reverse_
{
  template<class Index, class Cardinal>
  struct apply : boost::mpl::int_<Cardinal::value - Index::value - 1> {};
};

struct mirror_
{
  template<class Index, class Cardinal>
  struct apply
       : boost::mpl::int_ < (Index::value >= Cardinal::value/2)
                          ? (Cardinal::value - 1 - Index::value)
                          : Index::value
                          >
  {};
};

template<int N>
struct bcast_
{
  template<class Index, class Cardinal>
  struct apply
  {
    BOOST_MPL_ASSERT_MSG
    ( (N < Cardinal::value)
    , OUT_OF_BOUND_SIMD_BROADCAST
    , (Cardinal)
    );

    typedef boost::mpl::int_< N > type;
  };
};

struct null_
{
  template<class Index, class Cardinal> struct apply : boost::mpl::int_<-1> {};
};

struct half_null_
{
  template<class Index, class Cardinal>
  struct apply : boost::mpl::int_< Index::value % 2 ? Index::value/2 : -1> {};
};

struct random_
{
  template<class Index, class Cardinal>
  struct apply  : boost::mpl
                ::int_< (Index::value * 3 + 1) % Cardinal::value > {};
};

NT2_TEST_CASE_TPL ( shuffle, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::shuffle;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>               vT;

  vT origin, reference;
  vT id, shuffled, hnull, mirrored, bcasted,randed;
  for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
  origin[0] = boost::simd::Valmax<T>();

  id = shuffle<identity_>(origin);
  NT2_TEST_EQUAL(id,origin);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[vT::static_size - i -1];
  shuffled = shuffle<reverse_>(origin);
  NT2_TEST_EQUAL(shuffled,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = i%2 ? origin[i/2] : 0;
  hnull = shuffle<half_null_>(origin);
  NT2_TEST_EQUAL(hnull,reference);

  NT2_TEST_EQUAL( shuffle<null_>(origin), boost::simd::Zero<vT>());

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[i<vT::static_size/2 ? i : vT::static_size - i -1];
  mirrored = shuffle<mirror_>(origin);
  NT2_TEST_EQUAL(mirrored,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[1];
  bcasted = shuffle< bcast_<1> >(origin);
  NT2_TEST_EQUAL(bcasted,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[(i*3+1) % vT::static_size];
  randed = shuffle< random_ >(origin);
  NT2_TEST_EQUAL(randed,reference);
}

struct low0_upshuffled_
{
  template<class Index, class Cardinal>
  struct apply  : boost::mpl
                ::int_< (Index::value >= Cardinal::value/2) ? -1 : Index::value>
  {};
};

struct lowshuffled_up0_
{
  template<class Index, class Cardinal>
  struct apply  : boost::mpl
                ::int_< (Index::value >= Cardinal::value/2) ? Index::value : -1>
  {};
};

NT2_TEST_CASE_TPL( shuffle_optim, (float)(int32_t)(uint32_t) )
{
  using boost::simd::shuffle;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;

  {
    typedef native<T,ext_t>           vT;

    vT origin, reference, shuffled;
    for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
    origin[0] = boost::simd::Valmax<T>();

    // Half 0/shuffle
    for(std::size_t i=0; i < vT::static_size;++i)
      reference[i] = i < vT::static_size/2 ? origin[i] : 0;

    shuffled = shuffle<low0_upshuffled_>(origin);
    NT2_TEST_EQUAL(shuffled,reference);

    // Half shuffle/0
    for(std::size_t i=0; i < vT::static_size;++i)
      reference[i] = i < vT::static_size/2 ? 0 : origin[i];

    shuffled = shuffle<lowshuffled_up0_>(origin);
    NT2_TEST_EQUAL(shuffled,reference);
  }
}

NT2_TEST_CASE_TPL( shuffle_index2, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::shuffle;
  using boost::simd::native;
  using boost::simd::meta::vector_of;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef typename vector_of<T,2>::type    vT;

  vT origin, reference;
  vT id, shuffled, mirrored, bcasted;
  for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
  origin[0] = boost::simd::Valmax<T>();

  id = shuffle<0,1>(origin);
  NT2_TEST_EQUAL(id,origin);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[vT::static_size - i -1];
  shuffled = shuffle<1,0>(origin);
  NT2_TEST_EQUAL(shuffled,reference);

  NT2_TEST_EQUAL( (shuffle<-1,-1>(origin)), boost::simd::Zero<vT>());

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[i<vT::static_size/2 ? i : vT::static_size - i -1];
  mirrored = shuffle<0,0>(origin);
  NT2_TEST_EQUAL(mirrored,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[1];
  bcasted = shuffle<1,1>(origin);
  NT2_TEST_EQUAL(bcasted,reference);
}

NT2_TEST_CASE_TPL( shuffle_index4, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::shuffle;
  using boost::simd::native;
  using boost::simd::meta::vector_of;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef typename vector_of<T,4>::type    vT;

  vT origin, reference;
  vT id, shuffled, hnull, mirrored, bcasted,randed;
  for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
  origin[0] = boost::simd::Valmax<T>();

  id = shuffle<0,1,2,3>(origin);
  NT2_TEST_EQUAL(id,origin);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[vT::static_size - i -1];
  shuffled = shuffle<3,2,1,0>(origin);
  NT2_TEST_EQUAL(shuffled,reference);

  NT2_TEST_EQUAL( (shuffle<-1,-1,-1,-1>(origin)), boost::simd::Zero<vT>());

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[i<vT::static_size/2 ? i : vT::static_size - i -1];
  mirrored = shuffle<0,1,1,0>(origin);
  NT2_TEST_EQUAL(mirrored,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[1];
  bcasted = shuffle<1,1,1,1>(origin);
  NT2_TEST_EQUAL(bcasted,reference);
}

NT2_TEST_CASE_TPL( shuffle_index8, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::shuffle;
  using boost::simd::native;
  using boost::simd::meta::vector_of;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef typename vector_of<T,8>::type    vT;

  vT origin, reference;
  vT id, shuffled, hnull, mirrored, bcasted,randed;
  for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
  origin[0] = boost::simd::Valmax<T>();

  id = shuffle<0,1,2,3,4,5,6,7>(origin);
  NT2_TEST_EQUAL(id,origin);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[vT::static_size - i -1];
  shuffled = shuffle<7,6,5,4,3,2,1,0>(origin);
  NT2_TEST_EQUAL(shuffled,reference);

  NT2_TEST_EQUAL( (shuffle<-1,-1,-1,-1,-1,-1,-1,-1>(origin)), boost::simd::Zero<vT>());

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[i<vT::static_size/2 ? i : vT::static_size - i -1];
  mirrored = shuffle<0,1,2,3,3,2,1,0>(origin);
  NT2_TEST_EQUAL(mirrored,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[1];
  bcasted = shuffle<1,1,1,1,1,1,1,1>(origin);
  NT2_TEST_EQUAL(bcasted,reference);
}

NT2_TEST_CASE_TPL( shuffle_index16, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::shuffle;
  using boost::simd::native;
  using boost::simd::meta::vector_of;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef typename vector_of<T,16>::type    vT;

  vT origin, reference;
  vT id, shuffled, hnull, mirrored, bcasted,randed;
  for(std::size_t i=1; i < vT::static_size;++i) origin[i] = T(65+i);
  origin[0] = boost::simd::Valmax<T>();

  id = shuffle<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>(origin);
  NT2_TEST_EQUAL(id,origin);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[vT::static_size - i -1];
  shuffled = shuffle<15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0>(origin);
  NT2_TEST_EQUAL(shuffled,reference);

  NT2_TEST_EQUAL( (shuffle<-1,-1,-1,-1,-1,-1,-1,-1
                          ,-1,-1,-1,-1,-1,-1,-1,-1>(origin)), boost::simd::Zero<vT>());

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[i<vT::static_size/2 ? i : vT::static_size - i -1];
  mirrored = shuffle<0,1,2,3,4,5,6,7,7,6,5,4,3,2,1,0>(origin);
  NT2_TEST_EQUAL(mirrored,reference);

  for(std::size_t i=0; i < vT::static_size;++i)
    reference[i] = origin[1];
  bcasted = shuffle<1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1>(origin);
  NT2_TEST_EQUAL(bcasted,reference);
}


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "test_random_index"

#include <boost/test/unit_test.hpp>
#include "nanocv/math/random.hpp"

BOOST_AUTO_TEST_CASE(test_random)
{
        using namespace ncv;

        const size_t tests = 1024;
        const size_t test_size = 1024;

        for (size_t t = 0; t < tests; t ++)
        {
                const int32_t min = 17 + t;
                const int32_t max = min + t * 25 + 4;

                // initialize (uniform) random number generator for the index
                ncv::random_t<int32_t> rgen(min, max);
                ncv::random_index_t<int32_t> rindex(rgen);

                // initialize (uniform) random number generator for the size
                ncv::random_t<int32_t> rsize(0, std::numeric_limits<int32_t>::max());

                // check index generator
                for (size_t tt = 0; tt < test_size; tt ++)
                {
                        const int32_t size = rsize();
                        const int32_t index = rindex(size);

                        BOOST_CHECK_GE(index, 0);
                        BOOST_CHECK_LT(index, size);
                }
        }
}


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

#include <iostream>

#include <boost/numeric/mtl/mtl.hpp>

using mtl::io::tout;

template <typename Matrix>
void access_test(const Matrix& A)
{
    MTL_THROW_IF(A[0][0] != 1, mtl::runtime_error("wrong value"));
    MTL_THROW_IF(A[0][1] != 0, mtl::runtime_error("wrong value"));
    MTL_THROW_IF(A[0][2] != 2, mtl::runtime_error("wrong value"));
    MTL_THROW_IF(A[1][1] != 3, mtl::runtime_error("wrong value"));
    MTL_THROW_IF(A[2][2] != 4, mtl::runtime_error("wrong value"));
    MTL_THROW_IF(A[2][3] != 5, mtl::runtime_error("wrong value"));
}

template <typename Matrix>
void iteration_test(const Matrix& A)
{
  using mtl::tag::nz;
  namespace traits = mtl::traits;

  typename traits::row<Matrix>::type         row(A);
  typename traits::col<Matrix>::type         col(A);
  typename traits::const_value<Matrix>::type value(A);
  typedef typename traits::range_generator<nz, Matrix>::type Cursor;

  for (Cursor cursor = mtl::begin<nz>(A), cend = mtl::end<nz>(A); cursor != cend; ++cursor) {
     tout << "A[" << row(*cursor) << "," << col(*cursor) << "] = " << value(*cursor) << std::endl;
     MTL_THROW_IF(row(*cursor) == 0 && col(*cursor) == 2 && value(*cursor) != 2.0,
		  mtl::runtime_error("wrong value"))
  }

}

template <typename Matrix>
void copy_test(Matrix& A, unsigned dim1, unsigned dim2)
{
  Matrix B;
  laplacian_setup(B, dim1, dim2);

  typename Matrix::size_type  *row_ptr = B.address_major();
  typename Matrix::size_type  *col_ind = B.address_minor();
  typename Matrix::value_type *entries = B.address_data();

  A.change_dim(num_rows(B), num_cols(B));
  A = Matrix(num_rows(B), num_cols(B), B.nnz(), row_ptr, col_ind, entries);

  A *= 2.;
  MTL_THROW_IF(A[2][2] != 8, mtl::runtime_error("wrong value after scaling"));
  MTL_THROW_IF(B[2][2] != 4, mtl::runtime_error("wrong value after scaling, probably aliasing"));

  tout << "Matrix A:\n" << A;
  tout << "Matrix B:\n" << B;
}

template <typename Matrix>
void test(Matrix& A, const char* type)
{
    std::cout << "Matrix A [" << type << "]:\n" << A << std::endl;
    access_test(A);
    iteration_test(A);
    copy_test(A, 3, 3);
}

int main(int, char**)
{
    using namespace mtl; using std::size_t;

    typedef compressed2D<double> Matrix;

    size_t dim1= 3, dim2= 4, nnz = 5;

    /*
     * Initialize Matrix
     *
     *     [1 0 2 0]
     * A = [0 3 0 0]
     *     [0 0 4 5]
     */
    size_t row_ptr[] = {0, 2, 3, 5}, col_ind[] = {0, 2, 1, 2, 3};
    double entries[] = {1., 2., 3., 4., 5.};

    Matrix A(dim1, dim2, nnz, row_ptr, col_ind, entries);
    test(A, "row-major");

    typedef mtl::mat::parameters<mtl::tag::col_major>  Parameters2;
    typedef compressed2D<double, Parameters2>             Matrix2;

    size_t col_ptr2[] = {0, 1, 2, 4, 5}, row_ind2[] = {0, 1, 0, 2, 2};
    double entries2[] = {1., 3., 2., 4., 5.};

    Matrix2 A2(dim1, dim2, nnz, col_ptr2, row_ind2, entries2);
    test(A2, "column-major");

    return 0;
}