#include <iostream>
#include <armadillo>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <ctime>
#include "helper_functions.h"
#include "solve_network.h"
#include "solve_dde.h"
#include "backprop.h"
#include "numerical_gradient.h"
#include "katana_get_params.hh"
#include "global_constants.h"

using namespace std;
using namespace arma;
using namespace globalconstants;

int main(int argc, char const *argv[])
{
	// for cpu time measuring
	clock_t start_overall = clock();
	double cumulative_solve_time = 0.0;
	double cumulative_backprop_time = 0.0;

	arma_rng::set_seed_random();

	// ### ### ### --- OPTIONS --- ### ### ###

	// file name of the text file where parameters and results will be saved
	string results_file_name = katana::getCmdOption(argv, argv + argc, "-filename", "results.txt");
	// print message to results file
	string print_msg = "Simulation of the deep learning delay system.";

	// task, possible options are "MNIST", "Fashion-MNIST", "CIFAR-10", "SVHN"
	string task = "Fashion-MNIST-denoising";
	// modify global_constants.h accordingly!

	// number of example images to save
	int save_examples = katana::getCmdOption(argv, argv + argc, "-save_examples", 0);

	// print weights (and diagonals) after each training epoch to text files in weights folder
	bool print_weights_to_file = false;

	// record data for time-signal video (or plot), only for MNIST or Fashion-MNIST
	bool record_data = katana::getCmdOption_bool(argv, argv + argc, "-record_data", false);
	int number_of_examples = katana::getCmdOption(argv, argv + argc, "-record_examples", 10);

	// If the following option is true, the program will not train the machine learning system.
	// Instead it will just choose diag indices n_prime according to the given paramters
	// and save them in the text file diag.txt. This will be done 6 times, i.e. the text file
	// will contain 6 lines of diag indices, which can be used for 6-fold cross validation
	bool make_diags = katana::getCmdOption_bool(argv, argv + argc, "-make_diags", false);
	// If the following option is true, the program will create initial weight files and abort.
	bool save_init_weights = katana::getCmdOption_bool(argv, argv + argc, "-save_init_weights", false);
	// If the following option is true, the program will load the initial weights from files instead of creating random weights.
	// Note that the corresponding diag file must be loaded as well.
	bool init_weights_from_file = katana::getCmdOption_bool(argv, argv + argc, "-init_weights_from_file", false);

	// option for system simulation
	// "dde_ibp":	semi-analytic heun method with modified trapezoidal rule employing integration-by-parts
	// "dde_heun":	semi-analytic heun method with standard trapezoidal rule
	// "network":	solves the equivalent network equations
	// "network_decoupled": solves network ignoring the additional direct linear connections of neighboring nodes and layers
	string system_simu = katana::getCmdOption(argv, argv + argc, "-system_simu", "dde_ibp");

	// option for gradient computation
	// backprop_standard: newly derived backpropagation algorithm for the deep learning delay system (was derived for equivalent network)
	// backprop_classic: classical backpropagation algorithm, ignores the additional direct linear connections of neighboring nodes and layers
	string grad_comp = katana::getCmdOption(argv, argv + argc, "-grad_comp", "backprop_standard");

	// gradient_check: compute numerical gradient (for dde_ibp) for comparision
	bool gradient_check = katana::getCmdOption_bool(argv, argv + argc, "-gradient_check", false);
	double epsilon_gradient_check = 1e-9;
	bool print_gradients_to_file = false;

	// ### ### ### --- PARAMETERS --- ### ### ###

	// M and P are defined in global_constants.h

	// task parameter
	double noise_sigma = 1.0;

	// ... for the system/network architectur:

	int N = katana::getCmdOption(argv, argv + argc, "-N", 100);  // number of nodes per hidden layer
	int L = katana::getCmdOption(argv, argv + argc, "-L", 2);  // number of hidden layers
	int D = katana::getCmdOption(argv, argv + argc, "-D", 50);  // number of delays (NOTE: must be the correct number also if diag_method == "from_file")

	double theta = katana::getCmdOption(argv, argv + argc, "-theta", 0.5);  // node separation
	double alpha = -1.0;  // factor in linear part of delay system

	string diag_method = katana::getCmdOption(argv, argv + argc, "-diag_method", "uniform");  // method to choose diagonals
	// "uniform": uniform distribution with the additional condition that there is at least one n'_d < 0 and one n'_d > 0
	// "equi_dist": diagonals with equal distances in between, if D is an odd number: main diagonal and same number of upper and lower,
	//              if D is an even number and diag_distance is even: centered, e.g. -15, -5, 5, 15,
	//              if D is even and diag_distance is odd: centered around -0.5, e.g. -8, -3, 2, 7.
	// "from_file": diag indices are taken from a given text file
	// For a banded matrix as connection matrix choose equi_dist and diag_distance = 1
	// For a full connection matrix choose D = 2 * N - 1 (and either uniform or equi_dist with diag_distance = 1)
	int diag_distance = katana::getCmdOption(argv, argv + argc, "-diag_distance", 0);  // distance between diagonals if diag_method == "equi_dist",
							// minimum distance between diagonals if diag_method is "uniform", otherwise ignored
							// Do not choose diag_distance too large. If (D - 1) * (2 * diag_distance - 1) >= 2 * (N - diag_margin) - 1 and method is uniform, the program will abort.
	string diag_file_path = katana::getCmdOption(argv, argv + argc, "-diag_file_path", "diag.txt"); // path to text file containing D interger numbers n_prime_d, ignored if method is not "from_file"

	// ... for the training:
	int number_of_epochs = katana::getCmdOption(argv, argv + argc, "-number_of_epochs", 100);
	double eta_0 = katana::getCmdOption(argv, argv + argc, "-eta0", 0.001);
	double eta_1 = katana::getCmdOption(argv, argv + argc, "-eta1", 1000.0);  // learning rate eta = min(eta_0, eta_1 / step)
	bool pixel_shift = katana::getCmdOption_bool(argv, argv + argc, "-pixel_shift", false);  // on-off switch for training input random 1-pixel shift
	int max_pixel_shift = katana::getCmdOption(argv, argv + argc, "-max_pixel_shift", 1);
	bool input_noise = katana::getCmdOption_bool(argv, argv + argc, "-training_noise", false);  // on-off switch for training input gaussian noise
	double training_noise_sigma = katana::getCmdOption(argv, argv + argc, "-sigma", 0.01);  // standard deviation of gaussian noise to disturb training input
	bool rotation = katana::getCmdOption_bool(argv, argv + argc, "-rotation", false);
	double max_rotation_degrees = katana::getCmdOption(argv, argv + argc, "-max_rotation_degrees", 15.0);
	bool horizontal_flip = katana::getCmdOption_bool(argv, argv + argc, "-flip", false);  // only for CIFAR-10
	// dropout
	double dropout_rate = katana::getCmdOption(argv, argv + argc, "-dropout", 0.0);

	// ... for weight initialization:
	double initial_input_weigt_radius = sqrt(6.0 / ((double)D/2.0 + (double)M + 1.0));
	double initial_hidden_weigt_radius =  sqrt(6.0 / ((double)D + 1.0));
	double initial_output_weigt_radius =  sqrt(6.0 / ((double)D/2.0 + (double)P + 1.0));

	// ... for numerics:

	int N_h = max(32 ,(int)(16 * theta));  // computation steps per virtual node for solving DDE

	// computational precision for sums with exp(alpha * theta * n) factor in summands
	// in the functions "get_deltas" and "get_gradient" in "backprop.cpp".
	// exp_precision = -35.0 means that terms, where exponential factor
	// is smaller than exp(-35), are ignored.
	// Since exp(-35) is approximately 6.3e-16, the gradient will still be computed with double precision.
	double exp_precision = -35.0;

	// ### ### ### --- ETC. --- ### ### ###

	// make diag text file and end program if make_diags option is true
	if (make_diags){
		ofstream diag_file;
		diag_file.open("diag.txt");
		for (int i = 0; i < 6; ++i){
			vector<int> diag_indices = get_diag_indices(N, D, diag_method, diag_distance, diag_file_path, 0);
			for (int d = 0; d < D - 1; ++d){
				diag_file << diag_indices[d] << " ";
			}
			diag_file << diag_indices[D - 1] << endl;
		}
		diag_file.close();
		cout << "The option make_diags was true." << endl;
		cout << "Program made (or overrode) diag.txt" << endl;
		return 0;
	}

	// make initial weight files and diag file and end program if make_diags save_init_weights option is true
	if (save_init_weights){
		vector<int> diag_indices;
		ofstream diag_file;
		diag_file.open("diag.txt");
		for (int i = 0; i < 6; ++i){
			diag_indices = get_diag_indices(N, D, diag_method, diag_distance, diag_file_path, 0);
			for (int d = 0; d < D - 1; ++d){
				diag_file << diag_indices[d] << " ";
			}
			diag_file << diag_indices[D - 1] << endl;
		}
		diag_file.close();
		cout << "The option save_init_weights was true." << endl;
		cout << "Program made (or overrode) diag.txt" << endl;
		mat input_weights(N, M + 1);
		mat output_weights(P, N + 1);
		cube hidden_weights(L - 1, N, N + 1);
		initialize_weights(input_weights, hidden_weights, output_weights, D, L, N, diag_indices,
						   initial_input_weigt_radius, initial_hidden_weigt_radius, initial_output_weigt_radius, true);
		cout << "Save initial weights and end program because the option save_init_weights is true." << endl;
		return 0;
	}

	// The following lines are to create a look up table "exp_table"
	// which contains the values exp(alpha * theta * n)
	// which are often needed by the functions "get_deltas" and "get_gradient" in "backprop.cpp".
	vector<double> exp_table;
	int n = 0;
	while (alpha * theta * double(n) >= - 35.0){
		double exp_value = exp(alpha * theta * double(n));
		exp_table.push_back(exp_value);
		++n;
	}

	// make results file and print information about parameters
	print_parameters(results_file_name, print_msg, task,
					system_simu, grad_comp, gradient_check,
					N, L, D, theta, alpha,
					diag_method, diag_distance, diag_file_path,
					number_of_epochs, eta_0, eta_1,
					pixel_shift, input_noise, training_noise_sigma,
					N_h, exp_precision);

	// determine data directory
	string data_dir = "data-Fashion-MNIST";

	// read image data from files to arrays:
	cube train_images(number_of_training_batches, training_batch_size, M);
	mat test_images(test_batch_size, M);
	int train_labels[number_of_training_batches][training_batch_size];
	int test_labels[test_batch_size];
	read_files(train_images, test_images, train_labels, test_labels, data_dir);

	// for video:
	vector<int> step_indices_for_video;
	if (record_data){
		fstream index_file;
		string index_string;
		index_file.open("video/step_indices.txt", ios::in);
		do {
			getline(index_file, index_string);
			if (index_string != ""){
				int i = stoi(index_string);
				step_indices_for_video.push_back(i);
			}
		} while (index_string != "");
		index_file.close();
	}

	// ### ### ### --- INITIALIZATION --- ### ### ###

	vector<int> training_batch_indices;
	cout << "Begin training." << endl;
	for (int i = 0; i < number_of_training_batches; ++i){
		training_batch_indices.push_back(i);
	}

	// initialize arrays which are used below to store the current system states
	mat activations(L, N);
	mat node_states(L, N);
	double output_activations[P];
	double outputs[P];
	vec g_primes(N);

	// initialize arrays to store deltas and gradient
	mat deltas(L, N);
	double output_deltas[P];
	mat input_weight_gradient(N, M + 1, fill::zeros);
	mat output_weight_gradient(P, N + 1, fill::zeros);
	cube weight_gradient(L - 1, N, N + 1, fill::zeros);

	mat deltas_0(L, N);
	double output_deltas_0[P];
	mat input_weight_gradient_0(N, M + 1, fill::zeros);
	mat output_weight_gradient_0(P, N + 1, fill::zeros);
	cube weight_gradient_0(L - 1, N, N + 1, fill::zeros);

	// initialize arrays to eventually store numerical gradient
	mat num_input_weight_gradient(N, M + 1, fill::zeros);
	mat num_output_weight_gradient(P, N + 1, fill::zeros);
	cube num_weight_gradient(L - 1, N, N + 1, fill::zeros);

	// The function "get_diag_indices" returns D intergers n'_d between - N + 1 and N - 1.
	// The delays are then tau_d = N - n'_d.
	vector<int> diag_indices;
	diag_indices = get_diag_indices(N, D, diag_method, diag_distance, diag_file_path, 0);

	// initialize weights.
	mat input_weights(N, M + 1, fill::zeros);
	mat output_weights(P, N + 1, fill::zeros);
	cube hidden_weights(L - 1, N, N + 1, fill::zeros);
	if (init_weights_from_file){
		load_initial_weights(input_weights, hidden_weights, output_weights);
	} else {
		initialize_weights(input_weights, hidden_weights, output_weights, D, L, N, diag_indices,
					   initial_input_weigt_radius, initial_hidden_weigt_radius, initial_output_weigt_radius, false);
	}

	// weights for test_runs.
	mat input_weights_scaled(N, M + 1, fill::zeros);
	mat output_weights_scaled(P, N + 1, fill::zeros);
	cube hidden_weights_scaled(L - 1, N, N + 1, fill::zeros);

	// dropout mask for weights:
	mat input_weights_mask(N, M + 1, fill::ones);
	cube hidden_weights_mask(L - 1, N, N + 1, fill::ones);
	mat output_weights_mask(P, N + 1, fill::ones);

	// ### ### ### --- STOCHASTIC GRADIENT DESCENT TRAINING --- ### ### ###

	// vectors to track training accuracy and validition accuracy (and eventually cosine similarity):
	vector<double> training_accuracy_vector;
	vector<double> accuracy_vector;
	vector<double> similarity_vector;

	// vector and variables to measure and save cpu time needed for each epoch
	vector<double> time_vector;
	clock_t start;
	clock_t ende;
	double epoch_time;

	// loop over training epochs:
	for (int epoch = 0; epoch < number_of_epochs; ++epoch){

		start = clock();

		// make vector with randomly shuffled indices between 0 and 59999 for each epoch of the stochastic gradient descent
		vector<int> index_vector;
		for (int index = 0; index < number_of_training_batches*training_batch_size; ++index){
			index_vector.push_back(index);
		}
		shuffle(begin(index_vector), std::end(index_vector), rng);

		// loop over training steps:
		int step_index = 0;
		for (int index : index_vector){
			++step_index;

			// choose random dropout nodes
			input_weights_mask = mat(N, M + 1, fill::ones);
			hidden_weights_mask = cube(L - 1, N, N + 1, fill::ones);
			output_weights_mask = mat(P, N + 1, fill::ones);
			// input layer
			for (int m = 0; m < M; ++m){
				double random_num = uniform(0.0, 1.0);
				if (random_num < dropout_rate){
					for (int n = 0; n < N; ++n){
						input_weights_mask(n, m) = 0.0;
					}
				}
			}
			// first hidden layer
			if (L > 1){
				for (int n = 0; n < N; ++n){
					double random_num = uniform(0.0, 1.0);
					if (random_num < dropout_rate){
						for (int m = 0; m < M + 1; ++m){
							input_weights_mask(n, m) = 0.0;
						}
						for (int j = 0; j < N; ++j){
							hidden_weights_mask(0, j, n) = 0.0;
						}
					}
				}
			}
			// hidden layers except first and last
			for (int l = 1; l < L - 1; ++l){
				for (int n = 0; n < N; ++n){
					double random_num = uniform(0.0, 1.0);
					if (random_num < dropout_rate){
						for (int i = 0; i < N + 1; ++i){
							hidden_weights_mask(l - 1, n, i) = 0.0;
						}
						for (int j = 0; j < N; ++j){
							hidden_weights_mask(l, j, n) = 0.0;
						}
					}
				}
			}
			// last hidden layer
			if (L > 1){
				for (int n = 0; n < N; ++n){
					double random_num = uniform(0.0, 1.0);
					if (random_num < dropout_rate){
						for (int i = 0; i < N + 1; ++i){
							hidden_weights_mask(L - 2, n, i) = 0.0;
						}
						for (int p = 0; p < P; ++p){
							output_weights_mask(p, n) = 0.0;
						}
					}
				}
			}

			// record data for video: step 0
			if (record_data && epoch == 0 && step_index == 1){
				int rec_step = 0;
				cout << "record data after step: " << rec_step << endl;
				//save weights
				string input_file_name = "video/weights_input_step_" + to_string(rec_step) + ".txt";
				string hidden_file_name = "video/weights_hidden_step_" + to_string(rec_step) + ".txt";
				string output_file_name = "video/weights_output_step_" + to_string(rec_step) + ".txt";
				input_weights.save(input_file_name, csv_ascii);
				hidden_weights.save(hidden_file_name, raw_ascii);
				output_weights.save(output_file_name, csv_ascii);
				//run system with examples from validation batch and save input, output and x_states
				for (int example_index = 0; example_index < number_of_examples; ++example_index){
					// select image as input
					vec input_data = test_images.row(example_index).t();
					int label = test_labels[index];
					// run system and record data for x_states
					solve_dde_ibp(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights, hidden_weights, output_weights, diag_indices, theta, alpha,
							  N, L, N_h, example_index+1, rec_step);
					// save input and output
					string input_file_name = "video/vector_input_step_" + to_string(rec_step) + "_example_" + to_string(example_index + 1) + ".txt";
					input_data.save(input_file_name, csv_ascii);
					string output_file_name = "video/vector_output_step_" + to_string(rec_step) + "_example_" + to_string(example_index + 1) + ".txt";
					ofstream output_file;
					output_file.open(output_file_name);
					for (double y_p : outputs){
						output_file << y_p << endl;
					}
					output_file.close();
				}
			}

			double eta = learning_rate(epoch, step_index, eta_0, eta_1);

			// select image as input
			div_t div_result = div(index, training_batch_size);
			int batch_index = training_batch_indices[div_result.quot];
			int image_index = div_result.rem;
			vec input_data = train_images.tube(batch_index, image_index);
			int label = train_labels[batch_index][image_index];

			// data augmentation or regression
			if (rotation && M == 3072){
				double rotation_degrees = uniform(-max_rotation_degrees, max_rotation_degrees);
				rotation32(input_data, rotation_degrees);
			}

			if (pixel_shift && M == 784){
				pixel_shift28(input_data, max_pixel_shift);
			}
			if (pixel_shift && M == 3072){
				pixel_shift32(input_data, max_pixel_shift);
			}
			if (input_noise){
				vec training_noise = training_noise_sigma * vec(M, fill::randn);
				input_data += training_noise;
			}
			if (horizontal_flip && M == 3072){
				double random_val = uniform(0,1);
				if (random_val > 0.5){
					horizontal_flip32(input_data);
				}
			}

			// get target
			double targets[P];
			for (int p = 0; p < P; ++p){
				targets[p] = input_data(p);
			}

			// add noise to input:
			vec noise = noise_sigma * vec(M, fill::randn);
			input_data += noise;
			for (int m = 0; m < M; ++m){
				if (input_data[m] < 0.0){
					input_data[m] = 0.0;
				}
				if (input_data[m] > 1.0){
					input_data[m] = 1.0;
				}
			}

			// solve the DDE (or network)
			clock_t start_solve = clock();
			if (system_simu == "dde_ibp"){
				solve_dde_ibp(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights%input_weights_mask, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "dde_heun"){
				solve_dde_heun(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights%input_weights_mask, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "network"){
				solve_network(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights%input_weights_mask, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, theta, alpha,
							  N, L);
			} else if (system_simu == "network_decoupled"){
				solve_network_decoupled(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights%input_weights_mask, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, N, L);
			} else {
				cout << system_simu << " is not a valid value for the system_simu option." << endl;
				abort();
			}
			clock_t end_solve = clock();
			cumulative_solve_time += (end_solve - start_solve) / (double)CLOCKS_PER_SEC;

			// compute deltas and gradient
			clock_t start_backprop = clock();
			mat f_prime_activations = f_prime_matrix(activations);
			if (grad_comp == "backprop_standard"){
				get_gradient_node_by_node(input_weight_gradient, weight_gradient, output_weight_gradient, input_data, node_states, f_prime_activations, g_primes, outputs, targets, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, diag_indices, N, L, theta, alpha);
			} else if (grad_comp == "backprop_classic"){
				get_gradient_classical_backprop(input_weight_gradient, weight_gradient, output_weight_gradient, input_data, node_states, f_prime_activations, g_primes, outputs, targets, hidden_weights%hidden_weights_mask, output_weights%output_weights_mask, diag_indices, N, L);
			} else {
				cout << grad_comp << " is not a valid value for the grad_comp option." << endl;
				abort();
			}
			clock_t end_backprop = clock();
			cumulative_backprop_time += (end_backprop - start_backprop) / (double)CLOCKS_PER_SEC;
			// eventually compute numerical gradient for comparision and save numerical gradient and backprop gradient in files
			if (gradient_check && step_index == 1){
				if (system_simu != "dde_ibp"){
					cout << "The option to compute the gradient numerically is only available if the method 'dde_ibp' is used to solve the delay system. " << endl;
					cout << "Set the option 'system_simu' to 'dde_ibp' or set the option 'num_gradient' to false." << endl;
					abort();
				}
				get_num_gradient(num_input_weight_gradient, num_weight_gradient, num_output_weight_gradient,
								 activations, node_states, output_activations, outputs, g_primes, label,
								 input_data, input_weights, hidden_weights, output_weights, diag_indices, theta, alpha, N, L, N_h, epsilon_gradient_check);
				if (print_gradients_to_file){
					write_gradients_to_file(input_weight_gradient, weight_gradient, output_weight_gradient, num_input_weight_gradient, num_weight_gradient, num_output_weight_gradient, epoch + 1, step_index, 0);
				}
				double cos_sim = cosine_similarity(input_weight_gradient, weight_gradient, output_weight_gradient, num_input_weight_gradient, num_weight_gradient, num_output_weight_gradient);
				similarity_vector.push_back(cos_sim);
			}

			input_weight_gradient = input_weight_gradient % input_weights_mask;
			weight_gradient = weight_gradient % hidden_weights_mask;
			output_weight_gradient = output_weight_gradient % output_weights_mask;

			// perform weight updates
			input_weights += - eta * input_weight_gradient;
			hidden_weights += - eta * weight_gradient;
			output_weights += - eta * output_weight_gradient;

			// record data for video: step 1 to end
			if (record_data && find(step_indices_for_video.begin(), step_indices_for_video.end(), epoch * 50000 + step_index) != step_indices_for_video.end()){
				int rec_step = epoch * 50000 + step_index;
				cout << "record data after step: " << rec_step << endl;
				//save weights
				string input_file_name = "video/weights_input_step_" + to_string(rec_step) + ".txt";
				string hidden_file_name = "video/weights_hidden_step_" + to_string(rec_step) + ".txt";
				string output_file_name = "video/weights_output_step_" + to_string(rec_step) + ".txt";
				input_weights.save(input_file_name, csv_ascii);
				hidden_weights.save(hidden_file_name, raw_ascii);
				output_weights.save(output_file_name, csv_ascii);
				//run system with examples from validation batch and save input, output and x_states
				for (int example_index = 0; example_index < number_of_examples; ++example_index){
					// select image as input
					vec input_data = test_images.row(example_index).t();
					int label = test_labels[index];
					// run system and record data for x_states
					solve_dde_ibp(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights, hidden_weights, output_weights, diag_indices, theta, alpha,
							  N, L, N_h, example_index+1, rec_step);
					// save input and output
					string input_file_name = "video/vector_input_step_" + to_string(rec_step) + "_example_" + to_string(example_index + 1) + ".txt";
					input_data.save(input_file_name, csv_ascii);
					string output_file_name = "video/vector_output_step_" + to_string(rec_step) + "_example_" + to_string(example_index + 1) + ".txt";
					ofstream output_file;
					output_file.open(output_file_name);
					for (double y_p : outputs){
						output_file << y_p << endl;
					}
					output_file.close();
				}
			}
		}

		//weight scaling
		input_weights_scaled = input_weights / (1.0 - dropout_rate);
		hidden_weights_scaled = hidden_weights / (1.0 - dropout_rate);
		output_weights_scaled = output_weights / (1.0 - dropout_rate);

		// loop to get accuracy on training set:
		double mse_sum = 0.0; ;
		for (int index = 0; index < number_of_training_batches *training_batch_size; ++index){
			//cout << "validation step (on training set)" << index + 1 << endl;

			// select image as input
			div_t div_result = div(index, training_batch_size);
			int batch_index = training_batch_indices[div_result.quot];
			int image_index = div_result.rem;
			vec input_data = train_images.tube(batch_index, image_index);
			int label = train_labels[batch_index][image_index];

			// get target
			double targets[P];
			for (int p = 0; p < P; ++p){
				targets[p] = input_data(p);
			}

			// add noise to input:
			vec noise = noise_sigma * vec(M, fill::randn);
			input_data += noise;
			for (int m = 0; m < M; ++m){
				if (input_data[m] < 0.0){
					input_data[m] = 0.0;
				}
				if (input_data[m] > 1.0){
					input_data[m] = 1.0;
				}
			}

			// solve the DDE (or network)
			clock_t start_solve = clock();
			if (system_simu == "dde_ibp"){
				solve_dde_ibp(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "dde_heun"){
				solve_dde_heun(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "network"){
				solve_network(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, theta, alpha,
							  N, L);
			} else if (system_simu == "network_decoupled"){
				solve_network_decoupled(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, N, L);
			} else {
				cout << system_simu << " is not a valid value for the system_simu parameter." << endl;
				abort();
			}
			clock_t end_solve = clock();
			cumulative_solve_time += (end_solve - start_solve) / (double)CLOCKS_PER_SEC;

			double mse = 0;
			for (int p = 0; p < P; ++p){
				mse += pow(outputs[p] - targets[p], 2.0);
			}
			mse = mse/(double)P;
			mse_sum += mse;
		}
		training_accuracy_vector.push_back(mse_sum / 50000.0);

		// loop for validation:
		mse_sum = 0.0; ;
		for (int index = 0; index < test_batch_size; ++index){

			vec input_data = test_images.row(index).t();
			int label = test_labels[index];

			// get target
			double targets[P];
			for (int p = 0; p < P; ++p){
				targets[p] = input_data(p);
			}

			// add noise to input:
			vec noise = noise_sigma * vec(M, fill::randn);
			input_data += noise;
			for (int m = 0; m < M; ++m){
				if (input_data[m] < 0.0){
					input_data[m] = 0.0;
				}
				if (input_data[m] > 1.0){
					input_data[m] = 1.0;
				}
			}

			// solve the DDE (or network)
			clock_t start_solve = clock();
			if (system_simu == "dde_ibp"){
				solve_dde_ibp(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "dde_heun"){
				solve_dde_heun(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, diag_indices, theta, alpha,
							  N, L, N_h);
			} else if (system_simu == "network"){
				solve_network(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, theta, alpha,
							  N, L);
			} else if (system_simu == "network_decoupled"){
				solve_network_decoupled(activations, node_states, output_activations, outputs, g_primes,
							  input_data, input_weights_scaled, hidden_weights_scaled, output_weights_scaled, N, L);
			} else {
				cout << system_simu << " is not a valid value for the system_simu parameter." << endl;
				abort();
			}
			clock_t end_solve = clock();
			cumulative_solve_time += (end_solve - start_solve) / (double)CLOCKS_PER_SEC;

			// save example images
			if (index < save_examples){
				string original_file_name = "example_results/epoch_" + to_string(epoch + 1) + "_example_" + to_string(index + 1) + "_original.txt";
				string input_file_name = "example_results/epoch_" + to_string(epoch + 1) + "_example_" + to_string(index + 1) + "_input.txt";
				string output_file_name = "example_results/epoch_" + to_string(epoch + 1) + "_example_" + to_string(index + 1) + "_output.txt";
				ofstream original_file;
				original_file.open(original_file_name);
				for (int p = 0; p < P; ++p){
					original_file << targets[p] << endl;
				}
				original_file.close();
				ofstream input_file;
				input_file.open(input_file_name);
				for (int p = 0; p < P; ++p){
					input_file << input_data(p) << endl;
				}
				input_file.close();
				ofstream output_file;
				output_file.open(output_file_name);
				for (int p = 0; p < P; ++p){
					output_file << outputs[p] << endl;
				}
				output_file.close();
			}

			double mse = 0;
			for (int p = 0; p < P; ++p){
				mse += pow(outputs[p] - targets[p], 2.0);
			}
			mse = mse/(double)P;
			mse_sum += mse;
		}
		accuracy_vector.push_back(mse_sum / 10000.0);

		cout << "epoch " << epoch + 1 << ": validation MSE = " << mse_sum / 10000.0 << endl;
		// eventually print weights to file at end of each epoch
		if (print_weights_to_file){
			print_weights(input_weights_scaled, hidden_weights_scaled, output_weights_scaled, diag_indices, 0, epoch);
		}

		ende = clock();
		epoch_time = ((double) (ende - start)) / (double)CLOCKS_PER_SEC;
		time_vector.push_back(epoch_time);
	}

	// print result to file:
	print_results(results_file_name, 0, diag_indices, training_accuracy_vector, accuracy_vector, similarity_vector);

	// for cpu time measuring
	clock_t end_overall = clock();
	double cpu_time_overall = (end_overall - start_overall) / (double)CLOCKS_PER_SEC;
	double cpu_time_residual = cpu_time_overall - cumulative_solve_time - cumulative_backprop_time;
	double cpu_time_solve_percentage = 100.0 * cumulative_solve_time / cpu_time_overall;
	double cpu_time_backprop_percentage = 100.0 * cumulative_backprop_time / cpu_time_overall;
	double cpu_time_residual_percentage = 100.0 * cpu_time_residual / cpu_time_overall;

	// get current time and date and print to results text file
	auto current_clock = chrono::system_clock::now();
	time_t current_time = chrono::system_clock::to_time_t(current_clock);
	ofstream results_file;
	results_file.open(results_file_name, ios_base::app);
	results_file << endl;
	results_file << endl;
	results_file << "total cpu time (in seconds): " << cpu_time_overall << endl;
	results_file << "cumulative cpu time for solving the DDE or network (in seconds): " << cumulative_solve_time << " (" << cpu_time_solve_percentage << "%)" << endl;
	results_file << "cumulative cpu time for backpropagation (in seconds): " << cumulative_backprop_time << " (" << cpu_time_backprop_percentage << "%)" << endl;
	results_file << "residual cpu time (in seconds): " << cpu_time_residual << " (" << cpu_time_residual_percentage << "%)" << endl;
	results_file << endl;
	results_file << "end of simulation: " << ctime(&current_time);
	results_file.close();

	return 0;
}


/*
 * Rody Oldenhuis
 * cosine Science & Computing BV
 * roldenhuis@cosine.nl
 *
 * PhysUnits.hh
 * Created: 31.08.2012 16:20:53 CEST
 */

#ifndef _PHYSUNITS_HH
#define _PHYSUNITS_HH

#include <thread>
#include <boost/date_time/posix_time/posix_time.hpp>

// global helper variables
namespace
{
    union endian_t {
        long l;
        char c[sizeof(long)];
    } __endian__t;
}

/// SI multiplier table
namespace si
{
    const double

    // names          prefixes
    yotta = 1e24,     Y  = yotta,
    zetta = 1e21,     Z  = zetta,
    exa   = 1e18,     E  = exa  ,
    peta  = 1e15,     P  = peta ,
    tera  = 1e12,     T  = tera ,
    giga  = 1e9,      G  = giga ,
    mega  = 1e6,      M  = mega ,
    kilo  = 1e3,      k  = kilo ,
    hecto = 1e2,      h  = hecto,
    deca  = 1e1,      da = deca ,

    deci  = 1e-1,     d  = deci ,
    centi = 1e-2,     c  = centi,
    milli = 1e-3,     m  = milli,
    micro = 1e-6,     mu = micro,
    nano  = 1e-9,     n  = nano ,
    pico  = 1e-12,    p  = pico ,
    femto = 1e-15,    f  = femto,
    atto  = 1e-18,    a  = atto ,
    zepto = 1e-21,    z  = zepto,
    yocto = 1e-24,    y  = yocto;
}

/// Binary prefixes
namespace binary
{
    const double

    // names          prefixes
    kibi = 1.*1024,   ki = kibi,
    mebi = ki*1024,   Mi = mebi,
    gibi = Mi*1024,   Gi = gibi,
    tebi = Gi*1024,   Ti = tebi,
    pebi = Ti*1024,   Pi = pebi,
    exbi = Pi*1024,   Ei = exbi,
    zebi = Ei*1024,   Zi = zebi,
    yobi = Zi*1024,   Yi = yobi;

}

/// Physics constants
namespace physics
{
    const double

    c     = 299792458,       // speed of light [m/s]
    au    = 1.49597870e11,   // unit distance [m]
    KSun  = 1370,            // Solar constant [W/m^2]

    // FIXME: (Rody Oldenhuis) DEPRACATED
    //    Using Solar mass (or masses in general) introduces terrible numerical
    //    inaccuracies everywhere due to current uncertainty in the value of
    //    Newton's G. Use standard gravitational parameters (=GM); these have
    //    up to 6 orders of magnitude better accuracy.
    G     = 6.67384e-11,     // Gravitational constant [m^3/kg/s^2] (CODATA 2010 recommended value)
    MSun  = 1.98855e30,      // Solar mass [kg]
    // -----------------------------------------

    k     = 1.3806488e-23,   // Boltzmann constant [J K^-1]
    sigma = 5.670373e-8,     // Stefan-Boltzmann constant [W m^-2 K^-4]

    // FIXME: (Rody Oldenhuis) Not really a "constant", more a unit...
    // Use class Length for these things.
    A     = 1.0e-10,         // Angstrom
    // -----------------------------------------

    NA    = 6.02214129e+23,  // Avogadro's constant [1/mol]
    h     = 6.62606957e-34,  // Planck's constant [Js]
    hbar  = 1.054571726e-34, // Reduced Plankc's constant, or Dirac's constant [Js]
    re    = 2.817940285e-15, // Classical electron radius [m]
    q     = 1.60217646e-19,  // Proton charge [C]

    // FIXME: (Rody Oldenhuis) DEPRECATED
    //   Only included because full conversion takes too much time.
    //   Find files in need of conversion with "grep 'physics::day' *.cc *.hh"
    //   and convert everything to class Time.
    s        = 1,
    minute   = 60*s,
    hour     = 60*minute,
    day      = 24*hour,
    jyear    = 365.25*day,
    jcentury = 100*jyear;
    // -----------------------------------------

}

/// Mathematical constants
namespace math
{
    const double

    pi         = 3.14159265358979323846264338327950288419716939937510,
    pio2       = 1.57079632679489661923132169163975144209858469968755,
    pio4       = 0.78539816339744830961566084581987572104929234984377,
    pio6       = 0.52359877559829887307710723054658381403286156656251,
    threepio2  = 4.71238898038468985769396507491925432629575409906265,
    twopi      = 6.28318530717958647692528676655900576839433879875020,
    tau        = twopi, // the TRUE circle constant!!
    twotau     = 12.5663706143591729538505735331180115367886775975004,
    oneopi     = 0.31830988618379067153776752674502872406891929148091, // 1/pi
    twoopi     = 0.63661977236758134307553505349005744813783858296182, // 2/pi
    oneosqrtpi = 0.56418958354775628694807945156077258584405062932900, // 1/sqrt(pi)
    twoosqrtpi = 1.12837916709551257389615890312154517168810125865800, // 2/sqrt(pi)

    sqrt2      = 1.41421356237309504880168872420969807856967187537694,
    sqrt3      = 1.73205080756887729352744634150587236694280525381038,
    sqrt1o2    = 0.70710678118654752440084436210484903928483593768847, // sqrt(1/2)
    sqrt1o3    = 0.57735026918962576450914878050195745564760175127012, // sqrt(1/3)

    epsilon    = std::numeric_limits<double>::epsilon(),
    dx         = epsilon,
    eps        = epsilon,
    INF        = std::numeric_limits<double>::infinity(),
    inf        = INF,
    NaN        = std::numeric_limits<double>::quiet_NaN(),
    nan        = NaN;

    // some things needfloat versions of the above
    const float

    epsilonf = std::numeric_limits<float>::epsilon(),
    dxf      = epsilonf,
    epsf     = epsilonf;

    // handy utilities
    // ----------------

    // signum: -1 for anything <0, +1 for anything >0, 0 for anything ==0.
    template <typename T>
    int signum(T in) {
        return (in>0)-(in<0);
    }

    // negative-value-correct modulus
    // NOTE: NOT the same as % or fmod()
    template <typename T>
    T mod(T x, T y) {
        return (0==y) ? x : x-y*std::floor(x/y);
    }

    // FIXME: (Rody Oldenhuis) DEPRECATED
    //   Only included because full conversion takes too much time.
    //   Find files in need of conversion with "grep 'math::deg' *.cc *.hh"
    //   and convert everything to class Angle.
    const double
        rad = 1.0,
        deg = pi/180.0;
    // -----------------------------------------
}

/// Constants specific to the system HIPSIM's being run on
namespace machine
{
    const unsigned int

    // get number of physical cores present in the system
    // platform-independent, > C++11
    numCores = std::thread::hardware_concurrency();

    // This machine's endianness
    const bool

    machine_is_msb = (__endian__t.l = 1, __endian__t.c[sizeof(long)-1] == 1),
    machine_is_lsb = (__endian__t.l = 1, __endian__t.c[sizeof(long)-1] != 1);

    // handy dandy utilities

    template<typename T>
    T change_endianness(T x)
    {
        char *b = (char *)(&x);
        switch (sizeof(T))
        {
            case 1:
                break;

            case 2:
                std::swap(b[0], b[1]);
                break;

            case 4:
                std::swap(b[0], b[3]);
                std::swap(b[1], b[2]);
                break;

            case 8:
                std::swap(b[0], b[7]);
                std::swap(b[1], b[6]);
                std::swap(b[2], b[5]);
                std::swap(b[3], b[4]);
                break;

            default:
                static const bool cannot_swap = true;
                assert(cannot_swap);
        }
        return x;
    }

    template<typename T>
    void write_msb(std::ostream & os, T x) {
        if (machine_is_lsb)
            x = change_endianness(x);
        os.write(reinterpret_cast<char *>(&x), sizeof(T));
    }

    template<typename T>
    void write_lsb(std::ostream & os, T x) {
        if (machine_is_msb)
            x = change_endianness(x);
        os.write(reinterpret_cast<char *>(&x), sizeof(T));
    }

    template<typename T>
    void read_msb(std::istream & is, T & x) {
        is.read(reinterpret_cast<char *>(&x), sizeof(T));
        if (machine_is_lsb)
        x = change_endianness(x);
    }

    template<typename T>
    void read_lsb(std::istream & is, T & x) {
        is.read(reinterpret_cast<char *>(&x), sizeof(T));
        if (machine_is_msb)
        x = change_endianness(x);
    }

}

// base classes for all units
// --------------------------

// fwd declare everything
namespace _ignore{
    class Unit;
    template<typename T>class UnitOperators;
}

// fwd declare everything
class Angle;      class Energy;
class Length;     class Force;
class Area;       class Temperature;
class Volume;     class Mass;
class Time;       class Speed;
class Density;    class Pressure;

// Math operators
template <typename T> T  abs(const _ignore::UnitOperators<T>& t);
template <typename T> T fabs(const _ignore::UnitOperators<T>& t);

// definition of unit base classes
namespace _ignore
{
    // Base class for all units
    class Unit
    {
    protected:
        double value;
        Unit(double value) : value(value) {}

    public:
        virtual ~Unit(){}

        Unit() : value(0.0) {}
        Unit(const Unit& obj) : value(obj.value) {}

        // enforce string representation
        virtual std::string toString() const = 0;

    };

    // RO: use CRTP pattern to have the definition of the operators all in
    // one place, while guaranteeing that only same-type operators work. See
    // http://stackoverflow.com/questions/12742728/
    template <typename T>
    class UnitOperators
        : public Unit
    {
        friend T  abs<T>(const UnitOperators<T>& t);
        friend T fabs<T>(const UnitOperators<T>& t);

    protected:
        UnitOperators(double value) : Unit(value){}

    public:
        virtual ~UnitOperators(){}

        UnitOperators()             : Unit()    {}
        UnitOperators(const T& obj) : Unit(obj) {}

        // RO : use static_cast, and make this class a friend class of all
        // classes subclassing it, as per
        // http://stackoverflow.com/questions/12910883/
        T&     operator= (const T& rhs)       { if (this!=&rhs) value = rhs.value; return static_cast<T&>(*this); }

        bool   operator> (const T& rhs) const { return value >  rhs.value; }
        bool   operator< (const T& rhs) const { return value <  rhs.value; }
        bool   operator>=(const T& rhs) const { return value >= rhs.value; }
        bool   operator<=(const T& rhs) const { return value <= rhs.value; }
        bool   operator==(const T& rhs) const { return value == rhs.value; }
        bool   operator!=(const T& rhs) const { return value != rhs.value; }

        T&     operator+=(const T& rhs)       { value += rhs.value; return static_cast<T&>(*this); }
        T&     operator-=(const T& rhs)       { value -= rhs.value; return static_cast<T&>(*this); }
        T&     operator*=(double F)           { value *= F; return static_cast<T&>(*this); }
        T&     operator/=(double F)           { value /= F; return static_cast<T&>(*this); }

        T      operator+ (const T& rhs) const { return T(*this) += rhs; }
        T      operator- (const T& rhs) const { return T(*this) -= rhs; }
        T      operator- ()             const { return T(-value);}
        T      operator+ ()             const { return T(+value);}

        T      operator* (double F)     const { return T(*this) *= F; }
        T      operator/ (double F)     const { return T(*this) /= F; }
        double operator* (const T& rhs) const { return value*rhs.value; }
        double operator/ (const T& rhs) const { return value/rhs.value; }

        // some nice constant instances
        static const T zero        ;
        static const T dx          ;
        static const T infinitesmal;
        static const T small       ;
        static const T NaN         ;
        static const T nan         ;
        static const T inf         ;
        static const T infinite    ;
        static const T infinity    ;

    };

}

/**
 * @brief Object with units [angle]. Aimed at avoiding confusion between degrees, radians,...
 */

/**
 * Angle
 * avoid confusion between degrees, radians, gradians
 *
 * @see Length @see Time @see Mass @see Temperature @see Energy
 * @see Area @see Force @see Speed
 *
 */

class Angle final
    : public _ignore::UnitOperators<Angle>
{
    typedef _ignore::UnitOperators<Angle> base;
    friend class _ignore::UnitOperators<Angle>;

private:

    // value is always in radians
    explicit Angle(double value) : base(value) {};

public:
   ~Angle(){}

    // default value: 0
    Angle() : base(){};
    // copy constructor
    Angle(const Angle& a2) : base(a2){};
    Angle(const base&  a2) : base(a2){};

    /// Angle is instantiated through named constructors
    static Angle
        radians    (double rad),
        degrees    (double deg),
        arcminutes (double moa),
        arcseconds (double soa),
        GRADIANS   (double grad); // NOTE: capitalized to avoid
                                  // confusion w/ radians
    /// Angle's different return types
    double
        radians    () const,
        degrees    () const,
        arcminutes () const,
        arcseconds () const,
        GRADIANS   () const; // NOTE: capitalized to avoid
                             // confusion w/ radians
    /// alternatively, one can use various pre-defined angles
    static const Angle
        tau ,   _360,
        pi  ,   _180,
        pio2,   _90 ,
        pio4,   _45 ,
        pio6,   _30 ;

    /// wrap value into [-pi +pi) / [-180 +180)
    Angle& wrap_posneg();
    friend Angle wrap_posneg(const Angle& a); // free function

    /// wrap value into [0 2pi) / [0 360)
    Angle& wrap_positive();
    friend Angle wrap_positive(const Angle& a); // free function

    // NOTE: angles have no units -- it's OK for these
    // operators to return Angles
    using _ignore::UnitOperators<Angle>::operator/;
    using _ignore::UnitOperators<Angle>::operator*;
    Angle
        operator/ (const Angle& a2) const,
        operator* (const Angle& a2) const;

    /// human-readable string representation
    std::string toString() const;
};

/**
 * @brief Object with units of Length. Makes metric/imperial less prone to error.
 */

/**
 * Lengths
 * make metric <-> imperial a bit less prone to error
 *
 * @see Angle @see Time @see Mass @see Temperature @see Energy
 * @see Area @see Force @see Speed
 */
class Length final
    : public _ignore::UnitOperators<Length>
{
    typedef _ignore::UnitOperators<Length> base;
    friend class _ignore::UnitOperators<Length>;

private:
    // value is always in [m]
    explicit Length(double value) : base(value) {}

public:
    ~Length(){}

    // default
    Length() : base() {}
    // Copy constructor
    Length (const Length& L2) : base(L2) {}
    Length (const base&   L2) : base(L2) {}

    /// Length is instantiated through named constructors
    static Length
        meters           (double m)  ,     m (double   m),
        kilometers       (double km) ,    km (double  km),
        nanometers       (double nm) ,    nm (double  nm),
        microns          (double mu) ,    um (double  um),
        micrometers      (double mu) ,
        inches           (double in) ,    in (double  in),
        feet             (double ft) ,    ft (double  ft),
        foot             (double ft) ,
        yards            (double yrd),
        miles            (double mi) ,    mi (double  mi),
        nauticalMiles    (double nmi),   nmi (double nmi),
        astronomicalUnits(double au ),    AU (double  au),
        angstroms        (double a)  ,     A (double   a);

    /// Length can return various units of length
    double
        meters            () const,   m() const,
        kilometers        () const,  km() const,
        nanometers        () const,  nm() const,
        microns           () const,  um() const,
        micrometers       () const,
        inches            () const,  in() const,
        feet              () const,  ft() const,
        foot              () const,
        yards             () const,
        miles             () const,  mi() const,
        nauticalMiles     () const, nmi() const,
        astronomicalUnits () const,  AU() const,
        angstroms         () const,   A() const;

    /// alternatively, one can use various constant lengths
    static const Length

        // SI
        one_km, kilometer,          // 1 kilometer
        one_m,  meter,              // 1 meter
        one_mm, millimeter,         // 1 millimeter
        one_um, micrometer, micron, // 1 micron
        one_nm, nanometer,          // 1 nanometer

        // NON-SI
        one_AU, astronomicalUnit,
        inch, one_ft, yard, mile, nauticalMile, angstrom;

    ///cast operators
    operator Energy() const; // wavelength to energy

    /// Human-readable string representation
    std::string toString() const;
};

/**
 * @brief
 */

/**
 * Times
 * make hours, minute, seconds, etc. readable
 *
 * @see Angle @see Length @see Mass @see Temperature @see Energy
 * @see Area @see Force @see Speed
 */
class Time final
    : public _ignore::UnitOperators<Time>
{
    typedef _ignore::UnitOperators<Time> base;
    friend class _ignore::UnitOperators<Time>;

private:
    /// value is always given in seconds
    explicit Time(double value) : _ignore::UnitOperators<Time>(value){}

public:
   ~Time(){}

    // default value
    Time() : base(){}
    // Copy constructor
    Time (const Time& t2) : base(t2){}
    Time (const base& t2) : base(t2){}

    /// Time is instantiated through named constructors
    static Time
        microseconds(double ms),
        milliseconds(double ms),
        seconds     (double s),
        minutes     (double m),
        hours       (double h),
        days        (double d),
        years       (double y),
        centuries   (double c);

    /// Length can return various units of time
    double
        microseconds() const,
        milliseconds() const,
        seconds     () const,
        minutes     () const,
        hours       () const,
        days        () const,
        years       () const,
        centuries   () const;

    /// alternatively, one can use various pre-defined lengths
    static const Time
        microsecond,
        millisecond,
        second,
        minute,
        hour,
        day,
        year,
        century;

    // current unix time (seconds+microseconds)
    static Time now ();
    // boost version
    static boost::posix_time::ptime now(void *);

    /// Human-readable string representation
    std::string toString() const;
};

/**
 * @brief
 */

/**
 * Mass
 * make metric <-> imperial a bit less prone to error
 *
 * @see Angle @see Length @see Temperature @see Energy
 * @see Area @see Force @see Speed @see Time
 */
class Mass final
    : public _ignore::UnitOperators<Mass>
{
    typedef _ignore::UnitOperators<Mass> base;
    friend class _ignore::UnitOperators<Mass>;

private:

    // value is always in kg
    explicit Mass(double value) : base(value){}

public:
    ~Mass(){}

    // default value
    Mass() : base(){}
    // Copy constructor
    Mass (const Mass& m2) : base(m2){}
    Mass (const base& m2) : base(m2){}

    // nice named constructors
    static Mass
        kilogrammes (double kg ),  kg  (double kg ),  kilograms (double kg ),
        pounds      (double lbs),  lbs (double lbs),
        tonnes      (double T  ),
        longTonnes  (double LT ),
        shortTonnes (double sT ),
        stones      (double st ),
        ounces      (double st );

    // various return values
    double
        kilogrammes() const,    kg  () const,  kilograms () const,
        pounds     () const,    lbs () const,
        tonnes     () const,
        longTonnes () const,
        shortTonnes() const,
        stones     () const,
        ounces     () const;

    // constants
    static const Mass
        kilo,
        pound,
        tonne,
        longTonne,
        shortTonne,
        stone,
        ounce;

    // Human-readable string representation
    std::string toString() const;
};

/**
 * @brief
 */

/**
 * Temperature
 *
 * @see Angle @see Length @see Mass @see Energy
 * @see Area @see Force @see Speed @see Time
 */
class Temperature final
    : public _ignore::UnitOperators<Temperature>
{
    typedef _ignore::UnitOperators<Temperature> base;
    friend class _ignore::UnitOperators<Temperature>;

private:

    // value is always in K
    // NOTE: ensure temperature is POSITIVE
    explicit Temperature(double value) : base(value<0.0?0.0:value) {}

public:
   ~Temperature(){}

    // default value
    Temperature() : base(){}
    // copy constructor
    Temperature (const Temperature& T2) : base(T2){}
    Temperature (const base&        T2) : base(T2){}

    // nice named constructors
    static Temperature
        kelvin     (double K),
        celsius    (double C),
        fahrenheit (double F);

    // various return values
    double
        kelvin     () const,
        celsius    () const,
        fahrenheit () const;

    // constants
    static const Temperature
        K, // ZERO kelvin
        C, // ZERO celcius
        F; // ZERO fahrenheit

    // NOTE: negative temperatures do not exist; exclude unary minus operator
    Temperature& operator-();

    // cast operators
    operator Energy() const;

    // Human-readable string representation
    std::string toString() const;
};

/// Combined unit classes / operations

/// Area = Length * Length
class Area final
    : public _ignore::UnitOperators<Area>
{
    typedef _ignore::UnitOperators<Area> base;
    friend class _ignore::UnitOperators<Area>;

private:
    // value is always in m²
    explicit Area (double value) : base(value){}

public:
   ~Area(){}

    // default value
    Area() : base(){}
    // copy constructor
    Area (const Area& E2) : base(E2){}
    Area (const base& E2) : base(E2){}

    // nice named constructors
    static Area
        squareMeters     (double m2  ),    m2 (double m2),
        squareKilometers (double m2  ),   km2 (double km2),
        squareMillimeters(double m2  ),   mm2 (double mm2),
        ares             (double are ),
        acres            (double acre),
        hectares         (double ha  );

    // various return values
    double
        squareMeters     () const,    m2 () const,
        squareKilometers () const,   km2 () const,
        squareMillimeters() const,   mm2 () const,
        ares             () const,
        acres            () const,
        hectares         () const;

    // constants
    const static Area
        squareMeter,
        squareKilometer,
        squareMillimeter,
        are,
        acre,
        hectare;

    // Human-readable string representation
    std::string toString() const;

};

/// Volume = Length * Length * Length
class Volume final
    : public _ignore::UnitOperators<Volume>
{
    typedef _ignore::UnitOperators<Volume> base;
    friend class _ignore::UnitOperators<Volume>;

private:
    // value is always in m³
    explicit Volume (double value) : base(value){}

public:
   ~Volume(){}

    // default value
    Volume() : base(){}
    // copy constructors
    Volume (const Volume& V2) : base(V2){}
    Volume (const base&   V2) : base(V2){}

    // nice named constructors
    static Volume
        cubicKilometers (double km3  ),
        cubicMeters     (double  m3  ),
        cubicCentimeters(double cm3  ),
        cubicMillimeters(double mm3  ),
        cubicFeet       (double ft3  ),
        cubicInches     (double in3  ),
        litres          (double l    ),
        gallons         (double g    ), // imperial gallon
        USgallons       (double g    ); // US gallon

    // various return values
    double
        cubicKilometers  () const,
        cubicMeters      () const,
        cubicCentimeters () const,
        cubicMillimeters () const,
        cubicFeet        () const,
        cubicInches      () const,
        litres           () const,
        gallons          () const,
        USgallons        () const;

    // constants
    const static Volume
        cubicKilometer,    km3,
        cubicMeter,         m3,
        cubicCentimeter,   cm3,
        cubicMillimeter,   mm3,
        cubicFoot,         ft3,
        cubicInch,         in3,
        litre,               l,
        gallon,              g,
        USgallon,           Ug;

    // Human-readable string representation
    std::string toString() const;

};

/// Energy = Mass * Length / Time
class Energy final
    : public _ignore::UnitOperators<Energy>
{
    typedef _ignore::UnitOperators<Energy> base;
    friend class _ignore::UnitOperators<Energy>;

private:
    // value is always in Joules
    explicit Energy (double value) : base(value){}

public:
   ~Energy(){}

    // default value
    Energy() : base(){}
    // copy constructor
    Energy (const Energy& E2) : base(E2){}
    Energy (const base&   E2) : base(E2){}

    // nice named constructors
    static Energy
        joules        (double J),
        electronVolts (double eV);

    // various return values
    double
        joules       () const,
        electronVolts() const;

    // handy constants
    static const Energy
        joule       ,   J,
        electronVolt,   eV;

    // cast operators
    operator Length()       const; // to wavelength
    operator Temperature () const; // to temperature

    // Human-readable string representation
    std::string toString() const;
};

/// Force = Mass * Length / Time / Time
class Force final
    : public _ignore::UnitOperators<Force>
{
    typedef _ignore::UnitOperators<Force> base;
    friend class _ignore::UnitOperators<Force>;

private:

    // value is always in Newtons
    explicit Force(double value) : base(value){}

public:
   ~Force(){}

    // default value
    Force() : base(){}
    // copy constructor
    Force (const Force& F2) : base(F2){}
    Force (const base&  F2) : base(F2){}

    // nice named constructors
    static Force
        newtons (double N),  N (double n);

    // various return values
    double
        newtons() const,   N () const;

    // constants
    static const Force
        newton;

    /// Human-readable string representation
    std::string toString() const;
};

/// Speed = Length / Time
class Speed final
    : public _ignore::UnitOperators<Speed>
{
    typedef _ignore::UnitOperators<Speed> base;
    friend class _ignore::UnitOperators<Speed>;

private:

    // value is always given in m/s
    explicit Speed (double value) : base(value){}

public:
    ~Speed(){}

    // default value
    Speed() : base(){}
    // copy  constructor
    Speed (const Speed& s2) : base(s2){}
    Speed (const base&  s2) : base(s2){}

    // nice named constructors
    static Speed
        metersPerSecond     (double mps),    mps  (double mps),
        knots               (double knt),    kt   (double knt),
        kilometersPerHour   (double kph),    kph  (double kph),
        kilometersPerSecond (double kps),    kps  (double kps),
        milesPerHour        (double mph),    miph (double mph),
        milesPerSecond      (double mps),    mips (double mps);

    // various return values
    double
        metersPerSecond     () const,    mps  () const,
        knots               () const,    kt   () const,
        kilometersPerHour   () const,    kph  () const,
        kilometersPerSecond () const,    kps  () const,
        milesPerHour        () const,    miph () const,
        milesPerSecond      () const,    mips () const;

    // constants
    static const Speed
        meterPerSecond    ,
        knot              ,
        kilometerPerHour  ,
        kilometerPerSecond,
        milePerHour       ,
        milePerSecond     ,

        of_light          , c;

    // NOTE: in principle, speed is a magnitude and thus always positive.
    // However, negative speeds are used, so include unary minus operator.

    // Human-readable string representation
    std::string toString() const;
};

/// Pressure = Force / Area
class Pressure final
    : public _ignore::UnitOperators<Pressure>
{
    typedef _ignore::UnitOperators<Pressure> base;
    friend class _ignore::UnitOperators<Pressure>;

private:

     // value is always given in N/m²
    explicit Pressure (double value) : base(value){}

public:
    ~Pressure(){}

    // default value
    Pressure() : base(){}
    // copy  constructors
    Pressure (const Pressure& p2) : base(p2){}
    Pressure (const base&     p2) : base(p2){}

    // nice named constructors
    static Pressure
        pascals     (double Pa ),    Pa (double Pa ),
        kiloPascals (double kPa),   kPa (double kPa),
        bars        (double bar),
        atmospheres (double atm),   atm(double atm);

    // various return values

    // constants

    /// Human-readable string representation
    std::string toString() const;
};

/// Density = Mass / Volume
class Density final
    : public _ignore::UnitOperators<Density>
{
    typedef _ignore::UnitOperators<Density> base;
    friend class _ignore::UnitOperators<Density>;

private:

     // value is always given in kg/m³
    explicit Density (double value) : base(value){}

public:
    ~Density(){}

    // default value
    Density() : base(){}
    // copy  constructors
    Density (const Density& d2) : base(d2){}
    Density (const base&    d2) : base(d2){}

    // nice named constructors
    static Density
        kilogramsPerCubicMeter  (double kgm3),
        gramsPerCubicCentimeter (double gcm3),
        tonnesPerCubicMeter     (double Mgm3),
        kilogramsPerLiter       (double kgl ),
        poundsPerCubicInch      (double lbscuin),
        poundsPerCubicFoot      (double lbscuft);

    // various return values
    double
        kilogramsPerCubicMeter  (),
        gramsPerCubicCentimeter (),
        tonnesPerCubicMeter     (),
        kilogramsPerLiter       (),
        poundsPerCubicInch      (),
        poundsPerCubicFoot      ();

    // constants
    static const Density
        kgm3,    kilogramPerCubicMeter,
        gcm3,    gramPerCubicCentimeter,
        Mgm3,    tonPerCubicMeter,         Tm3,
        kgl ,    kilogramPerLiter,
        lbscuin, poundPerCubicInch,        lbsin,
        lbscuft, poundPerCubicFoot,        lbsft;

    /// Human-readable string representation
    std::string toString() const;

};

/// overload various relevant math operators

// Angle
// ---------------------------------

// wrap value into [-pi +pi) / [-180 +180)
Angle wrap_posneg(const Angle& a);

// wrap value into [0 2pi) / [0 360)
Angle wrap_positive(const Angle& a);

// regular trig functions
double sin  (const Angle& a);
double cos  (const Angle& a);
double tan  (const Angle& a);
// FIXME: namespace conflict:
//double csc  (const Angle& a);
double sec  (const Angle& a);
double cot  (const Angle& a);

// hyperbolic trig functions
double sinh (const Angle& a);
double cosh (const Angle& a);
double tanh (const Angle& a);
double csch (const Angle& a);
double sech (const Angle& a);
double coth (const Angle& a);

// Inverse trig functions need to be overloaded by return type,
// which is "obviously" not possible. Therefore, define a few
// leaf helper classes:

namespace _ignore
{
    class Invtrig : public UnitOperators<Angle> {
    protected:
        double value;
    public:
        operator double();
        operator Angle ();
        std::string toString() const; // NOTE: leave unimplemented
    };
}

// regular inverse functions
class Asin   final : public _ignore::Invtrig { public: Asin (double value); };
class Acos   final : public _ignore::Invtrig { public: Acos (double value); };
class Atan   final : public _ignore::Invtrig { public: Atan (double value); };
class Atan2  final : public _ignore::Invtrig { public: Atan2(double y, double x); };
class Acsc   final : public _ignore::Invtrig { public: Acsc (double value); };
class Asec   final : public _ignore::Invtrig { public: Asec (double value); };
class Acot   final : public _ignore::Invtrig { public: Acot (double value); };

// hyperbolic inverse functions
class Asinh  final : public _ignore::Invtrig { public: Asinh(double value); };
class Acosh  final : public _ignore::Invtrig { public: Acosh(double value); };
class Atanh  final : public _ignore::Invtrig { public: Atanh(double value); };
class Acsch  final : public _ignore::Invtrig { public: Acsch(double value); };
class Asech  final : public _ignore::Invtrig { public: Asech(double value); };
class Acoth  final : public _ignore::Invtrig { public: Acoth(double value); };

/// Make  std::cout << {class}  work
std::ostream& operator<<(std::ostream& target, const _ignore::Unit& U);

/// Make some cross-unit transformations valid

// sqrt(Area) = Length
Length sqrt(const Area& A);

// Length * Length = Area
Area   operator*(const Length& L1, const Length& L2);

// Length * Area = Volume
Volume operator*(const Length&  L, const Area&   A);
Volume operator*(const Area&    A, const Length& L);

// Mass / Volume = Density
Density operator/(const Mass&   M, const Volume& V);

// Length / Time = Speed
Speed operator/ (const Length&  L, const Time&   t);

// Speed * Time = Length
Length operator*(const Speed&   s, const Time&   t);
Length operator*(const Time&    t, const Speed&  s);

// Length * Angle = (arc)Length
Length operator*(const Length&  L, const Angle&  a);
Length operator*(const Angle&   a, const Length& L);

// Force * Length = Energy
Energy operator*(const Force&   F, const Length& L);
Energy operator*(const Length&  L, const Force&  F);

// Force / Area = Pressure
Pressure operator/(const Force& F, const Area&   A);

// Handy dandy physics utilities
// (defined HERE because otherwise the unit classes are incomplete)
namespace physics
{
    // FIXME: (Rody Oldenhuis) DEPRECATED
    // replaced by operator Length() in class Energy
    inline double energy2wavelength(const double e) {
        BOOST_ASSERT(e > 0);
        return h*c/e;
    }
    inline Length energy2wavelength(const Energy& E){
        BOOST_ASSERT(E > Energy::zero);
        return Length::meters(h*c/E.joules());
    }
    // -----------------------------------------

    // FIXME: (Rody Oldenhuis) DEPRECATED
    // replaced by operator Energy() in class Length
    inline double wavelength2energy(const double w) {
        BOOST_ASSERT(w > 0);
        return h*c/w;
    }
    inline Energy wavelength2energy(const Length& w) {
        BOOST_ASSERT(w > Length::zero);
        return Energy::joules(h*c/w.meters());
    }
    // -----------------------------------------

    // FIXME: (Rody Oldenhuis) DEPRECATED
    // replaced by operator Temperature() in class Energy
    inline double energy2temperature(const double e) {
        BOOST_ASSERT(e >= 0);
        return e/k;
    }
    inline Temperature energy2temperature(const Energy& E) {
        BOOST_ASSERT(E >= Energy::zero);
        return Temperature::kelvin(E.joules()/k);
    }
    // -----------------------------------------

    // FIXME: (Rody Oldenhuis) DEPRECATED
    // replaced by operator Energy() in class Temperature
    inline double temperature2energy(const double T) {
        BOOST_ASSERT(T >= 0);
        return T*k;
    }
    inline Energy temperature2energy(const Temperature& T) {
        BOOST_ASSERT(T >= Temperature::zero);
        return Energy::joules(T.kelvin()*k);
    }
    // -----------------------------------------
}

#endif


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2016 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifdef Cygwin
// availability of std::to_string
#define _GLIBCXX_USE_C99 1
#endif

#include <iostream>

#include <boost/timer/timer.hpp>

#include "dune/grid/config.h"
#include "dune/grid/uggrid.hh"

#include "fem/gridmanager.hh"
#include "fem/lagrangespace.hh"
//#include "fem/hierarchicspace.hh"
#include "utilities/kaskopt.hh"
#include "utilities/gridGeneration.hh" //  createUnitSquare

using namespace Kaskade;

#include "integrate-navierStokes.hh"
//#include "integrate-Limex.hh"

#include "navierStokes.hh"

struct Initial_1Value
{
  using Scalar = double;
  static int const components = 1;
  using ValueType = Dune::FieldVector<Scalar,components>;

  Initial_1Value(int c): component(c) {}

  template <class Cell> int order(Cell const&) const { return std::numeric_limits<int>::max(); }
  template <class Cell>
  ValueType value(Cell const& cell,
                  Dune::FieldVector<typename Cell::Geometry::ctype,Cell::dimension> const& localCoordinate) const
  {
    Dune::FieldVector<typename Cell::Geometry::ctype,Cell::Geometry::coorddimension> x = cell.geometry().global(localCoordinate);

    if (component==0)               // vector u = (u_1,u_2)
    {
      std::cout << "Error: wrong call of Initial_1Value" << std::endl;
      return 0;
    }
    else if (component==1)          // scalar p
    {
      return 0;
    }
    else
      assert("wrong index!\n"==0);

    return 0;
  }

private:
  int component;
};

struct Initial_2Value
{
  using Scalar = double;
  static int const components = 2;
  using ValueType = Dune::FieldVector<Scalar,components>;

  Initial_2Value(int c): component(c) {}

  template <class Cell> int order(Cell const&) const { return std::numeric_limits<int>::max(); }
  template <class Cell>
  ValueType value(Cell const& cell,
                  Dune::FieldVector<typename Cell::Geometry::ctype,Cell::dimension> const& localCoordinate) const
  {
    Dune::FieldVector<typename Cell::Geometry::ctype,Cell::Geometry::coorddimension> x = cell.geometry().global(localCoordinate);

    ValueType result(0);
    if (component==0)                    // vector u = (u_1,u_2), concrete
    {
      return result;
    }
    else
      assert("wrong index!\n"==0);

    return result;
  }

private:
  int component;
};

int main(int argc, char *argv[])
{
  using namespace boost::fusion;

  int verbosityOpt = 1;  // print to console if arguments are changed
  bool dump = false; // do not write properties into file
  std::unique_ptr<boost::property_tree::ptree> pt = getKaskadeOptions(argc, argv, verbosityOpt, dump);

  std::cout << "Start Navier-Stokes tutorial programm." << std::endl;

  boost::timer::cpu_timer totalTimer;

  constexpr int dim = 2;

  constexpr int uIdx  = 0;
  constexpr int pIdx  = 1;

  constexpr double rho    = 1.0;
  constexpr double lambda = 0.0;
  constexpr double mu     = 0.01;   // ----> Reynold number Re = 100

  using Grid = Dune::UGGrid<dim>;
  using H1Space = FEFunctionSpace<ContinuousLagrangeMapper<double,Grid::LeafGridView> >;
  //using H1Space = FEFunctionSpace<ContinuousHierarchicMapper<double,Grid::LeafGridView> >;
  using Spaces = vector<H1Space const*,H1Space const*>;
  using VariableDescriptions = vector<Variable<SpaceIndex<1>,Components<2>,VariableId<uIdx> >,
                                      Variable<SpaceIndex<0>,Components<1>,VariableId<pIdx> > >;
  using VariableSet = VariableSetDescription<Spaces,VariableDescriptions>;
  using CoefficientVectors = VariableSet::CoefficientVectorRepresentation<>::type;
  using Functional = NavierStokesFunctional<double,VariableSet>;

  constexpr int nvars = Functional::AnsatzVars::noOfVariables;
  constexpr int neq   = Functional::TestVars::noOfVariables;

  std::cout << std::endl;
  std::cout << "density rho     = "  << rho    << std::endl;
  std::cout << "viscosity mu    = "  << mu     << std::endl;

  double dt, dtMax, tStart, tEnd, rTolT, aTolT, rTolX, aTolX, writeInterval;

  tStart = 0.0;

  // command line parameters
  int verbosity = getParameter(pt, "verbosity", 1);
  int extrapolOrder = getParameter(pt, "extrapolOrder", 0);
  int maxTimeSteps  = getParameter(pt, "maxTimeSteps", 51);

  int refinements = getParameter(pt, "refinements", 6);
  int order = getParameter(pt, "order", 2); // order for velocity space, order for pressure space is (order-1)
  std::string empty;

  std::string s("names.type.");
  s += getParameter(pt, "solver.type", empty);
  bool direct = getParameter(pt, s, 0);

  s = "names.direct." + getParameter(pt, "solver.direct", empty);
  DirectType directType = static_cast<DirectType>(getParameter(pt, s, 4));  // 4: DirectType::UMFPACK3264

  std::cout << std::endl;
  std::cout << "order of extrapolation in Limex : " << extrapolOrder << std::endl;
  std::cout << "original mesh shall be refined  : " << refinements << " times" << std::endl;
  std::cout << "discretization order in space   : " << order << std::endl;
  //std::cout << "direct solver                   : " << directType << std::endl;
  std::cout << std::endl;

  boost::timer::cpu_timer gridTimer;
  // grid generation
  Dune::FieldVector<double,dim> x0(0.0), length(1.0);
  //GridManager<Grid> gridManager( createRectangle<Grid>(x0,length,0.5));   // mesh 2
  GridManager<Grid> gridManager( createRectangle<Grid>(x0,length,1.0));     // mesh 1
  gridManager.globalRefine(refinements);

  std::cout << "Initial grid: trs=" << gridManager.grid().size(0)
            << " eds=" << gridManager.grid().size(1) << " pts=" << gridManager.grid().size(2)
            << std::endl << std::endl;
  //std::cout << "computing time for generation of initial mesh: " << boost::timer::format(gridTimer.elapsed());

  // construct involved spaces.

  H1Space pressureSpace(gridManager,gridManager.grid().leafGridView(),order-1);
  H1Space velocitySpace(gridManager,gridManager.grid().leafGridView(),order);

  Spaces spaces(&pressureSpace,&velocitySpace);

  // construct variable list.
  // concrete 0: u = (u_1,u_2), velocity
  //          1: p              pressure
  std::string varNames[2] = { "u", "p" };

  VariableSet variableSet(spaces,varNames);

  // construct variational functional.
  Functional F(tStart,rho,lambda,mu);

  VariableSet::VariableSet x(variableSet);
  VariableSet::VariableSet dx(variableSet);

  F.time(tStart);
  x = 0;
  F.scaleInitialValue<uIdx>(Initial_2Value(uIdx),x);      // u
  F.scaleInitialValue<pIdx>(Initial_1Value(pIdx),x);      // p

  std::cout << "nvars = " << nvars << std::endl;
  std::cout << "  neq = " << neq   << std::endl;

  tStart = 0.0;
  tEnd   = 800.0;
  dt     = 2e-2;
  dtMax  = 2e-2;

  std::cout << "start time = "   << tStart       << std::endl;
  std::cout << "final time = "   << tEnd         << std::endl;
  std::cout << "step size = "    << dt           << std::endl;
  std::cout << "maxTimeSteps = " << maxTimeSteps << std::endl;
  std::cout << std::endl;

  rTolT = 1.0e+30;		//2e-5;		//1e-5/8.0;
  aTolT = 1.0e+30;		//2e-5;		//1e-5/8.0;
  rTolX = 1.0e+12;		//1e-4;   1e-5*2.0;
  aTolX = 1.0e+12;		//1e-4;   1e-5*2.0;

  std::cout << "aTolT = " << aTolT << std::endl;
  std::cout << "rTolT = " << rTolT << std::endl;
  std::cout << "aTolX = " << aTolX << std::endl;
  std::cout << "rTolX = " << rTolX << std::endl;
  std::cout << std::endl;

  std::vector<VariableSet::VariableSet> solutions;

  x = integrate(gridManager,F,variableSet,spaces,
                dt,dtMax,tEnd,maxTimeSteps,rTolT,aTolT,rTolX,aTolX,extrapolOrder,
                std::back_inserter(solutions),writeInterval,x,DirectType::MUMPS,
                verbosity);

  //writeVTKFile(gridManager.grid().leafGridView(),x,"navierStokes", IoOptions(), order);
  //std::cout << "graphical output finished, data in VTK format is written into file stokes.vtu \n";

  std::cout << "total computing time: " << boost::timer::format(totalTimer.elapsed()) << "\n";
  std::cout << "End Navier-Stokes tutorial program" << std::endl;
}


// Copyright (c) 2018 Steven Watanabe
//
// Distributed under the Boost Software License Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS2_DIMENSIONS_HPP_INCLUDED
#define BOOST_UNITS2_DIMENSIONS_HPP_INCLUDED

#include <boost/units2/unit.hpp>
#include <boost/units2/def.hpp>
#include <boost/units2/dimensions.hpp>
#include <ratio>

namespace boost {
namespace units2 {
namespace si {

BOOST_UNITS2_DEF(meter,length);
BOOST_UNITS2_DEF(gram,mass);
BOOST_UNITS2_DEF(second,time);
BOOST_UNITS2_DEF(kelvin,temperature);
BOOST_UNITS2_DEF(mole,amount);
BOOST_UNITS2_DEF(ampere,current);
BOOST_UNITS2_DEF(candela,luminous_intensity);

// Extra units that are techically dimensionless
BOOST_UNITS2_DEF(radian,angle);
BOOST_UNITS2_DEF(steradian,solid_angle);

// kilograms are actually the base unit, but for the sake
// of naming consistency, we're defining it this way, since
// it doesn't change the behavior significantly.
inline constexpr const auto kilogram = std::kilo() * gram;

inline constexpr const auto hertz = pow<-1>(second);
inline constexpr const auto newton = meter*kilogram/pow<2>(second);
inline constexpr const auto pascal = newton/pow<2>(meter);
inline constexpr const auto joule = newton*meter;
inline constexpr const auto watt = joule/second;
inline constexpr const auto couloumb = second * ampere;
inline constexpr const auto volt = watt/ampere;
inline constexpr const auto farad = couloumb/volt;
inline constexpr const auto ohm = volt/ampere;
inline constexpr const auto siemens = ampere/volt;
inline constexpr const auto weber = volt*second;
inline constexpr const auto tesla = weber/pow<2>(meter);
inline constexpr const auto henry = weber/ampere;
// celsius = kelvin + std::ratio<27315,100>
inline constexpr const auto lumen = candela*steradian;
inline constexpr const auto lux = lumen/pow<2>(meter);
inline constexpr const auto becquerel = pow<-1>(second);
inline constexpr const auto gray = joule/kilogram;
inline constexpr const auto sievert = joule/kilogram;
inline constexpr const auto katal = mole/second;

}
}
}

#endif


/* Copyright (c) 2016 - 2020, the adamantine authors.
 *
 * This file is subject to the Modified BSD License and may not be distributed
 * without copyright and license information. Please refer to the file LICENSE
 * for the text and further information on this license.
 */

#ifndef ELECTRON_BEAM_HH
#define ELECTRON_BEAM_HH

#include <deal.II/base/function_parser.h>

#include <boost/property_tree/ptree.hpp>

namespace adamantine
{
/**
 * This structure stores all the physical properties necessary to define an
 * electron beam.
 */
struct BeamProperty
{
public:
  /**
   * Absolute penetration of the electron beam into the material where 99% of
   * the beam energy is absorbed.
   */
  double depth;
  /**
   * Energy conversion efficiency on the surface.
   */
  double energy_conversion_eff;
  /**
   * Efficiency of beam control.
   */
  double control_eff;
  /**
   * Square of the beam diameter.
   */
  double diameter_squared;
  /**
   * Maximum power of the beam.
   */
  double max_power;
};

/**
 * This class describes the evolution of an electron beam source.
 */
template <int dim>
class ElectronBeam : public dealii::Function<dim>
{
public:
  /**
   * Constructor.
   * \param[in] database requires the following entries:
   *   - energy_conversion_efficiency: double in \f$[0,1]\f$
   *   - control_efficiency: double in \f$[0,1]\f$
   *   - depth: double in \f$[0,\infty)\f$
   *   - diameter: double in \f$[0,\infty)\f$
   *   - max_power: double in \f$[0, \infty)\f$ [optional: if not
   *   defined, current and voltage need to be defined]
   *   - current: double in \f$[0, \infty)\f$ [optional: if defined
   *   voltage should be defined too, if not defined max_power
   *   should be defined]
   *   - voltage: double in \f$[0,\infty)\f$ [optional: if defined
   *   current should be defined too, if not defined max_power
   *   should be defined]
   *   - input_file: name of the csv file that contains the successive
   *   position of the electron beam [optional: if not defined then
   *   abscissa and, in 3D, ordinate need to be defined]
   *   - delimiter: delimiting character used in input_file
   *   [required if input_file is defined]
   *   - abscissa: string, abscissa of the beam as a function of time
   *   (e.g. "(t-1) * (t-2)") [optional: need to be defined if input_file
   *   is not defined]
   *   - ordinate: string, ordinate of the beam as a function of time
   *   [required only for three dimensional calculation and if input_file
   *   is not defined]
   */
  ElectronBeam(boost::property_tree::ptree const &database);

  /**
   * Set the maximum height of the domain. This is the height at which the
   * electron beam penetrate the material.
   */
  void set_max_height(double height);

  /**
   * Compute the heat source at a given point at the current time.
   */
  double value(dealii::Point<dim> const &point,
               unsigned int const component = 0) const override;

  /**
   * Reset the current time and the position to the last saved state.
   */
  void rewind_time();

  /**
   * Save the current time and the position in the list of successive positions
   * of the beam.
   */
  void save_time();

private:
  /**
   * Flag is true if the beam is a point source of which the position is read
   * from a
   * file.
   */
  bool _is_point_source;

  /**
   * Height of the domain.
   */
  double _max_height;
  /**
   * Structure of the physical properties of the electron beam.
   */
  BeamProperty _beam;
  /**
   * Function that describes the position of the beam on the surface.
   */
  std::array<std::unique_ptr<dealii::Function<1>>, dim - 1> _position;
};

template <int dim>
inline void ElectronBeam<dim>::set_max_height(double height)
{
  _max_height = height;
}
} // namespace adamantine

#endif