#define BOOST_TEST_MODULE TEST_RNN

#include <dynet/dynet.h>
#include <dynet/expr.h>
#include <dynet/rnn.h>
#include <dynet/lstm.h>
#include <dynet/fast-lstm.h>
#include <dynet/gru.h>
#include <dynet/grad-check.h>
#include <boost/test/unit_test.hpp>
#include "test.h"
#include <stdexcept>
#include <fstream>

using namespace dynet;
using namespace std;

struct RNNTest {
  RNNTest() {
    // initialize if necessary
    if (default_device == nullptr) {
      for (auto x : {"RNNTest", "--dynet-seed", "10", "--dynet-mem", "10"}) {
        av.push_back(strdup(x));
      }
      char **argv = &av[0];
      int argc = av.size();
      dynet::initialize(argc, argv);
    }
    seq_vals = {1.f, 0.f, 1.f, 1.f, 0.f, 1.f, 0.f, 1.f, 1.f, 1.f, 0.f, 1.f};
    ones_vals = {1.f, 1.f, 1.f};
    param_vals = {1.1f, -2.2f, 3.3f};
    param_1_vals = {1.1f};
    param_2_vals = {-2.2f, 3.3f};
    param_3_vals = {0.f, 0.1f, 0.2f};
    param_4_vals = {1.f, 0.1f, 1.2f, -0.3f};
    param_5_zeros_vals = {0.0, 0.0, 0.0, 0.0, 0.0};
    param_6_vals = {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f};
    param_8_vals = {0.1f, 0.1f, 0.2f, 0.3f, 0.2f, 0.3f, 0.4f, 0.5f};
    param_10_vals = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f};
    param_10_zeros_vals = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    param_12_vals = {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f};
    param_24_vals = {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f};
    param_36_vals = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, -0.5};
    param_60_vals = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.4f, 0.5f, 0.3f, 0.4f, 0.5f};
    param_6_mask_vals = {0.f, 1.0f, 1.0f, 1.f, 1.0f, 0.0f};
    param_8_mask_vals = {0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0};
    param_10_mask_vals = {0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0};

    param_1 = mod.add_parameters(Dim({1}));
    TensorTools::set_elements(param_1.get_storage().values, param_1_vals);
    param_2 = mod.add_parameters(Dim({2}));
    TensorTools::set_elements(param_2.get_storage().values, param_2_vals);
    param_3 = mod.add_parameters(Dim({3}));
    TensorTools::set_elements(param_3.get_storage().values, param_3_vals);
    param_4 = mod.add_parameters(Dim({4}));
    TensorTools::set_elements(param_4.get_storage().values, param_4_vals);
    param_5_zeros = mod.add_parameters(Dim({5}));
    TensorTools::set_elements(param_5_zeros.get_storage().values, param_5_zeros_vals);
    param_6 = mod.add_parameters(Dim({6}));
    TensorTools::set_elements(param_6.get_storage().values, param_6_vals);
    param_8 = mod.add_parameters(Dim({8}));
    TensorTools::set_elements(param_8.get_storage().values, param_8_vals);
    param_10 = mod.add_parameters(Dim({10}));
    TensorTools::set_elements(param_10.get_storage().values, param_10_vals);
    param_10_zeros = mod.add_parameters(Dim({10}));
    TensorTools::set_elements(param_10_zeros.get_storage().values, param_10_zeros_vals);
    param_12 = mod.add_parameters(Dim({12}));
    TensorTools::set_elements(param_6.get_storage().values, param_12_vals);
    param_24 = mod.add_parameters(Dim({24}));
    TensorTools::set_elements(param_24.get_storage().values, param_24_vals);
    param_36 = mod.add_parameters(Dim({36}));
    TensorTools::set_elements(param_36.get_storage().values, param_36_vals);
    param_60 = mod.add_parameters(Dim({60}));
    TensorTools::set_elements(param_60.get_storage().values, param_60_vals);
    param_6_mask = mod.add_parameters(Dim({6}));
    TensorTools::set_elements(param_6_mask.get_storage().values, param_6_mask_vals);
    param_8_mask = mod.add_parameters(Dim({8}));
    TensorTools::set_elements(param_8_mask.get_storage().values, param_8_mask_vals);
    param_10_mask = mod.add_parameters(Dim({10}));
    TensorTools::set_elements(param_10_mask.get_storage().values, param_10_mask_vals);

  }
  ~RNNTest() {
    // This was causing double deallocation errors?
    // for (auto x : av) free(x);
  }

  template <class T>
  std::string print_vec(const std::vector<T> vec) {
    ostringstream oss;
    if (vec.size()) oss << vec[0];
    for (size_t i = 1; i < vec.size(); i++)
      oss << ' ' << vec[i];
    return oss.str();
  }

  std::vector<float> ones_vals, param_vals, param_1_vals, param_2_vals, seq_vals, param_3_vals, param_4_vals, param_5_zeros_vals, param_6_vals, param_8_vals, param_10_vals, param_10_zeros_vals, param_12_vals, param_24_vals, param_36_vals, param_60_vals, param_6_mask_vals, param_8_mask_vals, param_10_mask_vals;
  std::vector<char*> av;
  dynet::ParameterCollection mod;
  dynet::Parameter param_1, param_2, param_3, param_4, param_5_zeros, param_6, param_8, param_10, param_10_zeros, param_12, param_24, param_36, param_60, param_6_mask, param_8_mask, param_10_mask;
};

// define the test suite
BOOST_FIXTURE_TEST_SUITE(rnn_test, RNNTest);

#define DYNET_RNN_GRADIENT_TEST_CASE(name, RNN_TYPE)      \
BOOST_AUTO_TEST_CASE( name ) {                            \
  dynet::ParameterCollection mod;                         \
  RNN_TYPE rnn(2,3,4,mod);                                \
  dynet::ComputationGraph cg;                             \
  rnn.new_graph(cg);                                      \
  rnn.start_new_sequence();                               \
  for(unsigned i=0;i<4;i++){                              \
    Expression x = dynet::input(cg,Dim({3}), ones_vals);  \
    rnn.add_input(x);                                     \
  }                                                       \
  Expression z = squared_norm(rnn.final_h()[1]);          \
  BOOST_CHECK(check_grad(mod, z, 0));                     \
}                                                         \

DYNET_RNN_GRADIENT_TEST_CASE(simple_rnn_gradient, dynet::SimpleRNNBuilder)

DYNET_RNN_GRADIENT_TEST_CASE(vanilla_lstm_gradient, dynet::VanillaLSTMBuilder)

DYNET_RNN_GRADIENT_TEST_CASE(compact_vanilla_lstm_gradient, dynet::CompactVanillaLSTMBuilder)

DYNET_RNN_GRADIENT_TEST_CASE(lstm_gradient, dynet::LSTMBuilder)

DYNET_RNN_GRADIENT_TEST_CASE(gru_gradient, dynet::GRUBuilder)

DYNET_RNN_GRADIENT_TEST_CASE(fast_lstm, dynet::FastLSTMBuilder)

BOOST_AUTO_TEST_CASE( vanilla_lstm_ln_gradient ) {
  dynet::ParameterCollection mod;
  dynet::VanillaLSTMBuilder vanilla_lstm(2, 3, 10, mod, true);
  dynet::ComputationGraph cg;
  vanilla_lstm.new_graph(cg);
  vanilla_lstm.start_new_sequence();
  for (unsigned i = 0; i < 4; i++) {
    Expression x = dynet::input(cg, Dim({3}), ones_vals);
    vanilla_lstm.add_input(x);
  }
  Expression z = squared_norm(vanilla_lstm.final_h()[1]);
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_fwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 3;
  unsigned hidden_dim = 5;
  unsigned batch_size = 1;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);
  vanilla_lstm_builder.start_new_sequence({dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.f, 0.f, 0.f, 0.f}),
					   dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.f, 0.f, 0.f, 0.f})});

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]);

  Expression c_tm1 = -dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.f, 0.f, 0.f, 0.f});
  Expression h_tm1 = -dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.f, 0.f, 0.f, 0.f});
  Expression x = -dynet::input(cg, Dim({input_dim}, batch_size), {1.f, 1.f, 1.f});
  for (unsigned i = 0; i < 3; i++) {
    const Tensor& builder_h = vanilla_lstm_builder.add_input(x).value();
    Expression gates_t = dynet::vanilla_lstm_gates(x, h_tm1, Wx, Wh, b);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    for(unsigned i=0; i < hidden_dim; i++){
      BOOST_CHECK_CLOSE(as_vector(h_t.value())[i], as_vector(builder_h)[i], 0.001);
    }
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
}

BOOST_AUTO_TEST_CASE( lstm_node_bwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 3;
  unsigned hidden_dim = 5;
  unsigned batch_size = 1;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]);

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_5_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_5_zeros), Dim({hidden_dim},batch_size));
  Expression x = dynet::reshape(dynet::parameter(cg, param_3), Dim({input_dim},batch_size));
  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates(x, h_tm1, Wx, Wh, b);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
  Expression z = squared_norm(sum_batches(h_tm1 + c_tm1));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_batched_forward ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 3;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);
  vanilla_lstm_builder.start_new_sequence({dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f}),
					   dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f})});

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]);

  Expression c_tm1 = dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f});
  Expression h_tm1 = dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f});
  Expression x = dynet::input(cg, Dim({input_dim}, batch_size), {1.f, 1.f, 1.f, 1.f, 1.f, 1.f});
  for (unsigned i = 0; i < 3; i++) {
    const Tensor& builder_h = vanilla_lstm_builder.add_input(x).value();
    Expression gates_t = dynet::vanilla_lstm_gates(x, h_tm1, Wx, Wh, b);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    for(unsigned i=0; i < hidden_dim; i++){
      BOOST_CHECK_CLOSE(as_vector(h_t.value())[i], as_vector(builder_h)[i], 0.001);
    }
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
}

BOOST_AUTO_TEST_CASE( lstm_node_h_fwd ) {
  unsigned hidden_dim = 3;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression c_t = dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.7f});
  Expression gates_t = dynet::input(cg, Dim({hidden_dim*4}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.f, -0.1f, -0.2f, -0.3f, -0.4f, -0.5f, 0.01f, 0.11f, 0.21f, 0.31f, 0.41f, 0.51f, -0.01f, -0.11f, -0.21f, -0.31f, -0.41f, -0.51f});
  Expression h_t = vanilla_lstm_h(c_t, gates_t);
  BOOST_CHECK_SMALL(as_vector(pick_batch_elem(h_t, (unsigned)0).value())[0], (float)1.0e-6);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(h_t, (unsigned)0).value())[1], -0.009966799462, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(h_t, (unsigned)0).value())[2], -0.03947506404, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(h_t, (unsigned)1).value())[0], -0.002913126125, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(h_t, (unsigned)1).value())[1], -0.04179438585, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(h_t, (unsigned)1).value())[2], -0.1269172332, 0.001);
}

BOOST_AUTO_TEST_CASE( lstm_node_h_gradient ) {
  unsigned hidden_dim = 3;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression c_t = dynet::reshape(dynet::parameter(cg, param_6), Dim({hidden_dim},batch_size));
  Expression gates_t = dynet::reshape(dynet::parameter(cg, param_24), Dim({hidden_dim*4},batch_size));
  Expression h_t = vanilla_lstm_h(c_t, gates_t);
  Expression z = squared_norm(sum_batches(h_t));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_c_gradient ) {
  unsigned hidden_dim = 3;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_6), Dim({hidden_dim},batch_size));
  Expression gates_t = dynet::reshape(dynet::parameter(cg, param_24), Dim({hidden_dim*4},batch_size));
  Expression c_t = vanilla_lstm_c(c_tm1, gates_t);
  Expression z = sum_elems(sum_batches(c_t));
  z.value();
  BOOST_CHECK(check_grad(mod, z, 0));
}
BOOST_AUTO_TEST_CASE( lstm_node_c_fwd ) {
  unsigned hidden_dim = 3;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression c_tm1 = dynet::input(cg, Dim({hidden_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f});
  Expression gates_t = dynet::input(cg, Dim({hidden_dim*4}, batch_size), {0.f, 0.1f, 0.2f,         0.3f, 0.4f, 0.5f,        0.f, -0.1f, -0.2f,        -0.3f, -0.4f, -0.5f,
									  0.01f, 0.11f, 0.21f,     0.31f, 0.41f, 0.51f,    -0.01f, -0.11f, -0.21f,    -0.31f, -0.41f, -0.51f});
  Expression c_t = vanilla_lstm_c(c_tm1, gates_t);
  BOOST_CHECK_SMALL(as_vector(pick_batch_elem(c_t, (unsigned)0).value())[0], (float)1.0e-6);
  BOOST_CHECK_SMALL(as_vector(pick_batch_elem(c_t, (unsigned)0).value())[1], (float)1.0e-6);
  BOOST_CHECK_SMALL(as_vector(pick_batch_elem(c_t, (unsigned)0).value())[2], (float)1.0e-6);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(c_t, (unsigned)1).value())[0], 0.0899, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(c_t, (unsigned)1).value())[1], 0.1189, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(c_t, (unsigned)1).value())[2], 0.1479, 0.001);
}
BOOST_AUTO_TEST_CASE( lstm_node_gates_fwd ) {
  unsigned input_dim = 2;
  unsigned hidden_dim = 2;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression x_t = dynet::input(cg, Dim({input_dim}, batch_size), {0.f, 0.1f, 0.2f, 0.3f});
//    cout << "x_t: " << print_vec(as_vector(x_t.value())) << "\n";
//    cout << "x_t b0: " << print_vec(as_vector(pick_batch_elem(x_t, (unsigned)0).value())) << "\n";
//    cout << "x_t b1: " << print_vec(as_vector(pick_batch_elem(x_t, (unsigned)1).value())) << "\n";
  Expression h_tm1 = dynet::input(cg, Dim({input_dim}, batch_size), {0.f, -0.1f, 0.2f, -0.3f});
  Expression Wx = dynet::input(cg, Dim({hidden_dim*4, input_dim}, 1), {0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f});
  Expression Wh = dynet::input(cg, Dim({hidden_dim*4, hidden_dim}, 1), {0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f});
  Expression b = dynet::input(cg, Dim({hidden_dim*4}, 1), {0.f, 0.1f, 0.f, 0.1f, 0.f, 0.1f, 0.f, 0.1f});
  Expression gates = vanilla_lstm_gates(x_t, h_tm1, Wx, Wh, b);
  // Wx*x = 0.11 0.33 / 0.33 1.43
  // Wh*h = -0.12 -0.34 / -0.34 -0.56
  // + b  = -0.01 0.09 / -0.01 0.97
  // intermediate step:
  // Wx*x + Wh*h + b =
  // [-0.01 0.09 0.99 1.09 -0.01 0.09 -0.01 0.09]^T  (including forget bias + 1)
  // [-0.01 0.53 1.87 2.41 -0.01 0.53 0.87 1.41]^T   (including forget bias + 1)
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[0], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[1], 0.522484825, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[2], 0.729087922, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[3], 0.748381722, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[4], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[5], 0.522484825, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[6], -0.009999656681, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[7], 0.08975778475, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[0], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[1], 0.629483112, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[2], 0.866458277, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[3], 0.917586682, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[4], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[5], 0.629483112, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[6], 0.7013741309, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[7], 0.8874941329, 0.001);
}

BOOST_AUTO_TEST_CASE( lstm_node_gates_bwd ) {
  unsigned input_dim = 5;
  unsigned hidden_dim = 3;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression x_t = dynet::reshape(dynet::parameter(cg, param_10), Dim({input_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_6), Dim({hidden_dim},batch_size));
  Expression Wx = dynet::reshape(dynet::parameter(cg, param_60), Dim({hidden_dim*4, input_dim},1));
  Expression Wh = dynet::reshape(dynet::parameter(cg, param_36), Dim({hidden_dim*4, hidden_dim},1));
  Expression b = dynet::reshape(dynet::parameter(cg, param_12), Dim({hidden_dim*4},1));

  Expression gates = vanilla_lstm_gates(x_t, h_tm1, Wx, Wh, b);

  Expression z = squared_norm(sum_batches(gates));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_gates_dropout_fwd ) {
  unsigned input_dim = 2;
  unsigned hidden_dim = 2;
  unsigned batch_size = 2;
  dynet::ComputationGraph cg;

  Expression x_t = dynet::input(cg, Dim({input_dim}, batch_size), {0.0, 0.1, 0.2, 0.3});
  Expression mask_x = dynet::input(cg, Dim({input_dim}, batch_size), {0.0, 1, 1, 1});
//    cout << "x_t: " << print_vec(as_vector(x_t.value())) << "\n";
//    cout << "x_t b0: " << print_vec(as_vector(pick_batch_elem(x_t, (unsigned)0).value())) << "\n";
//    cout << "x_t b1: " << print_vec(as_vector(pick_batch_elem(x_t, (unsigned)1).value())) << "\n";
  Expression h_tm1 = dynet::input(cg, Dim({input_dim}, batch_size), {0.f, -0.1, 0.2, -0.3});
  Expression mask_h = dynet::input(cg, Dim({input_dim}, batch_size), {0.0, 0.0, 1.0, 1.0});
  Expression Wx = dynet::input(cg, Dim({hidden_dim*4, input_dim}, 1), {0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f, 0.f, 1.1f, 2.2f, 3.3f});
  Expression Wh = dynet::input(cg, Dim({hidden_dim*4, hidden_dim}, 1), {0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f, 0.1f, 1.2f, 2.3f, 3.4f});
  Expression b = dynet::input(cg, Dim({hidden_dim*4}, 1), {0.f, 0.1f, 0.f, 0.1f, 0.f, 0.1f, 0.f, 0.1f});
  Expression gates = vanilla_lstm_gates_dropout(x_t, h_tm1, Wx, Wh, b, mask_x, mask_h);
  // Wx*x = 0.11 0.33 / 0.33 1.43
  // Wh*h = -0.12 -0.34 / -0.34 -0.56
  // + b  = -0.01 0.09 / -0.01 0.97
  // intermediate step:
  // Wx*x + Wh*h + b =
  // [0 0.21 0.22 0.43 1 1.21 0.22 0.43]^T  (including forget bias + 1)
  // [-0.01 0.53 1.87 2.41 -0.01 0.53 0.87 1.41]^T   (including forget bias + 1)
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[0], 0.5, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[1], 0.5523079096, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[2], 0.7720635494, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[3], 0.8069013158, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[4], 0.5, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[5], 0.5523079096, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[6], 0.2165180615, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)0).value())[7], 0.4053213087, 0.001);

  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[0], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[1], 0.629483112, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[2], 0.866458277, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[3], 0.917586682, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[4], 0.497500021, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[5], 0.629483112, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[6], 0.7013741309, 0.001);
  BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates, (unsigned)1).value())[7], 0.8874941329, 0.001);
}

BOOST_AUTO_TEST_CASE( lstm_node_dropout_bwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 3;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression x = dynet::reshape(dynet::parameter(cg, param_6), Dim({input_dim},batch_size));

  Expression mask_x = dynet::reshape(dynet::parameter(cg, param_6_mask), Dim({input_dim},batch_size))*0.9;
  Expression mask_h = dynet::reshape(dynet::parameter(cg, param_10_mask), Dim({hidden_dim},batch_size))*0.8;

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_dropout(x, h_tm1, Wx, Wh, b, mask_x, mask_h, 0.0f);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
  Expression z = squared_norm(sum_batches(h_tm1 + c_tm1));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_multi_input_bwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 4;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression x1 = dynet::reshape(dynet::parameter(cg, param_2), Dim({1},batch_size));
  Expression x3 = dynet::reshape(dynet::parameter(cg, param_6), Dim({3},batch_size));

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_concat({x1, x3}, h_tm1, Wx, Wh, b, 0.0f);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
  Expression z = squared_norm(sum_batches(h_tm1 + c_tm1));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_multi_input_fwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 4;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));

  Expression x = dynet::reshape(dynet::parameter(cg, param_8), Dim({input_dim},batch_size));
  Expression x1 = dynet::pick_range(x, 0, 1);
  Expression x3 = dynet::pick_range(x, 1, 4);

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_concat({x1, x3}, h_tm1, Wx, Wh, b, 0.0f);
    Expression gates_t_2 = dynet::vanilla_lstm_gates_concat({x}, h_tm1, Wx, Wh, b, 0.0f);
    for(int b=0; b<batch_size; b++){
      for(int u=0; u<4*hidden_dim; u++){
        BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates_t, (unsigned)b).value())[u],
        as_vector(pick_batch_elem(gates_t_2, (unsigned)b).value())[u],
        0.001);
      }
    }
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
}

BOOST_AUTO_TEST_CASE( lstm_node_dropout_multi_input_bwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 4;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression x1 = dynet::reshape(dynet::parameter(cg, param_2), Dim({1},batch_size));
  Expression x3 = dynet::reshape(dynet::parameter(cg, param_6), Dim({3},batch_size));

  Expression mask_x = dynet::reshape(dynet::parameter(cg, param_8_mask), Dim({input_dim},batch_size))*0.9;
  Expression mask_h = dynet::reshape(dynet::parameter(cg, param_10_mask), Dim({hidden_dim},batch_size))*0.8;

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_dropout_concat({x1, x3}, h_tm1, Wx, Wh, b, mask_x, mask_h, 0.0f);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
  Expression z = squared_norm(sum_batches(h_tm1 + c_tm1));
  BOOST_CHECK(check_grad(mod, z, 0));
}

BOOST_AUTO_TEST_CASE( lstm_node_dropout_multi_input_fwd ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 4;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));

  Expression x = dynet::reshape(dynet::parameter(cg, param_8), Dim({input_dim},batch_size));
  Expression x1 = dynet::pick_range(x, 0, 1);
  Expression x3 = dynet::pick_range(x, 1, 4);

  Expression mask_x = dynet::reshape(dynet::parameter(cg, param_8_mask), Dim({input_dim},batch_size))*0.9;
  Expression mask_h = dynet::reshape(dynet::parameter(cg, param_10_mask), Dim({hidden_dim},batch_size))*0.8;

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_dropout_concat({x1, x3}, h_tm1, Wx, Wh, b, mask_x, mask_h, 0.0f);
    Expression gates_t_2 = dynet::vanilla_lstm_gates_dropout(x, h_tm1, Wx, Wh, b, mask_x, mask_h, 0.0f);
    for(int b=0; b<batch_size; b++){
      for(int u=0; u<4*hidden_dim; u++){
        BOOST_CHECK_CLOSE(as_vector(pick_batch_elem(gates_t, (unsigned)b).value())[u],
        as_vector(pick_batch_elem(gates_t_2, (unsigned)b).value())[u],
        0.001);
      }
    }
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
}

BOOST_AUTO_TEST_CASE( lstm_node_multi_weightnoise_runs ) {
  dynet::ParameterCollection mod;
  unsigned input_dim = 4;
  unsigned hidden_dim = 5;
  unsigned batch_size = 2;
  dynet::VanillaLSTMBuilder vanilla_lstm_builder(1, input_dim, hidden_dim, mod, false);
  dynet::ComputationGraph cg;
  vanilla_lstm_builder.new_graph(cg);

  Expression Wx = parameter(cg, vanilla_lstm_builder.params[0][0]);
  Expression Wh = parameter(cg, vanilla_lstm_builder.params[0][1]);
  Expression b = parameter(cg, vanilla_lstm_builder.params[0][2]) + 0.5;

  Expression c_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));
  Expression h_tm1 = dynet::reshape(dynet::parameter(cg, param_10_zeros), Dim({hidden_dim},batch_size));

  Expression x = dynet::reshape(dynet::parameter(cg, param_8), Dim({input_dim},batch_size));
  Expression x1 = dynet::pick_range(x, 0, 1);
  Expression x3 = dynet::pick_range(x, 1, 4);

  for (unsigned i = 0; i < 3; i++) {
    Expression gates_t = dynet::vanilla_lstm_gates_concat({x1, x3}, h_tm1, Wx, Wh, b, 0.1f);
    gates_t = dynet::vanilla_lstm_gates(x, h_tm1, Wx, Wh, b, 0.1f);
    Expression c_t = dynet::vanilla_lstm_c(c_tm1, gates_t);
    Expression h_t = dynet::vanilla_lstm_h(c_t, gates_t);
    c_tm1 = c_t;
    h_tm1 = h_t;
  }
}

BOOST_AUTO_TEST_SUITE_END()


#include "blazecorr.h"
#include "ui_blazecorr.h"
#include <iostream>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>
#include <cstdlib>
#include <QVector>
#include <spline.h>
#include <vector>
#define ARMA_64BIT_WORD
#include <armadillo>
#include <CCfits/CCfits>

using namespace std;
using std::vector;
using namespace arma;
using namespace arpack;

double g, thetabc, dbc, tot, lwbc, uwbc, fcam, pix, in, atbc, out, n1, n2, Il1, Il2, dout, powA, powB, scalef, gres, gres2;
int npix, nl, nu, nor, nmlines, bcorab=0;
QVector<double> eta(1), optval(1), Mi(1), Mw(1);
const double RAD = M_PI/180;
tk::spline s;
QString qBExt, qWCol, qICol;
string bExt, wCol, iCol;

BlazeCorr::BlazeCorr(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::BlazeCorr)
{
    ui->setupUi(this);

    this->setWindowTitle("Blaze Correction");

    ui->lineEdit->setText("master.dat");
    ui->lineEdit_3->setText("spline.dat");
    ui->lineEdit_4->setText("division.dat");
    ui->lineEdit_5->setText("spica.dat");
    ui->lineEdit_6->setText("splinefit.dat");

    ui->lineEdit_7->setText("Ha_vel_diff.txt");
    ui->lineEdit_10->setText("Ha_vel_diff_");
    ui->lineEdit_11->setText("txt");

    ui->comboBox->addItem("ASCII");
    ui->comboBox->addItem("fits");

    ui->lineEdit_15->setText("cor");

    ui->lineEdit_12->setText("DataVector");
    ui->lineEdit_13->setText("Arg");
    ui->lineEdit_14->setText("Fun");
    qBExt=ui->lineEdit_12->text();
    bExt = qBExt.toUtf8().constData();
    qWCol=ui->lineEdit_13->text();
    wCol = qWCol.toUtf8().constData();
    qICol=ui->lineEdit_14->text();
    iCol = qICol.toUtf8().constData();

    QFont legendFont = font();
    legendFont.setPointSize(16);
    ui->customPlot->axisRect()->setupFullAxesBox(true);
    ui->customPlot->xAxis->setLabelFont(legendFont);
    ui->customPlot->yAxis->setLabelFont(legendFont);
    ui->customPlot->xAxis->setTickLabelFont(legendFont);
    ui->customPlot->yAxis->setTickLabelFont(legendFont);

    ui->progressBar->setValue(0);
}

BlazeCorr::~BlazeCorr()
{
    delete ui;
}

void BlazeCorr::seData(QString str)
{
    ui->lineEdit_2->setText(str);
}

//******************************************
// Plot spline
//******************************************
void BlazeCorr::on_pushButton_5_clicked()
{
    int sppoi = ui->spinBox_2->value();
    int inter=0, counts=0;
    int smlines = 0;
    string eins, zwei, line;

    QString inputA=ui->lineEdit_2->text()+"/"+ui->lineEdit->text();
    string dataA = inputA.toUtf8().constData();
    std::ostringstream datANameStream(dataA);
    datANameStream<<dataA;
    std::string datAName = datANameStream.str();

    QFile checkfile1(datAName.c_str());

    if(!checkfile1.exists()){
        qDebug()<<"Error 1: The file "<<checkfile1.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile1.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
       return;
    }

    ifstream inA(datAName.c_str());

    QString outputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_3->text();
    string dataoA = outputA.toUtf8().constData();
    std::ostringstream datoANameStream(dataoA);
    datoANameStream<<dataoA;
    std::string datoAName = datoANameStream.str();
    ofstream outA(datoAName.c_str());

    while(std::getline(inA, line))
       ++smlines;

    inA.clear();
    inA.seekg(0, ios::beg);

    inter = smlines/sppoi;
    sppoi = smlines/inter+1;
    cout<<inter<<"\t"<<smlines<<"\t"<<sppoi<<endl;

    QVector<double> Msw(smlines), Msi(smlines), sw(sppoi), si(sppoi);

    for(int i =0; i<smlines; i++){
        inA >> eins >>zwei;
        istringstream ist(eins);
        ist >> Msw[i];
        istringstream ist2(zwei);
        ist2 >> Msi[i];
        if(i == counts*inter){
            sw[counts] = Msw[i];
            si[counts] = Msi[i];

            outA<<setprecision(14)<<sw[counts]<<"\t"<<si[counts]<<endl;
            ++counts;
        }
    }
    inA.close();
    outA.close();

    QPen pen1;
    pen1.setColor(Qt::red);

    ui->customPlot->clearGraphs();
    ui->customPlot->addGraph();
    ui->customPlot->graph(0)->setData(Msw, Msi);
    ui->customPlot->graph(0)->rescaleAxes();
    ui->customPlot->addGraph();
    ui->customPlot->graph(1)->setLineStyle(QCPGraph::lsNone);
    ui->customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 2));
    ui->customPlot->graph(1)->setPen(pen1);
    ui->customPlot->graph(1)->setData(sw, si);
    ui->customPlot->graph(1)->rescaleAxes(true);
    ui->customPlot->replot();
}

//******************************************
// fit spline
//******************************************
void BlazeCorr::on_pushButton_4_clicked()
{
    this->setCursor(QCursor(Qt::WaitCursor));

    ui->progressBar->setValue(0);

    int sppoi = ui->spinBox_2->value();
    int inter=0, counts=0;
    int smlines = 0, smlines2=0;
    string eins, zwei, line;
    double x;

    QString inputA=ui->lineEdit_2->text()+"/"+ui->lineEdit->text();
    string dataA = inputA.toUtf8().constData();
    std::ostringstream datANameStream(dataA);
    datANameStream<<dataA;
    std::string datAName = datANameStream.str();

    QFile checkfile1(datAName.c_str());

    if(!checkfile1.exists()){
        qDebug()<<"Error 1: The file "<<checkfile1.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile1.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
       return;
    }

    ifstream inA(datAName.c_str());

    QString inputB=ui->lineEdit_2->text()+"/"+ui->lineEdit_3->text();
    string dataB = inputB.toUtf8().constData();
    std::ostringstream datBNameStream(dataB);
    datBNameStream<<dataB;
    std::string datBName = datBNameStream.str();

    QFile checkfile2(datBName.c_str());

    if(!checkfile2.exists()){
        qDebug()<<"Error 1: The file "<<checkfile2.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile2.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
       return;
    }

    ifstream inB(datBName.c_str());

    while(std::getline(inA, line))
       ++smlines;

    inA.clear();
    inA.seekg(0, ios::beg);

    inter = smlines/sppoi;
    sppoi = smlines/inter+1;

    QVector<double> Msw(smlines), Msi(smlines), sw(smlines), si(smlines), stabw(sppoi);
    double tmean;

    for(int i =0; i<smlines; i++){
        inA >> eins >>zwei;
        istringstream ist(eins);
        ist >> Msw[i];
        istringstream ist2(zwei);
        ist2 >> Msi[i];
        sw[i]=Msw[i];
    }
    inA.close();

    counts = 0;
    cout<<"test"<<endl;

    for(int i = 0; i<smlines; i++){
        if(i == counts*inter){
            stabw[counts]=0;
            tmean=0;
            if(i==0){
                for(int e = 0; e<10; e++){
                    tmean += Msi[i+e]/10;
                }
                for(int e = 0; e<10; e++){
                    stabw[counts] += pow((tmean-Msi[i+e]),2);
                }
            }
            else{
                if(i>smlines-11){
                    for(int e = 0; e<10; e++){
                        tmean += Msi[i-e]/10;
                    }
                    for(int e = 0; e<10; e++){
                        stabw[counts] += pow((tmean-Msi[i-e]),2);
                    }
                }
                else{
                    for(int e = -4; e<5; e++){
                         tmean += Msi[i+e]/10;
                    }
                    for(int e = 0; e<10; e++){
                        stabw[counts] +=pow((tmean-Msi[i+e]),2);
                    }
                }

            }
            stabw[counts]=sqrt(stabw[counts]/10);
            ++counts;
        }
    }
    tmean=0;

    while(std::getline(inB, line))
       ++smlines2;

    inB.clear();
    inB.seekg(0, ios::beg);

    vector<double> spw(smlines2), spi(smlines2);

    cout<<inter<<"\t"<<smlines<<"\t"<<sppoi<<"\t"<<smlines2<<endl;

    double clipa=ui->doubleSpinBox_3->value();
    double clipb=ui->doubleSpinBox_4->value();
    int clspace = ui->spinBox_4->value();

    for(int i =0; i<smlines2; i++){
        inB >> eins >>zwei;
        istringstream ist3(eins);
        istringstream ist4(zwei);
        ist3 >> spw[i];
        ist4 >> spi[i];
        if(i>0 & (spw[i]<spw[i-1])){
            cout<<"Wrong order at position: "<<i<<endl;
            spw[i]=spw[i-1];
            spw[i]=spi[i-1];
        }
        else{
            ist3 >> spw[i];
            ist4 >> spi[i];
        }
        ist4 >> spi[i];
    }
    inB.close();

    int clipiter=1;
    if(ui->checkBox_6->isChecked()){
        clipiter=2;
    }
    else{
        clipiter=1;
    }

    int valr = smlines2, upda=0, ccount=0, zaehler=3*smlines2;
    double resi=0;
    cout<<valr<<endl;
    int Ph, Pl, Psh, eval=0;
    double yh, ysh, yl, ym, yi, ys, yt;
    double gamma=2.0;	//expansion coeff.
    double alpha =1.0;	//reflection coeff.
    double beta=0.5;	//contraction coeff.
    double btot=0.5;	//total contraction coeff.
    mat Pm(valr+1,valr), e(valr+1,valr);

    for(int cl=0; cl<clipiter; cl++){

    upda=0;
    ccount=0;
    resi=0;
    eval=0;
    double Z[valr], C[valr], S[valr], Em[valr], X[valr], y[valr+1], step[valr];

    Pm.resize(valr+1,valr);
    e.resize(valr+1,valr);

    //initial points
    for(int i =0; i<valr; i++){
        Pm(0,i)=spi[i];
        if(cl==0){
            step[i]=2*stabw[i];
        }
        else{
            step[i]=tmean;
        }
    }

    for (int i=0; i<valr+1; i++){
        for (int j=0; j<valr; j++){
            if(i>0 & i-1==j){
                e(i,j)=1.0;
            }
            else{
                e(i,j)=0.0;
            }
            if(i==0){

                spi[j]=Pm(0,j);
            }

            if(i!=0){
                Pm(i,j)=Pm(0,j)+step[i]*e(i,j);
            }
             spi[j]=Pm(i,j);
        }

        qApp->processEvents(QEventLoop::AllEvents);
        if(bcorab==1){
            bcorab=0;
            this->setCursor(QCursor(Qt::ArrowCursor));
            return;
        }
        //*************************************
        // function
        s.set_points(spw,spi);
        tmean=0;
        for(int ef = 0; ef<smlines; ef++){
            x = Msw[ef];
            si[ef]=s(x);
            tmean += pow((si[ef]-Msi[ef]),2);
        }
        tmean = sqrt(tmean/smlines);
        //*************************************
        y[i]=tmean;
        if(i==0 or tmean<resi){
            resi = y[i];
            ++upda;
        }

        ui->progressBar->setValue(100*(i+1)/(2*(valr+1)));

        eval++;
    }

    //start main loop
         for (int tc=0; tc<zaehler; tc++){
             //cout<<"tmean: "<<tmean<<endl;
             ui->progressBar->setValue(100*(tc+valr+2)/(2*(valr+1)));

         //initialize next step
         ym=0;
         ys=0;
         for (int i=0; i<valr; i++){
            Z[i]=0;
         }

         //looking for highest value
         yh=y[0];
         for (int j=0; j<valr+1; j++){
            if(y[j]>=yh){
                yh = y[j];
                Ph = j;
            }
         }

         //looking for smallest value
         yl=yh;
         for (int j=0; j<valr+1; j++){
            if(y[j]<yl){
                yl=y[j];
                Pl = j;
            }
         }

         // second highest value
         ysh=yl;
         for (int j=0; j<valr+1; j++){
             if((y[j]>ysh) & (y[j]<yh) & (y[j]>yl) & (j !=Pl)){
                 ysh=y[j];
                 Psh=j;
             }
         }

         yh=y[Ph];
         yl=y[Pl];
         ysh=y[Psh];

         //computing mean and sigma
         for (int i=0; i<valr+1; i++){
            ym+=y[i]/(valr+1);
         }
         for (int i=0; i<valr+1; i++){
            ys+=sqrt(pow((y[i]-ym),2));
         }
         ys=ys/(valr);

         //compute centroid
         for (int j=0; j<valr; j++){
            for (int i=0; i<valr+1; i++){
                if (i!=Ph){
                    Z[j]+=Pm(i,j)/valr;
                }
            }
         }

         //reflect highest value at centroid
         for (int i=0; i<valr; i++){
            C[i]=Z[i]+alpha*(Z[i]-Pm(Ph,i));
            spi[i]=C[i];
         }
         //*************************************
         // function
         s.set_points(spw,spi);
         tmean=0;
         for(int ef = 0; ef<smlines; ef++){
             x = Msw[ef];
             si[ef]=s(x);
             tmean += pow((si[ef]-Msi[ef]),2);
         }
         tmean = sqrt(tmean/smlines);
         //*************************************
         yt=tmean;
         if(yt<resi){
             resi=yt;
             ++upda;
         }
         else{
             //
         }
         eval++;

         if(yi<yl){
                 for (int i=0; i<valr; i++){
                    Em[i]=Z[i]+gamma*(C[i]-Z[i]);
                    spi[i]=Em[i];
                 }
                 //*************************************
                 // function
                 s.set_points(spw,spi);
                 tmean=0;
                 for(int ef = 0; ef<smlines; ef++){
                     x = Msw[ef];
                     si[ef]=s(x);
                     tmean += pow((si[ef]-Msi[ef]),2);
                 }
                 tmean = sqrt(tmean/smlines);
                 //*************************************
                 yt=tmean;
                 if(yt<resi){
                     resi=yt;
                     ++upda;
                 }
                 else{
                     //
                 }
                 eval++;
                 if(yt<yl){
                 for (int i=0; i<valr; i++){
                 Pm(Ph,i)=Em[i];
                 }
                 y[Ph]=yt;//BTfunction(E);
                 //eval++;
                 }
                 if (yt>=yl){
                 for (int i=0; i<valr; i++){
                 Pm(Ph,i)=C[i];
                 }
                 eval++;
                 y[Ph]=yi;
                 }}

                 if(yi>=yl){
                 if(yi<=ysh){
                 for(int i=0; i<valr; i++){
                 Pm(Ph,i)=C[i];
                 }
                 eval++;
                 y[Ph]=yi;
                 }
                 if(yi>ysh){
                 if(yi<=yh){
                 for(int i=0; i<valr; i++){
                 Pm(Ph,i)=C[i];
                 }
                 eval++;
                 y[Ph]=yi;
                 yh=y[Ph];
                 }
                 for(int i=0; i<valr; i++){
                 S[i]=Z[i]+beta*(Pm(Ph,i)-Z[i]);
                 spi[i]=S[i];
                 }
                 //*************************************
                 // function
                 s.set_points(spw,spi);
                 tmean=0;;
                 for(int ef = 0; ef<smlines; ef++){
                     x = Msw[ef];
                     si[ef]=s(x);
                     tmean += pow((si[ef]-Msi[ef]),2);
                 }
                 tmean = sqrt(tmean/smlines);
                 //*************************************
                 yt=tmean;
                 if(yt<resi){
                     resi=yt;
                     ++upda;
                 }
                 else{
                     //
                 }
                 eval++;
                 if(yt>yh){
                 for (int j=0; j<valr+1; j++){
                 for (int i=0; i<valr; i++){
                 Pm(j,i)=Pm(Pl,i)+btot*(Pm(j,i)-Pm(Pl,i)); //total contraction
                 X[i]=Pm(j,i);
                 spi[i]=X[i];
                 }
                 //*************************************
                 // function
                 s.set_points(spw,spi);
                 tmean=0;
                 for(int ef = 0; ef<smlines; ef++){
                     x = Msw[ef];
                     si[ef]=s(x);
                     tmean += pow((si[ef]-Msi[ef]),2);
                 }
                 tmean = sqrt(tmean/smlines);
                 //*************************************
                 y[j]=tmean;
                 if(y[j]<resi){
                     resi=y[j];
                     ++upda;
                 }
                 else{
                     //
                 }
                 eval++;
                 }}

                 if(yt<=yh){
                 for(int i=0; i<valr; i++){
                 Pm(Ph,i)=S[i];
                 }
                 eval++;
                 y[Ph]=yt;
                 }}

                 }

                 }//end main loop

         //looking for highest value
         yh=y[0];
         for (int j=0; j<valr+1; j++){
         if(y[j]>=yh){
         yh = y[j];
         Ph = j;
         }}

         //looking for smallest value
         yl=yh;
         for (int j=0; j<valr+1; j++){
         if(y[j]<yl){
         yl=y[j];
         if(y[j]<resi){
             resi=y[j];
             ++upda;
         }
         Pl = j;
         }}

         //looking for second highest value
         ysh=yl;
         for (int j=0; j<valr+1; j++){
         if(y[j]>ysh & (y[j]<yh)){
         ysh=y[j];
         Psh=j;
         }}

         QVector<double> spi2(smlines), spw2(smlines);

         if(ui->checkBox_6->isChecked() & (cl<clipiter-1)){
            ccount=0;
            for(int gg=0; gg<smlines; gg++){
                x = Msw[gg];
                si[gg]=s(x);
                if((Msi[gg]<si[gg]+tmean*clipa) & (Msi[gg]>si[gg]-tmean*clipb)){
                    spi2[ccount]=Msi[gg];
                    spw2[ccount]=Msw[gg];
                    ++ccount;
                    gg+=clspace;

                }
                else{
                    //cout<<"Clipping "<<clipiter<<" excluded point "<<gg<<endl;
                }
            }

            if(ccount==1){
                cout<<"All points excluded!"<<endl;
                this->setCursor(QCursor(Qt::ArrowCursor));
                return;
            }
            if(ccount<3){
                cout<<"Not enough points left."<<endl;
                this->setCursor(QCursor(Qt::ArrowCursor));
                return;
            }

            valr=ccount;
            cout<<"clip: "<<cl<<"; ccount: "<<ccount<<endl;
            zaehler = 3*valr;
            spi.resize(valr);
            spw.resize(valr);

            for(int gg=0; gg<valr; gg++){
                spi[gg]=spi2[gg];
                spw[gg]=spw2[gg];
                //cout<<spw[gg]<<"\t"<<spi[gg]<<endl;
            }
         }

        } // end clipping

                 QString outputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_3->text();
                 string dataoA = outputA.toUtf8().constData();
                 std::ostringstream datoANameStream(dataoA);
                 datoANameStream<<dataoA;
                 std::string datoAName = datoANameStream.str();
                 ofstream outA(datoAName.c_str());

                 for(int i =0; i<spi.size(); i++){
                     spi[i]=Pm(Pl,i);
                     outA<<setprecision(14)<<spw[i]<<"\t"<<spi[i]<<endl;
                 }

                 QString outputB=ui->lineEdit_2->text()+"/"+ui->lineEdit_6->text();
                 string dataoB = outputB.toUtf8().constData();
                 std::ostringstream datoBNameStream(dataoB);
                 datoBNameStream<<dataoB;
                 std::string datoBName = datoBNameStream.str();
                 ofstream outB(datoBName.c_str());

                 QVector<double> spla(smlines), splb(smlines);

                 s.set_points(spw,spi);
                 for(int i = 0; i<smlines; i++){
                     x = Msw[i];
                     si[i]=s(x);
                     outB<<setprecision(14)<<sw[i]<<"\t"<<si[i]<<endl;
                     spla[i]=si[i]+clipa*tmean;
                     splb[i]=si[i]-clipb*tmean;
                 }

                 QPen pen1;
                 pen1.setColor(Qt::red);

                 ui->customPlot->clearGraphs();
                 ui->customPlot->addGraph();
                 ui->customPlot->graph(0)->setData(Msw, Msi);
                 ui->customPlot->graph(0)->rescaleAxes();
                 ui->customPlot->addGraph();
                 ui->customPlot->graph(1)->setLineStyle(QCPGraph::lsNone);
                 ui->customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 2));
                 ui->customPlot->graph(1)->setPen(pen1);
                 ui->customPlot->graph(1)->setData(sw, si);
                 ui->customPlot->graph(1)->rescaleAxes(true);
                 if(ui->checkBox_6->isChecked()){
                     QPen pen2;
                     pen2.setColor(Qt::green);
                     QPen pen3;
                     pen3.setColor(Qt::black);
                     ui->customPlot->addGraph();
                     ui->customPlot->graph(2)->setPen(pen2);
                     ui->customPlot->graph(2)->setData(Msw, spla);
                     ui->customPlot->graph(2)->rescaleAxes(true);
                     ui->customPlot->addGraph();
                     ui->customPlot->graph(3)->setPen(pen3);
                     ui->customPlot->graph(3)->setData(Msw, splb);
                     ui->customPlot->graph(3)->rescaleAxes(true);
                 }
                 ui->customPlot->replot();
                 //QTimer::singleShot(1000, this, SLOT(yourSlot()));

                 ui->progressBar->setValue(100);
                 cout<<"upda: "<<upda<<endl;
                 if(ui->checkBox_4->isChecked() & upda!=2){
                     BlazeCorr::on_pushButton_4_clicked();
                 }

    this->setCursor(QCursor(Qt::ArrowCursor));

}

//**************************************
// Divide, add, subtract
//**************************************
void BlazeCorr::on_pushButton_6_clicked()
{
    string eins, zwei, line;
    int dslines1=0, dslines2=0;
    double x;

    QString inputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_5->text();
    string dataA = inputA.toUtf8().constData();
    std::ostringstream datANameStream(dataA);
    datANameStream<<dataA;
    std::string datAName = datANameStream.str();

    QFile checkfile1(datAName.c_str());

    if(!checkfile1.exists()){
        qDebug()<<"Error 1: The file "<<checkfile1.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile1.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
       return;
    }

    ifstream inA(datAName.c_str());

    QString outputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_6->text();
    string dataoA = outputA.toUtf8().constData();
    std::ostringstream datoANameStream(dataoA);
    datoANameStream<<dataoA;
    std::string datoAName = datoANameStream.str();

    QFile checkfile2(datoAName.c_str());

    if(!checkfile2.exists()){
        qDebug()<<"Error 1: The file "<<checkfile2.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile2.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
    }

    ifstream inB(datoAName.c_str());

    while(std::getline(inA, line))
       ++dslines1;

    inA.clear();
    inA.seekg(0, ios::beg);

    while(std::getline(inB, line))
       ++dslines2;

    inB.clear();
    inB.seekg(0, ios::beg);

    if(dslines1 != dslines2){
        dslines1=dslines2;
        QMessageBox::information(this, "Warning", "Spline and Measurement not the sampe samling.");
        cout<<dslines1<<"\t"<<dslines2<<endl;
        //return;
    }

    QString outA=ui->lineEdit_2->text()+"/"+ui->lineEdit_4->text();
    string dataB = outA.toUtf8().constData();
    std::ostringstream datBNameStream(dataB);
    datBNameStream<<dataB;
    std::string datBName = datBNameStream.str();
    ofstream outB(datBName.c_str());

    QVector<double> mw(dslines1), mi(dslines1), dw(dslines1), di(dslines1);// sw(dslines1), si(dslines1);

    for(int i =0; i<dslines1; i++){
        inA >> eins >>zwei;
        istringstream ist(eins);
        ist >> mw[i];
        x=mw[i];
        istringstream ist2(zwei);
        ist2 >> mi[i];
        /*inB >> eins >>zwei;
        istringstream ist3(eins);
        ist3 >> sw[i];
        istringstream ist4(zwei);
        ist4 >> si[i];*/
        if(ui->checkBox_3->isChecked()){
            di[i]=mi[i]-s(x);
        }
        else{
            if(ui->checkBox_2->isChecked()){
                di[i]=mi[i]/s(x);
            }
            else{
                if(ui->checkBox_5->isChecked()){
                    di[i]=mi[i]+s(x);
                }
                else{
                    //
                }
            }
        }
        dw[i]= mw[i];
        outB<<setprecision(14)<<dw[i]<<"\t"<<di[i]<<endl;
    }

    ui->customPlot->clearGraphs();
    ui->customPlot->addGraph();
    ui->customPlot->graph(0)->setData(dw, di);
    ui->customPlot->graph(0)->rescaleAxes();
    ui->customPlot->replot();

}

//********************************************
// separate difference into single files
//********************************************
void BlazeCorr::on_pushButton_clicked()
{
    string eins, zwei, line;
    int dlines1=0, ndiff=0;

    QString inputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_7->text();
    string dataA = inputA.toUtf8().constData();
    std::ostringstream datANameStream(dataA);
    datANameStream<<dataA;
    std::string datAName = datANameStream.str();

    QFile checkfile1(datAName.c_str());

    if(!checkfile1.exists()){
        qDebug()<<"Error 1: The file "<<checkfile1.fileName()<<" does not exist.";
        QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+checkfile1.fileName()+ " does not exist!");
        this->setCursor(QCursor(Qt::ArrowCursor));
       return;
    }

    ifstream inA(datAName.c_str());

    while(std::getline(inA, line))
       ++dlines1;

    inA.clear();
    inA.seekg(0, ios::beg);

    QVector<double> diffw(dlines1), diffi(dlines1);
    int counter=0;
    double test=0;

    for(int i =0; i<dlines1; i++){

        inA >> eins >> zwei;
        istringstream str(eins);
        istringstream str2(zwei);
        str >> test;

        if(test>1){
            diffw[counter] = test;
            str2 >> diffi[counter];

            if(i>0 & diffw[counter]<diffw[counter-1]){
                ++ndiff;
            }

            else{
                // do nothing
            }
            ++counter;
        }
        else{
            // do nothing
        }

    }

    dlines1=counter;
    counter=0;
    diffw.resize(dlines1);
    diffi.resize(dlines1);

    cout<<"ndiff:"<<ndiff<<endl;

    vector<int> pdiff(ndiff+1);
    pdiff[0]=1;
    counter=0;

        for(int i =0; i<dlines1; i++){
            if(counter==0){
                if(i>0 & diffw[i]<diffw[i-1]){
                    pdiff[counter]=i;
                    cout<<pdiff[counter]<<endl;
                    ++counter;
                }
            }
            else{
                if(i>pdiff[counter-1]-1 & (diffw[i]<diffw[i-1])){
                    pdiff[counter]=i;
                    cout<<pdiff[counter]<<endl;
                    ++counter;
                }
            }
        }
        pdiff[ndiff]=dlines1-1;
        cout<<pdiff[ndiff+1]<<endl;

    for(int i =0; i<=ndiff; i++){
        cout<<i<<"\t"<<pdiff[i]<<endl;
        counter=0;

        QString outputA=ui->lineEdit_2->text()+"/"+ui->lineEdit_10->text();
        QString qExt = ui->lineEdit_11->text();
        string ext = qExt.toUtf8().constData();
        string outA = outputA.toUtf8().constData();
        std::ostringstream outANameStream(outA);
        outANameStream<<outA<<i<<"."<<ext;
        std::string outAName = outANameStream.str();
        ofstream ouA(outAName.c_str());

        for(int n =0; n<pdiff[i]; n++){
            if(i ==0){
                ouA<<diffw[n]<<"\t"<<diffi[n]<<endl;
            }
            else{
                if(n>=pdiff[i-1]){
                    ouA<<setprecision(14)<<diffw[pdiff[i-1]+counter]<<"\t"<<diffi[pdiff[i-1]+counter]<<endl;
                    ++counter;
                }
                else{
                    // no nothing
                }
            }
        }

    }

}

//***********************************
// subtract spline fit from sequence
//***********************************
void BlazeCorr::on_pushButton_2_clicked()
{
    int fmin = ui->spinBox->value();
    int fmax = ui->spinBox_3->value();

    QString qBPath = ui->lineEdit_2->text();
    string bPath = qBPath.toUtf8().constData();

    ui->checkBox_2->setChecked(false);
    ui->checkBox_3->setChecked(false);
    ui->checkBox_5->setChecked(true);
    QString Qin, Qout, Qout2;

    for(int i =fmin; i<=fmax; i++){
        cout<<"File number: "<<i<<endl;

        QString ind=QString::number(i);

        Qin = ui->lineEdit_10->text()+ind+"."+ui->lineEdit_11->text();
        ui->lineEdit->setText(Qin);

        Qout = ui->lineEdit_8->text()+ind+"."+ui->lineEdit_9->text(); // individual spectra

        // fits files
        if(ui->comboBox->currentIndex()==1){

            std::valarray<double> wave;
            std::valarray<double> intens;

                string data = Qout.toUtf8().constData();
                std::ostringstream datNameStream(data);
                datNameStream<<bPath<<"/"<<data;
                std::string datName = datNameStream.str();

                QFile checkfile1(datName.c_str());

                if(!checkfile1.exists()){
                    qDebug()<<"Error 1: The file "<<checkfile1.fileName()<<" does not exist.";
                    QMessageBox::information(this, "Error1 ", "Error 1: Spectrum "+Qout+ " does not exist!");
                    return;
                }

                CCfits::FITS::setVerboseMode(true);

                try
                {
                    qBExt=ui->lineEdit_12->text();
                    bExt = qBExt.toUtf8().constData();
                    qWCol=ui->lineEdit_13->text();
                    wCol = qWCol.toUtf8().constData();
                    qICol=ui->lineEdit_14->text();
                    iCol = qICol.toUtf8().constData();

                    //open file for reading
                    shared_ptr<CCfits::FITS> input_file(new CCfits::FITS(datName.c_str(),CCfits::Read,true));

                    // Create pointer to extension
                        CCfits::ExtHDU& datavector = input_file->extension(bExt);

                      // Read rows
                      CCfits::Column& column = datavector.column(wCol);
                      column.read(wave, 1, column.rows());

                      // Read rows
                      CCfits::Column& column2 = datavector.column(iCol);
                      column2.read(intens, 1, column2.rows());

                      int bini=wave.size();

                      QString Qdata2 = ui->lineEdit_8->text()+ind+".bco";
                      string data2 = Qdata2.toUtf8().constData();
                      std::ostringstream dat2NameStream(data2);
                      dat2NameStream<<bPath<<"/"<<data2;
                      std::string dat2Name = dat2NameStream.str();
                      ofstream bcor(dat2Name.c_str());

                      for(int i=0; i<bini; i++){
                          bcor<<wave[i]<<"\t"<<intens[i]<<endl;
                      }
                      ui->lineEdit_5->setText(Qdata2);

                }
                    catch (CCfits::FitsException&)

                     {
                      std::cerr << " Fits exception thrown by test function \n";
                      }

        }

        // ASCII files
        else{
            ui->lineEdit_5->setText(Qout);
        }

        Qout2 = ui->lineEdit_8->text()+ui->lineEdit_15->text()+"_"+ind+"."+ui->lineEdit_9->text(); // corrected individual spectra
        ui->lineEdit_4->setText(Qout2);

        ui->lineEdit_3->setText("spline_"+ui->lineEdit_15->text()+"_"+ind+".dat");
        ui->lineEdit_6->setText("splinefit_"+ui->lineEdit_15->text()+"_"+ind+".dat");

        BlazeCorr::on_pushButton_5_clicked();
        //QTimer::singleShot(2000, this, SLOT(yourSlot()));
        BlazeCorr::on_pushButton_4_clicked();
        //QTimer::singleShot(2000, this, SLOT(yourSlot()));
        BlazeCorr::on_pushButton_6_clicked();
        //QTimer::singleShot(2000, this, SLOT(yourSlot()));
    }
}

void BlazeCorr::on_checkBox_2_clicked()
{
    if(ui->checkBox_2->isChecked()){
        ui->checkBox_3->setChecked(false);
    }
    else{
        ui->checkBox_3->setChecked(true);
    }
}

void BlazeCorr::on_checkBox_3_clicked()
{
    if(ui->checkBox_3->isChecked()){
        ui->checkBox_2->setChecked(false);
    }
    else{
        ui->checkBox_2->setChecked(true);
    }
}


//
// Created by Han Zhao on 20/02/2017.
//

#include "src/SPNetwork.h"
#include "src/utils.h"
#include "src/StructureLearning.h"
#include "src/BatchParamLearning.h"

#include <fstream>
#include <queue>
#include <random>

#include <boost/program_options.hpp>

namespace po = boost::program_options;

using SPN::SPNetwork;
using SPN::LearnOptSPN;
using SPN::BatchParamLearning;
using SPN::ExpectMax;

int main(int argc, char *argv[]) {
    // Positional program parameters.
    std::string model_filename, training_filename, valid_filename, test_filename, algo_name;
    // Hyperparameters for LearnOptSPN.
    uint seed = 42;
    double eps = 0.01;
    // Building command line parser
    po::options_description desc("Please specify the following options");
    desc.add_options()
            // Positional program parameters.
            ("train", po::value<std::string>(&training_filename), "file path of training data")
            ("valid", po::value<std::string>(&valid_filename), "file path of validation data")
            ("test", po::value<std::string>(&test_filename), "file path of test data")
            ("model", po::value<std::string>(&model_filename), "file path of SPN to be saved")
            ("algo", po::value<std::string>(&algo_name), "batch algorithm")
            // Hyperparameters for structure learning algorithms.
            ("seed", po::value<uint>(&seed), "random seed")
            ("eps", po::value<double>(&eps), "proportion of the uniform distribution during learning");
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);
    if (!vm.count("train") || !vm.count("valid") || !vm.count("test") ||
        !vm.count("model")) {
        std::cout << desc << std::endl;
        return -1;
    }
    // Load training and test data sets
    std::vector<std::vector<double>> training_data = SPN::utils::load_data(training_filename);
    std::vector<std::vector<double>> valid_data = SPN::utils::load_data(valid_filename);
    std::vector<std::vector<double>> test_data = SPN::utils::load_data(test_filename);

    std::cout << "Model path: " << model_filename << std::endl;
    std::cout << "Number of instances in training set = " << training_data.size() << std::endl;
    std::cout << "Number of instances in validation set = " << valid_data.size() << std::endl;
    std::cout << "Number of instances in test set = " << test_data.size() << std::endl;

    size_t num_train = training_data.size(), num_valid = valid_data.size(), num_test = test_data.size();
    if (training_data[0].size() != valid_data[0].size()
        || valid_data[0].size() != test_data[0].size()) {
        std::cerr << "Trainging data, validation data and test data are not consistent in dimension" << std::endl;
        return -1;
    }
    // Construct SPN.
    LearnOptSPN st_learner;
    SPNetwork *spn = st_learner.learn(training_data, valid_data, eps, true);
    spn->init();
    std::cout << "Uniform hyperparameter eps = " << eps << std::endl;
    std::cout << "Network statistics after initialization: " << std::endl;
    std::cout << "Network height: " << spn->height() << std::endl;
    std::cout << "Network size: " << spn->size() << std::endl;
    std::cout << "Network number of nodes: " << spn->num_nodes() << std::endl;
    std::cout << "Network number of edges: " << spn->num_edges() << std::endl;
    std::cout << "Network number of varnodes: " << spn->num_var_nodes() << std::endl;
    std::cout << "Network number of sumnodes: " << spn->num_sum_nodes() << std::endl;
    std::cout << "Network number of prodnodes: " << spn->num_prod_nodes() << std::endl;
    std::cout << "**********************************" << std::endl;
    // Compute test set average log-likelihoods
    std::clock_t t_start = std::clock();
    std::vector<double> test_logps = spn->logprob(test_data);
    std::clock_t t_end = std::clock();
    std::cout << "CPU time = " << 1000.0 * (t_end - t_start) / CLOCKS_PER_SEC << " milliseconds\n";

    double avg_logp = 0.0;
    for (double ll : test_logps) avg_logp += ll;
    avg_logp /= num_test;
    std::cout << "Average test log-likelihoods = " << avg_logp << std::endl;

    BatchParamLearning *param_learner = new ExpectMax(20, 1e-3, 1e-2);
    param_learner->fit(training_data, valid_data, *spn, true);
    test_logps = spn->logprob(test_data);
    avg_logp = 0.0;
    for (double ll : test_logps) avg_logp += ll;
    avg_logp /= num_test;
    std::cout << "Test average log-likelihoods = " << avg_logp << std::endl;
    std::cout << "**********************************************************" << std::endl;

//    std::vector<double> valid_logps = spn->logprob(valid_data);
//    avg_logp = 0.0;
//    for (double ll : valid_logps) avg_logp += ll;
//    avg_logp /= num_valid;
//    std::cout << "Average validation log-likelihood = " << avg_logp << std::endl;
//    std::cout << "-----------------------------------------------" << std::endl;
    if (vm.count("model")) {
        SPN::utils::save(spn, model_filename);
    }
    delete spn;
    return 0;
}