#include <iostream>
#include <complex>
#include <cmath>
#include <vector>
#include <numeric>
#include <tuple>
#include <fstream>
#include <ctime>
#include <chrono>
#include <stdio.h>
#include <Eigen/Dense>
#include <Eigen/Core>
#include <omp.h>

// Defining special types
typedef std::complex<double> dcomp;
typedef std::vector<double> dvec;
typedef std::vector< std::vector<double> > ddvec;
typedef std::vector<int> ivec;
typedef std::vector< std::vector<int> > iivec;
typedef std::vector< std::complex<double> > dcvec;
typedef std::vector< std::vector<std::complex<double> > > ddcvec;

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

		FUNCTIONS

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

inline const double pi()
{
	// Set teh value fo pi
	const double pi = std::atan(1) * 4;
	return pi;
}

/* --------------------------------------------------------------------
		FROM MICROSECONDS TO HH:MM:SS:MS
-----------------------------------------------------------------------*/
std::tuple<long long, long long, long long, long long> ms_to_time(long long ms)
{
	// Defining the variables
	long long s, m, h;

	// Calculating the time in hour:minutes:seconds:microseconds
	s = ms / 1000000;
	ms = ms % 1000000;
	m = s / 60;
	s = s % 60;
	h = m / 60;
	m = m % 60;

	return { ms,s,m,h };
}
/* --------------------------------------------------------------------
		PRINT in HH:MM:SS:MS FORMAT
-----------------------------------------------------------------------*/
void time_print(long long ms, long long s, long long m, long long h)
{

	// Print the time
	if (h < 10)
	{
		std::cout << "0" << h << ":";
	}
	else
	{
		std::cout << "" << h << ":";
	}
	if (m < 10)
	{
		std::cout << "0" << m << ":";
	}
	else
	{
		std::cout << "" << m << ":";
	}
	if (s < 10)
	{
		std::cout << "0" << s << ":" << ms;
	}
	else
	{
		std::cout << "" << s << ":" << ms;
	}

	return;
}
/* --------------------------------------------------------------------
		CHI FROM Z
-----------------------------------------------------------------------*/
inline dcomp chi_from_z(dcomp z, dcomp z1, dcomp z2, double L, double mu)
{
	// Defining the variables
	dcomp z0, Z, chi;

	// Calculating the chi from a z value
	z0 = 0.5 * (z1 + z2);
	Z = exp(dcomp(0, -1) * mu) * 2.0 * (z - z0) / L;
	chi = Z + sqrt(Z - 1.0) * sqrt(Z + 1.0);

	return chi;
}
/* --------------------------------------------------------------------
		Z FROM CHI
-----------------------------------------------------------------------*/
inline dcomp z_from_chi(dcomp chi, dcomp z1, dcomp z2, double L, double mu)
{
	// Defining the variables
	dcomp z, z0, Z;

	// Calculating the z from a chi value
	z0 = 0.5 * (z1 + z2);
	Z = 0.5 * (chi + 1.0 / chi);
	z = 0.5 * L * Z * exp(dcomp(0, 1) * mu) + z0;

	return z;
}
/* --------------------------------------------------------------------
		ANGEL CHANGE
-----------------------------------------------------------------------*/
inline double angel_change(dcomp z, dcomp z1, dcomp z2)
{
	// Defining the variables
	double u, v, eta;

	// Calculating the angels
	u = arg(z - z1);
	v = arg(z1 - z2);

	// Correct to angel of 0 to 2pi
	if (u < 0)
	{
		u += 2 * pi();
	}
	if (v < 0)
	{
		v += 2 * pi();
	}

	// Calculate angel between the vectors
	eta = abs(u - v);

	// Correct to angel of 0 to pi
	if (eta > pi())
	{
		eta -= 2 * pi();
	}

	eta = abs(eta);

	return eta;
}
/* --------------------------------------------------------------------
		FIND INTERSECTION POINT
-----------------------------------------------------------------------*/
inline std::tuple<dcomp, int> intersection_point(dcomp z1, dcomp z2, dcomp z3, dcomp z4)
{
	// Defining the variables
	dcomp zint, Z1, Z2;
	int int_check = 0;

	if (z1 != z3 || z2 != z4)
	{
		// Calculating the intersection point
		zint = ((conj(z2 - z1) * z1 - (z2 - z1) * conj(z1)) * (z4 - z3) - (conj(z4 - z3) * z3 - (z4 - z3) * conj(z3)) * (z2 - z1)) / ((z4 - z3) * conj(z2 - z1) - (z2 - z1) * conj(z4 - z3));

		// Check if intersection is on the lines
		Z1 = (zint - .5 * (z1 + z2)) / (.5 * (z1 - z2));
		Z2 = (zint - .5 * (z3 + z4)) / (.5 * (z3 - z4));

		if (real(Z1 * conj(Z1)) < 1.0)
		{
			if (real(Z2 * conj(Z2)) < 1.0)
			{
				int_check = 1;
			}
		}

	}

	return { zint, int_check };
}
/* --------------------------------------------------------------------
		GET DELTA
-----------------------------------------------------------------------*/
inline dcomp get_delta(ddcvec ab, int nab, int m, ivec pos_z1, ivec pos_z2)
{
	// Defintion of variables
	dcomp delta;

	delta = 0;
	for (int ii = 0; ii < nab; ii++)
	{
		// Adding the jump for the z1 elements
		if (pos_z1[ii] == 1)
		{
			for (int jj = 0; jj < m; jj++)
			{
				delta += ab[ii][jj]* pow(-1, jj);
			}
		}
		// Adding the jump for the z2 elements
		if (pos_z2[ii] == 1)
		{
			for (int jj = 0; jj < m; jj++)
			{
				delta -= ab[ii][jj];
			}
		}

	}

	return delta;
}
/* --------------------------------------------------------------------
		TAU UNIFORM STRESS
-----------------------------------------------------------------------*/
inline std::tuple<dcomp, dcomp>  tau_uni(double sigma_11inf)
{
	// Defining the variables
	dcomp tau_11, tau_12, phi, psi;

	// Get the phi and psi
	phi = -0.5 * sigma_11inf;
	psi = -0.5 * sigma_11inf;

	// calculating the tau
	tau_11 = -phi - psi;
	tau_12 = -phi - phi;

	return { tau_11, tau_12 };
}
/* --------------------------------------------------------------------
		TAU CRACK
-----------------------------------------------------------------------*/
inline std::tuple<dcomp, dcomp> tau_crack(dcomp z, dcomp z1, dcomp z2, double L, double mu, int m, dcvec a)
{
	// Defining the variables
	dcomp chi, chi_bar, Z, chi_pow;
	dcomp dphi, dphi_bar, ddphi, dpsi;
	dcomp tau_11, tau_12, S1, L_frac;

	// Getting the chi - and Z - coordinates
	chi = chi_from_z(z, z1, z2, L, mu);
	chi_bar = conj(chi);
	Z = exp(dcomp(0, -1) * mu) * 2.0 * (z - 0.5 * (z1 + z2)) / L;

	// Calculating the series
	dphi = 0;
	dphi_bar = 0;
	ddphi = 0;
	dpsi = 0;
	chi_pow = chi * chi - 1.0;
	for (int ii = 0; ii < m; ii++)
	{
		double n = ii + 1.0;
		dcomp beta_n = a[ii] * n;
		dcomp chipow = pow(chi, (1.0 - n)) / chi_pow;
		dphi += conj(beta_n) * chipow;
		dphi_bar += beta_n * conj(chipow);
		ddphi -= conj(beta_n) * (pow(chi, (2.0 - n)) / (chi_pow * chi_pow * chi_pow)) * ((n + 1.0) * chi * chi - n + 1.0);
		dpsi -= beta_n * chipow;
	}

	// Multiplying the constants
	L_frac = (4.0 / L) * exp(dcomp(0, -1) * mu);
	dphi *= L_frac;
	dphi_bar *= conj(L_frac);
	ddphi *= (16.0 / (L * L)) * exp(dcomp(0, -2) * mu);
	dpsi *= L_frac;

	// Calcualting tau
	tau_11 = -0.5 * L * (Z - conj(Z)) * ddphi - exp(dcomp(0, -1) * mu) * (dphi + dpsi);
	tau_12 = -exp(dcomp(0, 1) * mu) * dphi - exp(dcomp(0, -1) * mu) * dphi_bar;

	return { tau_11, tau_12 };
}
/* --------------------------------------------------------------------
		TAU TOTAL
-----------------------------------------------------------------------*/
inline std::tuple<dcomp, dcomp>  tau_total(dcomp z, double sigma_11inf, dcvec z1, dcvec z2, dvec L, dvec mu, int ma, int na, ddcvec a, int m_not_a)
{
	// Defining the variables
	dcomp tau_11, tau_12;

	// Add the unifrom stress field
	std::tie(tau_11, tau_12) = tau_uni(sigma_11inf);

	// Add the analytic element for a crack
	if (na > 0)
	{
		for (int ii = 0; ii < na; ii++)
		{
			if (ii != m_not_a)
			{
				dcomp tau_11c, tau_12c;
				std::tie(tau_11c, tau_12c) = tau_crack(z, z1[ii], z2[ii], L[ii], mu[ii], ma, a[ii]);
				tau_11 += tau_11c;
				tau_12 += tau_12c;
			}
		}
	}

	return { tau_11, tau_12 };
}
/* --------------------------------------------------------------------
		T TOTAL
-----------------------------------------------------------------------*/
inline dcomp T_total(dcomp z, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a, int m_not_a, int m_is_a)
{
	// Defining the variables
	dcomp tau_11, tau_12, T;

	// Get teh taus
	std::tie(tau_11, tau_12) = tau_total(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a, m_not_a);

	// Calculate the tractions
	T = dcomp(0, -.5) * (tau_11 * exp(dcomp(0, 2) * mua[m_is_a]) - tau_12);

	return { T };
}
/* --------------------------------------------------------------------
		SOLVE CRACK ANALYTIC ELEMENT A (BETA IN PAPER)
-----------------------------------------------------------------------*/
inline dcvec AE_crack_solver(Eigen::VectorXd T_s, Eigen::VectorXd T_n, Eigen::MatrixXd A, int ma, int Na, dvec pa, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int na, ddcvec a_in, dcvec zint, ivec int_check, int m_is_a)
{
	// Defining the variables
	dcvec a(ma);
	Eigen::VectorXd b1(ma);
	Eigen::VectorXd b2(ma);

	// Count number of intersections
	int int_count = std::accumulate(int_check.begin(), int_check.end(), 0.0);

	// Check if the crack has any intersection
	if (int_count == 0)
	{
		// Solving the linear system (without intersections)
		b1 = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(T_s);
		b2 = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(T_n);
	}
	else
	{
		// Define new matrices for A and T and assign the pre-calculated values
		Eigen::MatrixXd A_int_s(Na + int_count * 3, ma);
		Eigen::MatrixXd A_int_n(Na + int_count, ma);
		Eigen::VectorXd T_s_int(Na + int_count * 3);
		Eigen::VectorXd T_n_int(Na + int_count);
		#pragma omp parallel for default(none) shared(A_int_s, A_int_n, A, T_n_int, T_n, T_s_int, T_s)
		for (int ii = 0; ii < Na; ii++)
		{
			for (int jj = 0; jj < ma; jj++)
			{
				/*A_int_re(ii, jj) = A(ii, jj);
				A_int_im(ii, jj) = A(ii, jj);*/
				A_int_s(ii, jj) = A(ii, jj);
				A_int_n(ii, jj) = A(ii, jj);
			}
			T_s_int(ii) = T_s(ii);
			T_n_int(ii) = T_n(ii);
		}
		int cnt_s = 0;
		int cnt_n = 0;
		for (int ii = 0; ii < na; ii++)
		{
			// Check if element ii has any intersections
			if (int_check[ii] == 1)
			{
				// Define variables
				dcomp chia, tau11, chi_pow, L_frac, T_temp;
				double thetaa;

				// Compute the intersection angel in the chi-plane
				chia = chi_from_z(zint[ii], z1a[m_is_a], z2a[m_is_a], La[m_is_a], mua[m_is_a]);
				thetaa = imag(log(chia));

				// Pre-calcualte terms
				chi_pow = chia /( chia * chia - 1.0 );
				L_frac = (dcomp(0,8.0) / La[m_is_a]) * exp(dcomp(0, -2) * mua[m_is_a]);

				// Add the intersection to the A matrix
				for (int jj = 0; jj < ma; jj++)
				{
					double n = jj + 1.0;
					// Add a control point at the intersection - tau^11 condition
					A_int_s(Na + cnt_s, jj) = real( chi_pow * L_frac * n * pow(chia, -n) );
					A_int_s(Na + (cnt_s + 1), jj) = imag( chi_pow * L_frac * n * pow(chia, -n) );

					// Add a control point at the intersection - traction condition
					A_int_s(Na + (cnt_s + 2), jj) = n * sin(n * thetaa);
					A_int_n(Na + cnt_n, jj) = n * sin(n * thetaa);
				}

				// Calculate the taus at the intersection
				dcomp tau_11, tau_12;
				std::tie(tau_11, tau_12) = tau_total(zint[ii], sigma_11inf, z1a, z2a, La, mua, ma, na, a_in, m_is_a);

				// Add the intersection to the T_s vector
				T_s_int(Na + cnt_s) = -real(tau_11);
				T_s_int(Na + (cnt_s + 1)) = -imag(tau_11);

				// Calculate the traction at the control point at the intersection
				T_temp = T_total(zint[ii], sigma_11inf, z1a, z2a, La, mua, ma, na, a_in, m_is_a, m_is_a);

				// Add the intersection to the T vectors
				T_s_int(Na + (cnt_s + 2)) = real(T_temp) * 0.5 * La[m_is_a] * sin(thetaa);
				T_n_int(Na + cnt_n) = (pa[ii] + imag(T_temp)) * -0.5 * La[m_is_a] * sin(thetaa);

				// Add step to loop count
				cnt_s += 3;
				cnt_n += 1;
			}

		}

		// Solving the linear system (with intersections)
		b1 = A_int_s.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(T_s_int);
		b2 = A_int_n.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(T_n_int);
	}
	// Assign to a
	for (int ii = 0; ii < ma; ii++)
	{
		a[ii] = dcomp(b2[ii], b1[ii]);
	}

	return { a };
}
/* --------------------------------------------------------------------
		ITERATOR
-----------------------------------------------------------------------*/
inline ddcvec iterator(double cond, int ITR, int Na, dvec pa, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na)
{
	// Defining the variables
	dcomp tau_11, tau_12, T;
	Eigen::MatrixXd A(Na, ma);
	dvec theta_a(Na);
	ddvec term(na, dvec(Na));
	ddcvec za(na, dcvec(Na));
	ddcvec a(na, dcvec(ma));
	ddcvec zint(na, dcvec(na));
	iivec int_check(na, ivec(na));
	ivec int_count(na);
	double theta_0a, delthetaa;

	// Print the conditions
	std::cout << std::scientific;
	std::cout << "Solver for " << na << " analytical element cracks with " << ma << " coefficients at " << Na << " integration points." << std::endl;
	std::cout << "Iterations break after: error < " << cond << " or iterations > " << ITR << "." << std::endl << std::endl;
	std::cout << "	Error:" << "		Iteration:" << std::endl;

	// Assigning variables
	theta_0a = pi() / (Na);
	delthetaa = (pi() - 2.5*theta_0a) / (Na - 1.0);

	if (na > 0)
	{
		// Find the intersection points
		#pragma omp parallel for default(none) shared(zint, int_check, int_count, z1a, z2a)
		for (int ii = 0; ii < na; ii++)
		{
			for (int jj = 0; jj < na; jj++)
			{
				std::tie(zint[ii][jj], int_check[ii][jj]) = intersection_point(z1a[ii], z2a[ii], z1a[jj], z2a[jj]);
			}
			int_count[ii] = std::accumulate(int_check[ii].begin(), int_check[ii].end(), 0.0);
		}

		// Calculating the A, a theta and term matrices
		#pragma omp parallel for default(none) shared(theta_a, theta_0a, delthetaa)
		for (int ii = 0; ii < Na; ii++)
		{
			theta_a[ii] = theta_0a + ii * delthetaa;
		}

		//#pragma omp parallel for default(none) shared(A, za, term, z1a, z2a, La, mua, theta_a)
		for (int ii = 0; ii < Na; ii++)
		{
			for (int mm = 0; mm < ma; mm++)
			{
				A(ii, mm) = (mm + 1.0) * sin((mm + 1.0) * theta_a[ii]);
			}
			for (int jj = 0; jj < na; jj++)
			{
				dcomp chi;
				chi = exp(dcomp(0, 1) * theta_a[ii]);
				za[jj][ii] = z_from_chi(chi, z1a[jj], z2a[jj], La[jj], mua[jj]);
				term[jj][ii] = -0.5 * La[jj] * sin(theta_a[ii]);
			}
		}

	}

	// Sovle the a (betas in paper)
	double error = 1;
	ddcvec a_old(na, dcvec(ma, 0));
	double error_a;
	int NIT = 0;
	while ( error > cond && NIT < ITR )
	{
				for (int ii = 0; ii < na; ii++)
				{
					Eigen::VectorXd T_s(Na);
					Eigen::VectorXd T_n(Na);
					#pragma omp parallel for default(none) shared(T_s, T_n, za, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
					for (int jj = 0; jj < Na; jj++)
					{
						dcomp T;
						T = T_total(za[ii][jj], sigma_11inf, z1a, z2a, La, mua, ma, na, a, ii, ii);
						T_s(jj) = -real(T) * term[ii][jj];
						T_n(jj) = (pa[ii] + imag(T)) * term[ii][jj];
					}
					dcomp tau_11, tau_12;
					a[ii] = AE_crack_solver(T_s, T_n, A, ma, Na, pa, sigma_11inf, z1a, z2a, La, mua, na, a, zint[ii], int_check[ii], ii);
					std::tie(tau_11, tau_12) = tau_total(zint[0][1], sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1);
				}

		// Calcualte the error
		error_a = 0;
		if (na > 0)
		{
			dvec dela(na);
			#pragma omp parallel for default(none) shared(dela, a, error_a)
			for (int ii = 0; ii < na; ii++)
			{
				dvec dela_temp(ma);
				for (int jj = 0; jj < ma; jj++)
				{
					dela_temp[jj] = abs(a[ii][jj] - a_old[ii][jj]);
				}
				dela[ii] = *max_element(dela_temp.begin(), dela_temp.end());
			}
			error_a = *max_element(dela.begin(), dela.end());
		}
		dvec error_vec{ error_a };
		error = *max_element(error_vec.begin(), error_vec.end());

		NIT += 1;
		a_old = a;

		std::cout << std::scientific;
		std::cout << "	" << error << "	" << NIT << std::endl;

	}

	return { a };
}
/* --------------------------------------------------------------------
		STRESS TOTAL
-----------------------------------------------------------------------*/
inline std::tuple<double, double, double> sigma_total(dcomp z, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	dcomp S1, S2, tau_11, tau_12;
	double sigma_11, sigma_22, sigma_12;

	// Calculating the tau
	std::tie(tau_11, tau_12) = tau_total(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1);

	// Calculate the sigmas
	S1 = .5 * (tau_11 + tau_12);
	S2 = .5 * (-tau_11 + tau_12);
	sigma_11 = real(S1);
	sigma_22 = real(S2);
	sigma_12 = -imag(S1);

	return { sigma_11, sigma_22, sigma_12 };
}
/* --------------------------------------------------------------------
		STRESS FIELD
-----------------------------------------------------------------------*/
std::tuple<dvec, dvec, ddvec, ddvec, ddvec> stress_field(double xfrom, double xto, double yfrom, double yto, int Nx, int Ny, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	ddvec grid_11(Nx, dvec(Ny));
	ddvec grid_22(Nx, dvec(Ny));
	ddvec grid_12(Nx, dvec(Ny));
	double dx;
	double dy;
	dvec x_vec(Nx);
	dvec y_vec(Ny);

	// Calcualte the sigma grids
	dx = (xto - xfrom) / (Nx - 1.0);
	dy = (yto - yfrom) / (Ny - 1.0);
	#pragma omp parallel for default(none) shared(grid_11, grid_22, grid_12, x_vec, y_vec, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
	for (int ii = 0; ii < Nx; ii++)
	{
		for (int jj = Ny; jj--;)
		{
			x_vec[ii] = xfrom + ii * dx;
			y_vec[jj] = yfrom + jj * dy;
			std::tie(grid_11[ii][jj], grid_22[ii][jj], grid_12[ii][jj]) = sigma_total(dcomp(x_vec[ii], y_vec[jj]), sigma_11inf, z1a, z2a, La, mua, ma, na, a);
		}
	}
	return { x_vec, y_vec, grid_11, grid_22, grid_12 };
}
/* --------------------------------------------------------------------
		PRINCIPAL STRESSES
-----------------------------------------------------------------------*/
std::tuple<double, double, double> principal_sigma(dcomp z, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	double sigma_1;
	double sigma_2;
	double theta_p;
	double frac1, frac2, sqrt1;
	dcomp S1, S2, tau_11, tau_12;
	double sigma_11, sigma_22, sigma_12;

	// Calculating the tau
	std::tie(tau_11, tau_12) = tau_total(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1);

	// Calculate the sigmas
	S1 = .5 * (tau_11 + tau_12);
	S2 = .5 * (-tau_11 + tau_12);
	sigma_11 = real(S1);
	sigma_22 = real(S2);
	sigma_12 = -imag(S1);

	// Calculating the terms
	frac1 = (sigma_11 + sigma_22) / 2.0;
	frac2 = (sigma_11 - sigma_22) / 2.0;
	sqrt1 = sqrt(frac2 * frac2 + sigma_12 * sigma_12);

	// Calculating the principal stresses and the angel of sigma
	sigma_1 = frac1 + sqrt1;
	sigma_2 = frac1 - sqrt1;

	// Calcuating the angel theta_p
	theta_p = -0.5 * imag(log(tau_11));

	// Changing the absolut sigma is sigma_2 > sigma_1 (TURNED OFF)
	/*if (abs(sigma_2) > abs(sigma_1))
	{
		sigma_1 = frac1 - sqrt1;
		sigma_2 = frac1 + sqrt1;
		theta_p = -0.5*imag(log(tau_11)) + pi() / 2;
	}
	*/

	return { sigma_1, sigma_2, theta_p };
}
/* --------------------------------------------------------------------
		PRINCIPAL STRESSES PLOT
-----------------------------------------------------------------------*/
std::tuple<double, double, double> principal_sigma_plt(double sigma_11, double sigma_22, double sigma_12)
{
	// Defining the variables
	double sigma_1;
	double sigma_2;
	double theta_p;
	double frac1, frac2, sqrt1;
	dcomp tau_11;

	// Calculating the tau
	tau_11 = sigma_11 - sigma_22 - dcomp(0, 2) * sigma_12;

	// Calculating the terms
	frac1 = (sigma_11 + sigma_22) / 2.0;
	frac2 = (sigma_11 - sigma_22) / 2.0;
	sqrt1 = sqrt(frac2 * frac2 + sigma_12 * sigma_12);

	// Calculating the principal stresses and the angel of sigma
	sigma_1 = frac1 + sqrt1;
	sigma_2 = frac1 - sqrt1;

	// Calcuating the angel theta_p
	theta_p = -0.5 * imag(log(tau_11));

	// Changing the absolut sigma is sigma_2 > sigma_1 (TURNED OFF)
	/*if (abs(sigma_2) > abs(sigma_1))
	{
		sigma_1 = frac1 - sqrt1;
		sigma_2 = frac1 + sqrt1;
		theta_p = -0.5*imag(log(tau_11)) + pi() / 2;
	}
	*/

	return { sigma_1, sigma_2, theta_p };
}
/* --------------------------------------------------------------------
		PRINCIPAL STRESS FIELDS
-----------------------------------------------------------------------*/
std::tuple<ddvec, ddvec, ddvec> principal_stress_field(int Nx, int Ny, ddvec grid_11, ddvec grid_22, ddvec grid_12)
{
	// Defining the variables
	ddvec grid_1(Nx, dvec(Ny));
	ddvec grid_2(Nx, dvec(Ny));
	ddvec grid_tp(Nx, dvec(Ny));

	// Calculate teh principal stresses from the stress feilds
	#pragma omp parallel for default(none) shared(grid_1, grid_2, grid_tp, grid_11, grid_22, grid_12)
	for (int ii = 0; ii < Nx; ii++)
	{
		for (int jj = Ny; jj--;)
		{
			std::tie(grid_1[ii][jj], grid_2[ii][jj], grid_tp[ii][jj]) = principal_sigma_plt(grid_11[ii][jj], grid_22[ii][jj], grid_12[ii][jj]);
		}
	}
	return { grid_1, grid_2, grid_tp };
}
/* --------------------------------------------------------------------
		PRINCIPAL STRESS TRAJECTORIES
-----------------------------------------------------------------------*/
std::tuple<ddcvec, ddcvec> principal_stress_trajectories(double xfrom, double xto, double yfrom, double yto, dcvec xtraj, dcvec ytraj, int Ntraj, int lvs_traj, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	ddcvec traj_1(lvs_traj * 2, dcvec(Ntraj));
	ddcvec traj_2(lvs_traj * 2, dcvec(Ntraj));
	double dx, dy, dx_lvsre, dx_lvsim, dy_lvsre, dy_lvsim, pi_val;
	double cond;
	int NIT;

	// Getting the starting points
	dx = (xto - xfrom) / (Ntraj * 0.8);
	dy = (yto - yfrom) / (Ntraj * 0.8);
	dx_lvsre = (real(xtraj[1]) - real(xtraj[0])) / (lvs_traj - 1.0);
	dx_lvsim = (imag(xtraj[1]) - imag(xtraj[0])) / (lvs_traj - 1.0);
	dy_lvsre = (real(ytraj[1]) - real(ytraj[0])) / (lvs_traj - 1.0);
	dy_lvsim = (imag(ytraj[1]) - imag(ytraj[0])) / (lvs_traj - 1.0);
	pi_val = 0.5 * pi();
	cond = 1e-6;
	NIT = 10;

	// SIGMA 1
	#pragma omp parallel for default(none) shared(traj_1, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
	for (int ii = 0; ii < lvs_traj; ii++)
	{
		traj_1[ii][0] = dcomp(real(xtraj[0]) + ii * dx_lvsre, imag(xtraj[0]) + ii * dx_lvsim);
		dcomp z = traj_1[ii][0];
		dcomp z_old = z;
		double dx1 = dx;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z11, z_oldt, sigma, sigmat;
			double sigma_1, theta_p;
			double ee, eta;
			std::tie(sigma_1, std::ignore, theta_p) = principal_sigma(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			sigma = abs(sigma_1) * exp(dcomp(0, 1) * theta_p);
			z11 = z + sigma / abs(sigma) * dx1;
			eta = angel_change(z11, z, z_old);
			if (eta > pi_val && jj > 1) {
				dx1 = -dx1;
			}
			zt = z + sigma / abs(sigma) * dx1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				std::tie(sigma_1, std::ignore, theta_p) = principal_sigma(zt, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				sigmat = abs(sigma_1) * exp(dcomp(0, 1) * theta_p);
				z11 = z + (sigma + sigmat) / abs(sigma + sigmat) * dx1;
				eta = angel_change(z11, z, z_old);
				if (eta > pi_val && jj > 1) {
					dx1 = -dx1;
				}
				zt = z + (sigma + sigmat) / abs(sigma + sigmat) * dx1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_1[ii][jj] = zt;
			z_old = z;
			z = zt;
		}

		int kk = ii + lvs_traj;
		traj_1[kk][0] = traj_1[ii][0];
		z = traj_1[kk][0];
		z_old = z;
		dx1 = -dx;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z11, z_oldt, sigma, sigmat;
			double sigma_1, theta_p;
			double ee, eta;
			std::tie(sigma_1, std::ignore, theta_p) = principal_sigma(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			sigma = abs(sigma_1) * exp(dcomp(0, 1) * theta_p);
			z11 = z + sigma / abs(sigma) * dx1;
			eta = angel_change(z11, z, z_old);
			if (eta > pi_val && jj > 1) {
				dx1 = -dx1;
			}
			zt = z + sigma / abs(sigma) * dx1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				std::tie(sigma_1, std::ignore, theta_p) = principal_sigma(zt, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				sigmat = abs(sigma_1) * exp(dcomp(0, 1) * theta_p);
				z11 = z + (sigma + sigmat) / abs(sigma + sigmat) * dx1;
				eta = angel_change(z11, z, z_old);
				if (eta > pi_val && jj > 1) {
					dx1 = -dx1;
				}
				zt = z + (sigma + sigmat) / abs(sigma + sigmat) * dx1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_1[kk][jj] = zt;
			z_old = z;
			z = zt;
		}
	}

	// SIGMA 2
	#pragma omp parallel for default(none) shared(traj_2, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
	for (int ii = 0; ii < lvs_traj; ii++)
	{
		traj_2[ii][0] = dcomp(real(ytraj[0]) + ii * dy_lvsre, imag(ytraj[0]) + ii * dy_lvsim);
		double dy1 = dy;
		dcomp z = traj_2[ii][0];
		dcomp z_old = z;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z11, z_oldt, sigma, sigmat;
			double sigma_2, theta_p;
			double ee, eta;
			std::tie(std::ignore, sigma_2, theta_p) = principal_sigma(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			sigma = abs(sigma_2) * exp(dcomp(0, 1) * (theta_p + pi_val));
			z11 = z + sigma / abs(sigma) * dy1;
			eta = angel_change(z11, z, z_old);
			if (eta > pi_val && jj > 1) {
				dy1 = -dy1;
			}
			zt = z + sigma / abs(sigma) * dy1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				std::tie(std::ignore, sigma_2, theta_p) = principal_sigma(zt, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				sigmat = abs(sigma_2) * exp(dcomp(0, 1) * (theta_p + pi_val));
				z11 = z + (sigma + sigmat) / abs(sigma + sigmat) * dy1;
				eta = angel_change(z11, z, z_old);
				if (eta > pi_val && jj > 1) {
					dy1 = -dy1;
				}
				zt = z + (sigma + sigmat) / abs(sigma + sigmat) * dy1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_2[ii][jj] = zt;
			z_old = traj_2[ii][(int)(jj - 1)];
			z = zt;
		}

		int kk = ii + lvs_traj;
		traj_2[kk][0] = traj_2[ii][0];
		dy1 = -dy;
		z = traj_2[kk][0];
		z_old = z;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z11, z_oldt, sigma, sigmat;
			double sigma_2, theta_p;
			double ee, eta;
			std::tie(std::ignore, sigma_2, theta_p) = principal_sigma(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			sigma = abs(sigma_2) * exp(dcomp(0, 1) * (theta_p + pi_val));
			z11 = z + sigma / abs(sigma) * dy1;
			eta = angel_change(z11, z, z_old);
			if (eta > pi_val && jj > 1) {
				dy1 = -dy1;
			}
			zt = z + sigma / abs(sigma) * dy1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				std::tie(std::ignore, sigma_2, theta_p) = principal_sigma(zt, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				sigmat = abs(sigma_2) * exp(dcomp(0, 1) * (theta_p + pi_val));
				z11 = z + (sigma + sigmat) / abs(sigma) * dy1;
				eta = angel_change(z11, z, z_old);
				if (eta > pi_val && jj > 1) {
					dy1 = -dy1;
				}
				zt = z + (sigma + sigmat) / abs(sigma + sigmat) * dy1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_2[kk][jj] = zt;
			z_old = z;
			z = zt;
		}
	}
	return { traj_1, traj_2 };
}
/* --------------------------------------------------------------------
		W UNIFORM STRESS
-----------------------------------------------------------------------*/
inline dcomp  w_uni(dcomp z, double kappa, double G, double sigma_11inf)
{
	// Defining the variables
	dcomp phi_bar, dphi, psi, w;

	// calculating the veriables
	phi_bar = -0.5 * sigma_11inf * conj(z);
	dphi = -0.5 * sigma_11inf;
	psi = -0.5 * sigma_11inf * z;

	// Calculating w
	w = 1 / (4 * G) * ((z - conj(z)) * dphi + kappa * phi_bar + psi);

	return { w };
}
/* --------------------------------------------------------------------
		W CRACK - ANALYTIC ELEMENT
-----------------------------------------------------------------------*/
inline dcomp w_crack(dcomp z, double kappa, double G, dcomp z1, dcomp z2, double L, double mu, int m, dcvec a)
{
	// Defining the variables
	dcomp chi, chi_bar, Z, chi_pow;
	dcomp dphi, phi_bar, psi;
	dcomp w, L_frac;
	double n;

	// Getting the chi - and Z - coordinates
	chi = chi_from_z(z, z1, z2, L, mu);
	chi_bar = conj(chi);
	Z = exp(dcomp(0, -1) * mu) * 2.0 * (z - 0.5 * (z1 + z2)) / L;

	// Calculating the series
	phi_bar = 0;
	dphi = 0;
	psi = 0;
	n = 0;
	chi_pow = chi * chi - 1.0;
	for (int ii = 0; ii < m; ii++)
	{
		dcomp a_n;
		n += 1;
		a_n = a[ii] * n;
		dphi += conj(a_n) * pow(chi, (1.0 - n)) / chi_pow;
		phi_bar -= a[ii] * pow(chi_bar, -n);
		psi += a[ii] * pow(chi, -n);
	}

	// Multiplying the constants
	L_frac = (4.0 / L) * exp(dcomp(0, -1) * mu);
	dphi *= L_frac;

	// Calcualting w
	w = 1 / (4 * G) * (0.5 * L * (Z - conj(Z)) * exp(dcomp(0, 1) * mu) * dphi + kappa * phi_bar + psi);

	return { w };
}
/* --------------------------------------------------------------------
		W TOTAL
-----------------------------------------------------------------------*/
inline dcomp  w_total(dcomp z, double kappa, double G, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	dcomp w, wg;

	// Add the unfirm stress field
	w = w_uni(z, kappa, G, sigma_11inf);

	// Add the anlytic element for a crack
	if (na > 0)
	{
		for (int ii = 0; ii < na; ii++)
		{
			dcomp wc;
			wc = w_crack(z, kappa, G, z1a[ii], z2a[ii], La[ii], mua[ii], ma, a[ii]);
			w += wc * exp(dcomp(0, -1) * mua[ii]);
		}
	}

	return { w };
}
/* --------------------------------------------------------------------
		DISPLACEMENT FIELD
-----------------------------------------------------------------------*/
std::tuple<dvec, dvec, ddcvec> w_field(double xfrom, double xto, double yfrom, double yto, int Nw, double kappa, double G, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)

{
	// Defining the variables
	ddcvec grid_w(Nw, dcvec(Nw));
	dvec x_vecw(Nw), y_vecw(Nw);
	double dx;
	double dy;

	// Calcualte the displacement grid
	dx = (xto - xfrom) / (Nw - 1.0);
	dy = (yto - yfrom) / (Nw - 1.0);
#pragma omp parallel for default(none) shared(grid_w, x_vecw, y_vecw, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
	for (int ii = 0; ii < Nw; ii++)
	{
		for (int jj = 0; jj < Nw; jj++)
		{

			x_vecw[ii] = xfrom + ii * dx;
			y_vecw[jj] = yfrom + jj * dy;
			grid_w[ii][jj] = w_total(dcomp(x_vecw[ii], y_vecw[jj]), kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
		}
	}
	return { x_vecw, y_vecw, grid_w };
}
/*---------------------------------------------------------------------
		DISPLACEMENT TRAJECTORIES
-----------------------------------------------------------------------*/
ddcvec w_trajectories(double xfrom, double xto, double yfrom, double yto, int Ntraj, int Nw, double kappa, double G, double sigma_11inf, dcvec z1a, dcvec z2a, dvec La, dvec mua, int ma, int na, ddcvec a)
{
	// Defining the variables
	ddcvec traj_w((int)(Nw * 2), dcvec(Ntraj));
	double dx, dy_lvs;
	double cond, pi_val;
	int NIT;

	// Getting the starting points
	dx = (xto - xfrom) / (Ntraj);
	dy_lvs = (yto - yfrom) / (Nw - 1.0);
	pi_val = 0.5 * pi();
	cond = 1e-6;
	NIT = 10;

	// w trajectories
#pragma omp parallel for default(none) shared(traj_w, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a)
	for (int ii = 0; ii < Nw; ii++)
	{
		traj_w[ii][0] = dcomp(xfrom, yfrom + ii * dy_lvs);
		dcomp z = traj_w[ii][0];
		dcomp z_old = z;
		double dx1 = dx;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z_oldt, w, w1;
			double ee;
			w = w_total(z, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			zt = z + conj(w) / abs(w) * dx1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				w1 = w_total(zt, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				zt = z + conj(w + w1) / abs(w + w1) * dx1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_w[ii][jj] = zt;
			z_old = z;
			z = zt;
		}

		int kk = ii + Nw;
		traj_w[kk][0] = dcomp(xto, yfrom + ii * dy_lvs);
		z = traj_w[kk][0];
		z_old = z;
		dx1 = dx;
		for (int jj = 1; jj < Ntraj; jj++)
		{
			dcomp zt, z_oldt, w, w1;
			double ee;
			w = w_total(z, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
			zt = z + conj(w) / abs(w) * dx1;

			ee = 1;
			for (int rr = NIT; rr--;)
			{
				z_oldt = zt;
				w1 = w_total(zt, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
				zt = z + conj(w + w1) / abs(w + w1) * dx1;
				ee = std::norm(z_oldt - zt);
				if (ee < cond)
				{
					break;
				}
			}

			traj_w[kk][jj] = zt;
			z_old = z;
			z = zt;

		}
	}
	return { traj_w };
}

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

		MAIN SCRIPT

-----------------------------------------------------------------------*/
int main()
{
	/* --------------------------------------------------------------------
			Defining variables and importing data
	-----------------------------------------------------------------------*/

	// Header in console window
	auto start = std::chrono::high_resolution_clock::now(); // Start the clock

	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		ANALYTIC ELEMENT LINEAR ELASTIC SOLVER	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;

	auto date_time1 = std::chrono::system_clock::now();
	std::time_t start_time = std::chrono::system_clock::to_time_t(date_time1);
	char str_time1[26];
	ctime_s(str_time1, sizeof str_time1, &start_time);
	std::cout << "Program started: " << str_time1 << std::endl;

	// Setting the data types
	dcomp z;
	double kappa, sigma_11inf, G, cond;
	int na, ma, Na, NIT;

	// Read the input data from binary file PART 1
	std::ifstream input_file("geometry_data.bin", std::ios::in | std::ios::binary | std::ios::ate);
	std::streampos size = input_file.tellg();
	char* memblock = new char[size];
	input_file.seekg(0, std::ios::beg);
	input_file.read(memblock, size);
	double* fin = (double*)memblock;//reinterpret as doubles

	sigma_11inf = fin[0];
	kappa = fin[1];
	G = fin[2];
	na = (int)fin[3];
	ma = (int)fin[4];
	Na = (int)fin[5];
	cond = (double)fin[6];
	NIT = (int)fin[7];

	// Declaring the vecotrs
	dcvec z1a(na), z2a(na);
	dvec pa(na), La(na), mua(na);
	ddcvec a(na, dcvec(ma));

	int pos = 7 + 1;
	if (na > 0)
	{
		for (int ii = 0; ii < na; ii++)
		{
			int re = pos + ii;
			int im = pos + na + ii;
			z1a[ii] = dcomp(fin[re], fin[im]);
		}
		pos += 2 * na;
		for (int ii = 0; ii < na; ii++)
		{
			int re = pos + ii;
			int im = pos + na + ii;
			z2a[ii] = dcomp(fin[re], fin[im]);
		}
		pos += 2 * na;
		for (int ii = 0; ii < na; ii++)
		{
			pa[ii] = fin[pos + ii];
		}
		pos += na;
		for (int ii = 0; ii < na; ii++)
		{
			La[ii] = fin[pos + ii];
		}
		pos += na;
		for (int ii = 0; ii < na; ii++)
		{
			mua[ii] = fin[pos + ii];
		}
		pos += na;
	}
	else
	{
		z1a = { dcomp(0, 0) };
		z2a = { dcomp(0, 0) };
		La = { 0 };
		mua = { 0 };
		a = { { dcomp(0,0) } };
		ma = 1;
		Na = 1;
	}

	// Setting the data types
	double xfrom, xto, yfrom, yto;
	int Nx, Ny, Nw, Ntraj, lvs_traj;
	ddvec grid_11, grid_22, grid_12, grid_1, grid_2, theta_p;
	ddcvec traj_1, traj_2, grid_w, traj_w;
	dvec x_vec, y_vec, x_vecw, y_vecw;
	dcvec xtraj, ytraj;

	// Read the plot data from binary file
	std::ifstream plot_file("plot_data.bin", std::ios::in | std::ios::binary | std::ios::ate);
	std::streampos size2 = plot_file.tellg();
	char* memblock2 = new char[size2];
	plot_file.seekg(0, std::ios::beg);
	plot_file.read(memblock2, size2);
	double* fplot = (double*)memblock2;//reinterpret as doubles

	xfrom = fplot[0];
	xto = fplot[1];
	yfrom = fplot[2];
	yto = fplot[3];
	Nx = (int)fplot[4];
	Ny = (int)fplot[5];
	Nw = (int)fplot[6];
	Ntraj = (int)fplot[7];
	lvs_traj = (int)fplot[8];
	xtraj = { fplot[9] + dcomp(0,1) * fplot[10], fplot[11] + dcomp(0,1) * fplot[12] };
	ytraj = { fplot[13] + dcomp(0,1) * fplot[14], fplot[15] + dcomp(0,1) * fplot[16] };

	// Displying the plot data in the command window
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		THE GEOMETRY DATA	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "This is the retrived geometry data:\n";
	std::cout << "sigma_11inf = " << sigma_11inf << std::endl;
	std::cout << "      kappa = " << kappa << std::endl;
	std::cout << "          G = " << G << std::endl;
	std::cout << "         na = " << na << std::endl;
	std::cout << "         ma = " << ma << std::endl;
	std::cout << "         Na = " << Na << std::endl;
	// Displying the plot data in the command window
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		THE READ PLOT DATA	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "This is the retrived plot data:\n";
	std::cout << "x from: " << xfrom << " to " << xto << std::endl;
	std::cout << "y from: " << yfrom << " to " << yto << std::endl;
	std::cout << "x resolution: " << Nx << std::endl;
	std::cout << "y resolution: " << Ny << std::endl;
	std::cout << "Total number of points: " << Nx * Ny << std::endl;
	std::cout << "Number of steps in trajectories: " << Ntraj << std::endl;
	std::cout << "Number of trajectory levels: " << lvs_traj << std::endl;
	std::cout << "Total number of trajectory points: " << Ntraj * lvs_traj * 4 << std::endl;
	std::cout << "sigma_1 starting line from: " << xtraj[0] << " to " << xtraj[1] << std::endl;
	std::cout << "sigma_2 starting line from: " << ytraj[0] << " to " << ytraj[1] << std::endl;
	std::cout << "x and y quiver resolution: " << Nw << std::endl << std::endl;
	std::cout << std::endl;

	/* --------------------------------------------------------------------
			Solve the system
	-----------------------------------------------------------------------*/
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		INITIALIZING THE SOVLER	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;

	auto start_solv = std::chrono::high_resolution_clock::now();
	a = iterator(cond, NIT, Na, pa, sigma_11inf, z1a, z2a, La, mua, ma, na);
	auto stop_solv = std::chrono::high_resolution_clock::now();
	auto duration_solv = std::chrono::duration_cast<std::chrono::microseconds>(stop_solv - start_solv);

	// Displaying the computation time
	std::cout << std::endl;
	long long ms_solv = duration_solv.count();
	long long s_solv, m_solv, h_solv;
	std::tie(ms_solv, s_solv, m_solv, h_solv) = ms_to_time(ms_solv);
	std::cout << "Computations finnished after ";
	time_print(ms_solv, s_solv, m_solv, h_solv);

	std::cout << std::endl;

	/* --------------------------------------------------------------------
			Checking the error
	-----------------------------------------------------------------------*/
	double error_med_a_re{}, error_mean_a_re{}, error_max_a_re{}, error_med_a_im{}, error_mean_a_im{}, error_max_a_im{}, error_med_int{}, error_mean_int{}, error_max_int{};
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		ERRORS	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;
	int numa;
	numa = (int)round(Na);
	dcvec T_check(numa* na);
	if (na > 0)
	{
		// Calculate the error along the cracks, i.e. T = t_s + i*t_n = 0 + i*p
		dvec T_check_a_re(numa * na), T_check_a_im(numa * na);
		double theta_0a = pi()/numa;
		double delthetaa = (pi() - 2.0*theta_0a) / (numa-1.0);
		for (int ii = 0; ii < na; ii++)
		{
			for (int jj = 0; jj < numa; jj++)
			{
				dcomp z, tau_11, tau_12;
				double theta;
				theta = theta_0a + (jj) * delthetaa;
				z = z_from_chi(exp(dcomp(0, 1) * theta), z1a[ii], z2a[ii], La[ii], mua[ii]);
				T_check[(ii * numa) + jj] = T_total(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1, ii) + dcomp(0, pa[ii]);
				T_check_a_re[(ii * numa) + jj] = abs(real(T_check[(ii * numa) + jj]));
				T_check_a_im[(ii * numa) + jj] = abs(imag(T_check[(ii * numa) + jj]));
			}
		}
		// Median
		size_t n1 = T_check_a_re.size() / 2;
		nth_element(T_check_a_re.begin(), T_check_a_re.begin() + n1, T_check_a_re.end());
		error_med_a_re = T_check_a_re[n1];
		nth_element(T_check_a_im.begin(), T_check_a_im.begin() + n1, T_check_a_im.end());
		error_med_a_im = T_check_a_im[n1];
		// Mean
		error_mean_a_re = 1.0 * std::accumulate(T_check_a_re.begin(), T_check_a_re.end(), 0.0) / (n1 * 2);
		error_mean_a_im = 1.0 * std::accumulate(T_check_a_im.begin(), T_check_a_im.end(), 0.0) / (n1 * 2);
		// Max
		error_max_a_re = *max_element(T_check_a_re.begin(), T_check_a_re.end());
		error_max_a_im = *max_element(T_check_a_im.begin(), T_check_a_im.end());
		// Print the results
		std::cout << "     Analytic Element for a Crack" << std::endl;
		std::cout << "     Difference for ts" << std::endl;
		std::cout << "     Maximum = " << error_max_a_re << std::endl;
		std::cout << "     Mean    = " << error_mean_a_re << std::endl;
		std::cout << "     Median  = " << error_med_a_re << std::endl;
		std::cout << "     Difference for tn" << std::endl;
		std::cout << "     Maximum = " << error_max_a_im << std::endl;
		std::cout << "     Mean    = " << error_mean_a_im << std::endl;
		std::cout << "     Median  = " << error_med_a_im << std::endl;

		// Error at the intersection
		ddcvec zint(na, dcvec(na));
		iivec int_check(na, ivec(na));
		ivec int_count(na);

		// Find the intersection points
		#pragma omp parallel for default(none) shared(zint, int_check, int_count, z1a, z2a)
		for (int ii = 0; ii < na; ii++)
		{
			for (int jj = 0; jj < na; jj++)
			{
				std::tie(zint[ii][jj], int_check[ii][jj]) = intersection_point(z1a[ii], z2a[ii], z1a[jj], z2a[jj]);
			}
			int_count[ii] = std::accumulate(int_check[ii].begin(), int_check[ii].end(), 0.0);
		}
		int int_sum = std::accumulate(int_count.begin(), int_count.end(), 0.0);
		dvec int_error(int_sum);
		int cnt_int = 0;

		// Calcualte the error at the intersections, i.e. real(tau^11) = 0
		if (int_sum > 0)
		{
			for (int ii = 0; ii < na; ii++)
			{
				if (int_count[ii] > 0)
				{
					int cnt_er = 0;
					for (int jj = 0; jj < na; jj++)
					{
						if (int_check[ii][jj] == 1)
						{
							dcomp tau_11, tau_12, T;
							std::tie(tau_11, tau_12) = tau_total(zint[ii][jj], sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1);
							T = T_total(z, sigma_11inf, z1a, z2a, La, mua, ma, na, a, -1, ii);
							std::cout << "z = " << zint[ii][jj] << " tau_11 = " << tau_11 << " tau_12 = " << tau_12 << " T = " << T << std::endl;
							int_error[cnt_int + cnt_er] = abs(real(tau_11));
							cnt_er += 1;
						}
					}
				}
				cnt_int += int_count[ii];
			}

			// Mean
			error_mean_int = std::accumulate(int_error.begin(), int_error.end(), 0.0) / (int_sum);
			// Max
			error_max_int = *max_element(int_error.begin(), int_error.end());
			// Median
			size_t n2 = int_error.size() / 2;
			nth_element(int_error.begin(), int_error.begin() + n2, int_error.end());
			error_med_int = int_error[n2];
			// Print the results
			std::cout << "     Intersection/s:" << std::endl;
			std::cout << "     Maximum = " << error_max_int << std::endl;
			std::cout << "     Mean    = " << error_mean_int << std::endl;
			std::cout << "     Median  = " << error_med_int << std::endl;
		}
	}

	// Create/open the two output files
	std::ofstream outfile_coef("input_data.bin", std::ios::out | std::ios::binary);

	// Save the plot properties
	dvec prop = { sigma_11inf,  kappa, G, 1.0 * na, 1.0 * ma};
	const char* pointerprop = reinterpret_cast<const char*>(&prop[0]);
	std::size_t bytesprop = prop.size() * sizeof(prop[0]);
	outfile_coef.write(pointerprop, bytesprop);

	// saving the coordinates of the cracks
	if (na > 0)
	{
		dvec fz1_re(na);
		dvec fz1_im(na);
		for (int jj = 0; jj < na; jj++)
		{
			fz1_re[jj] = real(z1a[jj]);
			fz1_im[jj] = imag(z1a[jj]);
		}
		const char* pointerz1_re = reinterpret_cast<const char*>(&fz1_re[0]);
		std::size_t bytesz1_re = fz1_re.size() * sizeof(fz1_re[0]);
		outfile_coef.write(pointerz1_re, bytesz1_re);
		const char* pointerz1_im = reinterpret_cast<const char*>(&fz1_im[0]);
		std::size_t bytesz1_im = fz1_im.size() * sizeof(fz1_im[0]);
		outfile_coef.write(pointerz1_im, bytesz1_im);

		dvec fz2_re(na);
		dvec fz2_im(na);
		for (int jj = 0; jj < na; jj++)
		{
			fz2_re[jj] = real(z2a[jj]);
			fz2_im[jj] = imag(z2a[jj]);
		}
		const char* pointerz2_re = reinterpret_cast<const char*>(&fz2_re[0]);
		std::size_t bytesz2_re = fz2_re.size() * sizeof(fz2_re[0]);
		outfile_coef.write(pointerz2_re, bytesz2_re);
		const char* pointerz2_im = reinterpret_cast<const char*>(&fz2_im[0]);
		std::size_t bytesz2_im = fz2_im.size() * sizeof(fz2_im[0]);
		outfile_coef.write(pointerz2_im, bytesz2_im);

		dvec fL = La;
		const char* pointerL = reinterpret_cast<const char*>(&fL[0]);
		std::size_t bytesL = fL.size() * sizeof(fL[0]);
		outfile_coef.write(pointerL, bytesL);

		dvec fmu = mua;
		const char* pointermu = reinterpret_cast<const char*>(&fmu[0]);
		std::size_t bytesmu = fmu.size() * sizeof(fmu[0]);
		outfile_coef.write(pointermu, bytesmu);

		// Save the a
		for (int ii = 0; ii < na; ii++)
		{
			dvec fbeta_re(ma);
			dvec fbeta_im(ma);

			for (int jj = 0; jj < ma; jj++)
			{
				fbeta_re[jj] = real(a[ii][jj]);
				fbeta_im[jj] = imag(a[ii][jj]);
			}
			const char* pointerbeta_re = reinterpret_cast<const char*>(&fbeta_re[0]);
			std::size_t bytesbeta_re = fbeta_re.size() * sizeof(fbeta_re[0]);
			outfile_coef.write(pointerbeta_re, bytesbeta_re);
			const char* pointerbeta_im = reinterpret_cast<const char*>(&fbeta_im[0]);
			std::size_t bytesbeta_im = fbeta_im.size() * sizeof(fbeta_im[0]);
			outfile_coef.write(pointerbeta_im, bytesbeta_im);
		}
	}

	// Estimate the time of the program
	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		ESTIMATED CALCULATION TIME	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;
	int Ne = (Nx + Ny + Ntraj) / 100;
	if (Ne < 8)
	{
		Ne = 8;
	}
	auto start0 = std::chrono::high_resolution_clock::now();
	stress_field(xfrom, xto, yfrom, yto, Ne, Ne, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop0 = std::chrono::high_resolution_clock::now();
	auto start01 = std::chrono::high_resolution_clock::now();
	principal_stress_trajectories(xfrom, xto, yfrom, yto, xtraj, ytraj, Ne, Ne, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop01 = std::chrono::high_resolution_clock::now();
	auto duration0 = std::chrono::duration_cast<std::chrono::microseconds>(stop0 - start0);
	long long ms0 = duration0.count();
	auto duration01 = std::chrono::duration_cast<std::chrono::microseconds>(stop01 - start01);
	long long ms01 = duration01.count();
	long long calcs0 = (int)(2 * Nx * Ny) + (int)(Nw * Nw);
	long long calcs01 = (int)(Ntraj * lvs_traj) + (int)(Ntraj * Nw);
	ms0 = ((ms0 / ((int)Ne * Ne) * calcs0 + ms01 / ((int)Ne * Ne) * calcs01))*0.7;
	long long s0, m0, h0;
	std::tie(ms0, s0, m0, h0) = ms_to_time(ms0);
	std::cout << "Estimated calculation time:  ";
	time_print(ms0, s0, m0, h0);
	std::cout << std::endl << std::endl;
	auto date_time2 = std::chrono::system_clock::now();
	std::time_t start_time2 = std::chrono::system_clock::to_time_t(date_time2);
	char str_time2[26];
	ctime_s(str_time2, sizeof str_time2, &start_time2);
	std::cout << "Plotting started: " << str_time2 << std::endl << std::endl;

	/* --------------------------------------------------------------------
			Calculating the plots
	-----------------------------------------------------------------------*/

	std::cout << "=================================================================" << std::endl << std::endl;
	std::cout << "		COMPUTING THE PLOTS	" << std::endl << std::endl;
	std::cout << "=================================================================" << std::endl << std::endl;

	// Get the Cartisian stress field
	std::cout << "Initiating the stress field calculation\n";
	auto start1 = std::chrono::high_resolution_clock::now();
	std::tie(x_vec, y_vec, grid_11, grid_22, grid_12) = stress_field(xfrom, xto, yfrom, yto, Nx, Ny, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop1 = std::chrono::high_resolution_clock::now();
	auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(stop1 - start1);
	std::cout << "Completed, time taken by function: stress_field = ";
	long long ms1, s1, m1, h1;
	ms1 = duration1.count();
	std::tie(ms1, s1, m1, h1) = ms_to_time(ms1);
	time_print(ms1, s1, m1, h1);
	std::cout << std::endl << std::endl;

	// Get the principal stress field
	std::cout << "Initiating the principal stress field calculation\n";
	auto start2 = std::chrono::high_resolution_clock::now();
	std::tie(grid_1, grid_2, theta_p) = principal_stress_field(Nx, Ny, grid_11, grid_22, grid_12);
	auto stop2 = std::chrono::high_resolution_clock::now();
	auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(stop2 - start2);
	std::cout << "Completed, time taken by function: principal_stress_field = ";
	ms1 = duration2.count();
	std::tie(ms1, s1, m1, h1) = ms_to_time(ms1);
	time_print(ms1, s1, m1, h1);
	std::cout << std::endl << std::endl;

	// Get the stress trajectories
	std::cout << "Initiating the principal stress trajectories calculation\n";
	auto start3 = std::chrono::high_resolution_clock::now();
	std::tie(traj_1, traj_2) = principal_stress_trajectories(xfrom, xto, yfrom, yto, xtraj, ytraj, Ntraj, lvs_traj, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop3 = std::chrono::high_resolution_clock::now();
	auto duration3 = std::chrono::duration_cast<std::chrono::microseconds>(stop3 - start3);
	std::cout << "Completed, time taken by function: principal_stress_trajectories = ";
	ms1 = duration3.count();
	std::tie(ms1, s1, m1, h1) = ms_to_time(ms1);
	time_print(ms1, s1, m1, h1);
	std::cout << std::endl << std::endl;

	// Get the displacement field
	std::cout << "Initiating the displacement field calculation\n";
	auto start4 = std::chrono::high_resolution_clock::now();
	std::tie(x_vecw, y_vecw, grid_w) = w_field(xfrom, xto, yfrom, yto, Nw, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop4 = std::chrono::high_resolution_clock::now();
	auto duration4 = std::chrono::duration_cast<std::chrono::microseconds>(stop4 - start4);
	std::cout << "Completed, time taken by function: w_field = ";
	ms1 = duration4.count();
	std::tie(ms1, s1, m1, h1) = ms_to_time(ms1);
	time_print(ms1, s1, m1, h1);
	std::cout << std::endl << std::endl;

	// Get the displacement trajectories
	std::cout << "Initiating the displacement trajectories calculation\n";
	auto start5 = std::chrono::high_resolution_clock::now();
	traj_w = w_trajectories(xfrom, xto, yfrom, yto, Ntraj, Nw, kappa, G, sigma_11inf, z1a, z2a, La, mua, ma, na, a);
	auto stop5 = std::chrono::high_resolution_clock::now();
	auto duration5 = std::chrono::duration_cast<std::chrono::microseconds>(stop5 - start5);
	std::cout << "Completed, time taken by function: w_trajectories = ";
	ms1 = duration5.count();
	std::tie(ms1, s1, m1, h1) = ms_to_time(ms1);
	time_print(ms1, s1, m1, h1);
	std::cout << std::endl << std::endl;

	// Displaying the computation time
	std::cout << "=================================================================" << std::endl << std::endl;
	long long mstime = duration1.count() + duration2.count() + duration3.count() + duration4.count() + duration5.count();
	long long stime, mtime, htime;
	std::tie(mstime, stime, mtime, htime) = ms_to_time(mstime);
	std::cout << "Total calculation time: ";
	time_print(mstime, stime, mtime, htime);
	std::cout << std::endl << std::endl;

	/* --------------------------------------------------------------------
			Saving the data as binary files
	-----------------------------------------------------------------------*/

	// Create/open the two output files
	std::ofstream outfile("data.bin", std::ios::out | std::ios::binary);
	std::ofstream outfiledim("dim_data.bin", std::ios::out | std::ios::binary);

	// Save the x and y vectors
	dvec fx = x_vec;
	const char* pointerx = reinterpret_cast<const char*>(&fx[0]);
	std::size_t bytesx = fx.size() * sizeof(fx[0]);
	outfile.write(pointerx, bytesx);

	dvec fy = y_vec;
	const char* pointery = reinterpret_cast<const char*>(&fy[0]);
	std::size_t bytesy = fy.size() * sizeof(fy[0]);
	outfile.write(pointery, bytesy);

	dvec fxw = x_vecw;
	const char* pointerxw = reinterpret_cast<const char*>(&fxw[0]);
	std::size_t bytesxw = fxw.size() * sizeof(fxw[0]);
	outfile.write(pointerxw, bytesxw);

	dvec fyw = y_vecw;
	const char* pointeryw = reinterpret_cast<const char*>(&fyw[0]);
	std::size_t bytesyw = fyw.size() * sizeof(fyw[0]);
	outfile.write(pointeryw, bytesyw);

	// Save the grids
	for (size_t ii = 0; ii < grid_11.size(); ii++)
	{
		dvec fg11 = grid_11[ii];
		const char* pointerg11 = reinterpret_cast<const char*>(&fg11[0]);
		std::size_t bytesg11 = fg11.size() * sizeof(fg11[0]);
		outfile.write(pointerg11, bytesg11);
	}
	for (size_t ii = 0; ii < grid_22.size(); ii++)
	{
		dvec fg22 = grid_22[ii];
		const char* pointerg22 = reinterpret_cast<const char*>(&fg22[0]);
		std::size_t bytesg22 = fg22.size() * sizeof(fg22[0]);
		outfile.write(pointerg22, bytesg22);
	}
	for (size_t ii = 0; ii < grid_12.size(); ii++)
	{
		dvec fg12 = grid_12[ii];
		const char* pointerg12 = reinterpret_cast<const char*>(&fg12[0]);
		std::size_t bytesg12 = fg12.size() * sizeof(fg12[0]);
		outfile.write(pointerg12, bytesg12);
	}
	for (size_t ii = 0; ii < grid_1.size(); ii++)
	{
		dvec fg1 = grid_1[ii];
		const char* pointerg1 = reinterpret_cast<const char*>(&fg1[0]);
		std::size_t bytesg1 = fg1.size() * sizeof(fg1[0]);
		outfile.write(pointerg1, bytesg1);
	}
	for (size_t ii = 0; ii < grid_2.size(); ii++)
	{
		dvec fg2 = grid_2[ii];
		const char* pointerg2 = reinterpret_cast<const char*>(&fg2[0]);
		std::size_t bytesg2 = fg2.size() * sizeof(fg2[0]);
		outfile.write(pointerg2, bytesg2);
	}
	for (size_t ii = 0; ii < theta_p.size(); ii++)
	{
		dvec fgtp = theta_p[ii];
		const char* pointergtp = reinterpret_cast<const char*>(&fgtp[0]);
		std::size_t bytesgtp = fgtp.size() * sizeof(fgtp[0]);
		outfile.write(pointergtp, bytesgtp);
	}
	for (size_t ii = 0; ii < traj_1.size(); ii++)
	{
		dvec fgt1_re(Ntraj);
		dvec fgt1_im(Ntraj);

		for (size_t jj = 0; jj < traj_1[0].size(); jj++)
		{
			fgt1_re[jj] = real(traj_1[ii][jj]);
			fgt1_im[jj] = imag(traj_1[ii][jj]);
		}
		const char* pointergt1_re = reinterpret_cast<const char*>(&fgt1_re[0]);
		std::size_t bytesgt1_re = fgt1_re.size() * sizeof(fgt1_re[0]);
		outfile.write(pointergt1_re, bytesgt1_re);
		const char* pointergt1_im = reinterpret_cast<const char*>(&fgt1_im[0]);
		std::size_t bytesgt1_im = fgt1_im.size() * sizeof(fgt1_im[0]);
		outfile.write(pointergt1_im, bytesgt1_im);
	}
	for (size_t ii = 0; ii < traj_2.size(); ii++)
	{
		dvec fgt2_re(Ntraj);
		dvec fgt2_im(Ntraj);
		for (size_t jj = 0; jj < traj_2[0].size(); jj++)
		{
			fgt2_re[jj] = real(traj_2[ii][jj]);
			fgt2_im[jj] = imag(traj_2[ii][jj]);
		}
		const char* pointergt2_re = reinterpret_cast<const char*>(&fgt2_re[0]);
		std::size_t bytesgt2_re = fgt2_re.size() * sizeof(fgt2_re[0]);
		outfile.write(pointergt2_re, bytesgt2_re);
		const char* pointergt2_im = reinterpret_cast<const char*>(&fgt2_im[0]);
		std::size_t bytesgt2_im = fgt2_im.size() * sizeof(fgt2_im[0]);
		outfile.write(pointergt2_im, bytesgt2_im);
	}
	for (size_t ii = 0; ii < grid_w.size(); ii++)
	{
		dvec fgw_re(Nw);
		dvec fgw_im(Nw);
		for (size_t jj = 0; jj < grid_w[0].size(); jj++)
		{
			fgw_re[jj] = real(grid_w[ii][jj]);
			fgw_im[jj] = imag(grid_w[ii][jj]);
		}
		const char* pointergw_re = reinterpret_cast<const char*>(&fgw_re[0]);
		std::size_t bytesgw_re = fgw_re.size() * sizeof(fgw_re[0]);
		outfile.write(pointergw_re, bytesgw_re);
		const char* pointergw_im = reinterpret_cast<const char*>(&fgw_im[0]);
		std::size_t bytesgw_im = fgw_im.size() * sizeof(fgw_im[0]);
		outfile.write(pointergw_im, bytesgw_im);
	}
	for (size_t ii = 0; ii < traj_w.size(); ii++)
	{
		dvec fgtw_re(Ntraj);
		dvec fgtw_im(Ntraj);
		for (size_t jj = 0; jj < traj_w[0].size(); jj++)
		{
			fgtw_re[jj] = real(traj_w[ii][jj]);
			fgtw_im[jj] = imag(traj_w[ii][jj]);
		}
		const char* pointergtw_re = reinterpret_cast<const char*>(&fgtw_re[0]);
		std::size_t bytesgtw_re = fgtw_re.size() * sizeof(fgtw_re[0]);
		outfile.write(pointergtw_re, bytesgtw_re);
		const char* pointergtw_im = reinterpret_cast<const char*>(&fgtw_im[0]);
		std::size_t bytesgtw_im = fgtw_im.size() * sizeof(fgtw_im[0]);
		outfile.write(pointergtw_im, bytesgtw_im);
	}
	if (na > 0)
	{
		dvec fT_res(numa * na);
		dvec fT_ims(numa * na);
		for (int jj = 0; jj < numa * na; jj++)
		{
			fT_res[jj] = real(T_check[jj]);
			fT_ims[jj] = imag(T_check[jj]);
		}
		const char* pointerT_res = reinterpret_cast<const char*>(&fT_res[0]);
		std::size_t bytesT_res = fT_res.size() * sizeof(fT_res[0]);
		outfile.write(pointerT_res, bytesT_res);
		const char* pointerT_ims = reinterpret_cast<const char*>(&fT_ims[0]);
		std::size_t bytesT_ims = fT_ims.size() * sizeof(fT_ims[0]);
		outfile.write(pointerT_ims, bytesT_ims);
	}

	// Save the plot properties
	dvec dim = { 1.0 * Nx, 1.0 * Ny, 1.0 * Nw, 1.0 * Ntraj, 1.0 * lvs_traj, 1.0 * na, error_med_a_re, error_mean_a_re, error_max_a_re, error_med_a_im, error_mean_a_im, error_max_a_im, error_med_int, error_mean_int, error_max_int};
	const char* pointerdim = reinterpret_cast<const char*>(&dim[0]);
	std::size_t bytesdim = dim.size() * sizeof(dim[0]);
	outfiledim.write(pointerdim, bytesdim);

	// saving the coordinates of the cracks
	if (na > 0)
	{
		dvec fz1_res(na);
		dvec fz1_ims(na);
		for (int jj = 0; jj < na; jj++)
		{
			fz1_res[jj] = real(z1a[jj]);
			fz1_ims[jj] = imag(z1a[jj]);
		}
		const char* pointerz1_res = reinterpret_cast<const char*>(&fz1_res[0]);
		std::size_t bytesz1_res = fz1_res.size() * sizeof(fz1_res[0]);
		outfiledim.write(pointerz1_res, bytesz1_res);
		const char* pointerz1_ims = reinterpret_cast<const char*>(&fz1_ims[0]);
		std::size_t bytesz1_ims = fz1_ims.size() * sizeof(fz1_ims[0]);
		outfiledim.write(pointerz1_ims, bytesz1_ims);

		dvec fz2_res(na);
		dvec fz2_ims(na);
		for (int jj = 0; jj < na; jj++)
		{
			fz2_res[jj] = real(z2a[jj]);
			fz2_ims[jj] = imag(z2a[jj]);
		}
		const char* pointerz2_re = reinterpret_cast<const char*>(&fz2_res[0]);
		std::size_t bytesz2_re = fz2_res.size() * sizeof(fz2_res[0]);
		outfiledim.write(pointerz2_re, bytesz2_re);
		const char* pointerz2_im = reinterpret_cast<const char*>(&fz2_ims[0]);
		std::size_t bytesz2_im = fz2_ims.size() * sizeof(fz2_ims[0]);
		outfiledim.write(pointerz2_im, bytesz2_im);

	}

	// Close the output files
	outfile.close();
	outfiledim.close();

	// Get the date and execution time
	auto end = std::chrono::high_resolution_clock::now();
	auto date_time3 = std::chrono::system_clock::now();
	auto elapsed_seconds = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
	long long mseconds = elapsed_seconds.count();
	long long seconds, hours, minutes;
	std::tie(mseconds, seconds, minutes, hours) = ms_to_time(mseconds);
	std::time_t end_time = std::chrono::system_clock::to_time_t(date_time3);
	char str_time[26];
	ctime_s(str_time, sizeof str_time, &end_time);

	std::cout << "Program finnished after ";
	time_print(mseconds, seconds, minutes, hours);
	std::cout << std::endl;
	std::cout << "Output data saved to binary files: data.bin and dim_data.bin" << std::endl;

	return 0;
}


#include "potgen.hpp"
#include "global.hpp"
#include "profiling.hpp"
#include "dynamic_grid.hpp"
#include "fft.hpp"
#include "multiindex.hpp"
#include "discretize.hpp"
#include "randomize.hpp"

#include <array>
#include <cmath>
#include <cassert>
#include <future>
#include <iostream>
#include <boost/throw_exception.hpp>
#include <boost/numeric/ublas/vector.hpp>

// defined here so can be inlined
inline double pow_small(double base, int exponent)
{
    switch(exponent)
    {
        case 0:
            return 1;
        case 1:
            return base;
        case 2:
            return base*base;
        case 3:
            return base*base*base;
        default:
            return std::pow(base, exponent);
    }
}

// -------------------------------------------------------------------------------------------------------------
// calculate the derivative in position space if the fourier transform is given
// this assumes that the original function was defined inside [-1, 1]^N
/// takes order_per_dir by value to make calling from multiple threads easier.
/// threads safe, if \p f_k is not changed when function is run, otherwise undefined behaviour
default_grid calculateDerivative( std::vector<int> order_per_dir, const complex_grid& f_k )
{
    std::size_t dimension = f_k.getDimension();
    // argument checks
    if(order_per_dir.size() != f_k.getDimension())
    THROW_EXCEPTION( std::invalid_argument, "derivation index %1% count does not match data dimension %2%", order_per_dir.size(), f_k.getDimension() );

    /// \todo maybe just enable fft access and disable after the function...
    if( f_k.getAccessMode() != TransformationType::FFT_INDEX )
    THROW_EXCEPTION( std::invalid_argument, "grid is not in fft index mode" );

    /// \todo this might throw. Catch and generate more detailed error message
    auto der_grid = f_k.clone();

    std::size_t total_order = std::accumulate( std::begin(order_per_dir), std::end(order_per_dir), (size_t)0 );
    for( unsigned i = 0; i < dimension; ++i)
    {
        if(order_per_dir[i] < 0)
        THROW_EXCEPTION( std::invalid_argument, "negative order of derivative %1% supplied", i );
    }

    // the actual calculation starts here
    {
        MultiIndex index( dimension );
        for(unsigned i = 0; i < dimension; ++i)
        {
            // get extents returns unsigned ints, so we need to explicitly convert so singed to apply the unary minus.
            index.setLowerBoundAt( i, -(int)(f_k.getExtents()[i]/2) );
            index.setUpperBoundAt( i, (int)f_k.getExtents()[i]/2 );
        }

        PROFILE_BLOCK("derivative calculation");
        complex_t i_factor = std::pow( complex_t(0, pi), total_order );

        for(index.init() ;index.valid(); ++index)
        {
            /// \todo add comment how/why this works
            // f'(k) = i k f(k)
            double r_factor = 1;
            // this is pushed into another function
            for(unsigned dir = 0; dir < dimension; ++dir)
            {
                // if no derivative in that direction, factor is 1 so no computation needed
                if(order_per_dir[dir] == 0)
                    continue;

                r_factor *= pow_small(2*index[dir], order_per_dir[dir]);
            }

            der_grid(index) *= r_factor * i_factor;
        }
    }

    ifft(der_grid);

    default_grid result( f_k.getExtents(), TransformationType::FFT_INDEX );
    std::transform(der_grid.begin(), der_grid.end(), result.begin(), (double(*)(const complex_t&))&std::real);
    return std::move(result);
}

// -------------------------------------------------------------------------------------------------------------
//  first step of potential generation: generate the new potential in k - space
// -------------------------------------------------------------------------------------------------------------
complex_grid generatePotentialInKSpace( std::vector<std::size_t> sizes, std::vector<double> support, correlation_fn cor_fun, const PGOptions& opt )
{
    // create discrete correlation function and fourier transform -> power spectrum
    // load discretized function data into correlation array
    auto grid = discretizeFunctionForFFT(sizes, support, cor_fun);

    /// \todo logging

    // calculate fft of correlation
    fft(grid);

    // calculate randomize potential in momentum space
    // potential is square root of power spectrum
    {
        PROFILE_BLOCK("power spectrum")

        for(auto& v : grid )
        {
            double real = std::real(v);
            /// \todo actually measure the error here and return it in PGResult
            if( real < -1e-5 || std::abs(std::imag(v)) > 1e-5)
            {
                THROW_EXCEPTION( std::runtime_error, "power spectrum contains negative or imaginary components, check correlation function!" );
            }
            // it is faster (and better?) to use v as a nonnegative real here for sqrt calculation
            v = real < 0 ? 0 : std::sqrt(real);
        }
    }

    // randomize phases
    if( opt.randomize )
    {
        randomizePhases(grid, opt.randomSeed);
    }

    return std::move(grid);
}

// -------------------------------------------------------------------------------------------------------------
// takes a potential in k-space and calculates all requested derivatives, stores inside a Potential datatype
/// \todo write tests for this function

void calculateAllDerivatives(Potential &potential, const complex_grid &potential_k, unsigned int max_order)
{
    PROFILE_BLOCK("calculate all derivatives");

    typedef std::future<default_grid> f_type;
    std::vector<f_type> started_tasks;
    std::vector<MultiIndex> task_orders;

    // calculation function
    auto calc_deriv = [&potential_k](MultiIndex order)
    {
        auto deriv = calculateDerivative(order.getAsVector(), potential_k);

        // use same scale factor as for potential
        std::size_t vec_element_count = potential_k.getElementCount();
        double factor = std::sqrt(vec_element_count);
        scaleVectorBy( deriv, factor );
        return std::move(deriv);
    };

    for(MultiIndex order( potential.getDimension(), 0, max_order + 1 ); order.valid(); ++order)
    {
        // check total order of derivative
        std::size_t total_order = order.getAccumulated();
        if(total_order <= max_order && total_order > 0)
        {
            started_tasks.push_back( std::async(std::launch::async, calc_deriv, order) );
            task_orders.push_back( order );
        }
    }

    for(unsigned i = 0; i < task_orders.size(); ++i)
    {
        try
        {
            potential.setDerivative( task_orders[i], std::move(started_tasks[i].get()) );
        } catch (const std::bad_alloc& e)
        {
            std::cerr << "bad alloc called in multi threaded derivative calculation. probably ran out of memory. "
                    "retry as sequential calculation to reduce memory footprint.";

            potential.setDerivative( task_orders[i], std::move( calc_deriv(task_orders[i]) ) );
        }
    }
}

// -------------------------------------------------------------------------------------------------------------

Potential generatePotential( std::vector<std::size_t> sizes, std::vector<double> support, const PGOptions& opt )
{
    Potential res(sizes, std::vector<double>(sizes.size(), 1.0));
    res.setCreationInfo(opt.randomSeed, 3, opt.corrlength);

    // setup threads
    setFFTThreads(opt.numThreads);

    // calculate the potential in k-space
    auto potential_k = generatePotentialInKSpace(sizes, support, opt.cor_fun, opt);

    // calculate derivatives in k-space
    calculateAllDerivatives( res, potential_k, opt.maxDerivativeOrder );

    std::size_t vec_element_count = potential_k.getElementCount();

    // calculate potential in position space
    auto& cpotential_x = potential_k;
    ifft(cpotential_x);

    // this requires additional memory again
    /// \todo actually measure the error here and return it in PGResult
    double averageComplexPart = 0;
    double average = 0;

    default_grid potential_x(sizes, TransformationType::IDENTITY);

    auto it = potential_x.begin();
    for( auto& value : cpotential_x)
    {
        *it = std::real(value);
        ++it;

        average += std::real( value );
        averageComplexPart += std::imag( value );
    }

    average /= vec_element_count;
    averageComplexPart /= vec_element_count;

    // calculate average and shift
    double variance = 0.0;
    for( auto& d : potential_x )
    {
        d -= average;
        variance += d*d;
    }

    if(opt.verbose)
        std::cout << "original quality: " << average << " " << variance << "\n";
    res.scalePotential( std::sqrt(1.0/variance) );

    scaleVectorBy( potential_x, std::sqrt(vec_element_count / variance) );
    // take scaling into account
    if( opt.verbose )
        std::cout << "the average imaginary component in the result was " << averageComplexPart * std::sqrt(vec_element_count / variance)<< "\n";

    res.setPotential( std::move(potential_x) );

    res.setSupport( support );
    return std::move(res);
}


#include <emscripten.h>
#include <emscripten/bind.h>
#include "geometrycentral/surface/manifold_surface_mesh.h"
#include "geometrycentral/utilities/mesh_data.h"
#include "geometrycentral/surface/edge_length_geometry.h"
#include "geometrycentral/surface/heat_method_distance.h"
#include <Eigen/Core>
#include <stdio.h>
#include <iostream>

using namespace geometrycentral;
using namespace geometrycentral::surface;
typedef int iptr_t;
typedef int dptr_t;

// mesh data
static Eigen::MatrixX3i faces;
static Eigen::MatrixX3d edges;
static EdgeData<double> edgeLengths;

// mesh containers
static std::unique_ptr<ManifoldSurfaceMesh> mesh;
static std::unique_ptr<EdgeLengthGeometry> geometry;

// the Heat Method solver
static std::unique_ptr<HeatMethodDistanceSolver> heatSolver;

// the output vertex data
static VertexData<double> distToSource;

// parameters
static double timeStep = 1.0;
static bool robust = false;
static bool verbose = true;

std::string getExceptionMessage(intptr_t exceptionPtr) {
    return std::string(reinterpret_cast<std::exception *>(exceptionPtr)->what());
}

EMSCRIPTEN_BINDINGS(Bindings) {
  emscripten::function("getExceptionMessage", &getExceptionMessage);
};

extern "C" {

  EMSCRIPTEN_KEEPALIVE
  iptr_t allocate_faces(size_t num_faces){
    faces.resize(num_faces, 3);
    edges.resize(num_faces, 3);
    return reinterpret_cast<iptr_t>(&faces(0, 0));
  }
  EMSCRIPTEN_KEEPALIVE
  void set_face(size_t f, size_t idx0, size_t idx1, size_t idx2){
    faces(f, 0) = idx0;
    faces(f, 1) = idx1;
    faces(f, 2) = idx2;
  }
  EMSCRIPTEN_KEEPALIVE
  void set_face_edges(size_t f, double e0, double e1, double e2){
    edges(f, 0) = e0;
    edges(f, 1) = e1;
    edges(f, 2) = e2;
  }
  EMSCRIPTEN_KEEPALIVE
  void print_faces(){
    std::cout << "Faces:\n" << faces << "\n";
  }
  EMSCRIPTEN_KEEPALIVE
  dptr_t get_edge_ptr(){
    return reinterpret_cast<dptr_t>(&edges(0, 0));
  }
  EMSCRIPTEN_KEEPALIVE
  void print_edges(){
    std::cout << "Edges:\n" << edges << "\n";
  }

  EMSCRIPTEN_KEEPALIVE
  iptr_t allocate_edges(size_t num_edges){
    edges.resize(num_edges, 3);
    return reinterpret_cast<iptr_t>(&edges(0, 0));
  }

  EMSCRIPTEN_KEEPALIVE
  void set_verbose(bool v = true){
    verbose = v;
  }
  EMSCRIPTEN_KEEPALIVE
  void set_quiet(){
    set_verbose(false);
  }
  EMSCRIPTEN_KEEPALIVE
  void set_time_step(double step){
    timeStep = step;
  }
  EMSCRIPTEN_KEEPALIVE
  void set_robust(bool flag){
    robust = flag;
  }

  EMSCRIPTEN_KEEPALIVE
  void create_surface_mesh(){
    // create underlying mesh topology
    mesh.reset(new ManifoldSurfaceMesh(faces));
    mesh->compress();
    if(verbose)
      mesh->printStatistics();
  }

  EMSCRIPTEN_KEEPALIVE
  void precompute(){

    // create implicit geometry using edge lengths and mesh
    edgeLengths = EdgeData<double>(*mesh);
    for(size_t i = 0; i < faces.rows(); ++i){
      if(verbose)
        printf("Setting lengths of face #%zu\n", i);
      Face f = mesh->face(i);
      if(!f.isTriangle()){
        printf("Face is not a triangle\n");
        return;
      }
      Halfedge he = f.halfedge(); edgeLengths[he.edge()] = edges(i, 0);
      he = he.next(); edgeLengths[he.edge()] = edges(i, 1);
      he = he.next(); edgeLengths[he.edge()] = edges(i, 2);
    }
    if(verbose){
      std::cout << "Edges:\n" << edgeLengths.raw() << "\n";
      for(Edge e : mesh->edges()){
        printf("Edge #%zu = %g\n", e.getIndex(), edgeLengths[e]);
      }
    }
    geometry.reset(new EdgeLengthGeometry(*mesh, edgeLengths));

    // create heat method distance solver (precomputation happens here)
    heatSolver.reset(new HeatMethodDistanceSolver(*geometry, timeStep, robust));
  }

  EMSCRIPTEN_KEEPALIVE
  dptr_t compute_from_source(size_t srcIndex){
    const Vertex v = mesh->vertex(srcIndex);
    distToSource = heatSolver->computeDistance(v);

    if(verbose)
      printf("Returning result pointer\n");

    Eigen::VectorXd &mat = distToSource.raw();
    double* ptr = &mat(0, 0);
    return reinterpret_cast<dptr_t>(ptr);
  }

}


//
// Copyright (c) 2016,2018 CNRS
//

#ifndef __pinocchio_math_fwd_hpp__
#define __pinocchio_math_fwd_hpp__

#include "pinocchio/fwd.hpp"
#include <math.h>
#include <boost/math/constants/constants.hpp>
#include "pinocchio/math/sincos.hpp"

#ifdef PINOCCHIO_WITH_CPPAD_SUPPORT
namespace boost
{
  namespace math
  {
    namespace constants
    {
      namespace detail
      {
        template<typename Scalar>
        struct constant_pi< CppAD::AD<Scalar> > : constant_pi<Scalar> {};

#if defined(PINOCCHIO_WITH_CPPADCG_SUPPORT) && defined(PINOCCHIO_WITH_CXX11_SUPPORT)
        template<typename Scalar>
        struct constant_pi< CppAD::cg::CG<Scalar> > : constant_pi<Scalar> {};
#endif
      }
    }
  }
}
#endif

namespace pinocchio
{
  ///
  /// \brief Returns the value of PI according to the template parameters Scalar
  ///
  /// \tparam Scalar The scalar type of the return pi value
  ///
  template<typename Scalar>
  const Scalar PI()
  { return boost::math::constants::pi<Scalar>(); }

  /// The value of PI for double scalar type
  const double PId = PI<double>();

  namespace math
  {
    using std::fabs;
    using std::sqrt;
    using std::atan;
    using std::acos;
    using std::asin;
    using std::pow;
    using std::cos;
    using std::sin;

#ifdef PINOCCHIO_WITH_CPPAD_SUPPORT
    using CppAD::fabs;
    using CppAD::sqrt;
    using CppAD::atan;
    using CppAD::acos;
    using CppAD::asin;
    using CppAD::atan2;
    using CppAD::pow;
    using CppAD::cos;
    using CppAD::sin;
#else
    using std::atan2;
#endif
  }
}

#endif //#ifndef __pinocchio_math_fwd_hpp__


#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/iterator.h>
#include <CGAL/point_generators_2.h>

#include <boost/bind.hpp>

typedef CGAL::Exact_predicates_exact_constructions_kernel K;
typedef K::Point_2                     Point;
typedef K::Segment_2                   Segment;

typedef CGAL::Creator_uniform_2<double,Point>              Pt_creator;
typedef CGAL::Random_points_on_segment_2<Point,Pt_creator> P1;
typedef CGAL::Random_points_on_circle_2<Point,Pt_creator>  P2;
typedef CGAL::Creator_uniform_2< Point, Segment>           Seg_creator;
typedef CGAL::Join_input_iterator_2< P1, P2, Seg_creator>  Seg_iterator;

struct Intersector{
  typedef CGAL::cpp11::result_of<K::Intersect_2(Segment,Segment)>::type result_type;
  const Segment& s;
  K::Intersect_2 intersect;

  Intersector(const Segment& seg): s(seg) {}

  result_type
  operator() ( const Segment& other) const
  {
    return intersect(s, other);
  }
};

int main()
{
  std::vector<Segment> input;

  // Prepare point generator for the horizontal segment, length 200.
  P1 p1( Point(-100,0), Point(100,0));

  // Prepare point generator for random points on circle, radius 250.
  P2 p2( 250);

  // Create segments.
  Seg_iterator g( p1, p2);
  std::copy_n( g, 200, std::back_inserter(input));

  // splitting results with Dispatch_output_iterator
  std::vector<Point> points;
  std::vector<Segment> segments;

  typedef CGAL::Dispatch_output_iterator<
    std::tuple<Point,Segment>, std::tuple< std::back_insert_iterator<std::vector<Point> >,
                                               std::back_insert_iterator<std::vector<Segment> > > >
    Dispatcher;

  Dispatcher disp = CGAL::dispatch_output<Point,Segment>( std::back_inserter(points),
                                                          std::back_inserter(segments) );

  // intersects the first segment of input with all other segments
  // The resulting points or segments are written in the vectors with the same names
  std::transform( input.begin(), input.end(), disp,
                  Intersector(input.front()) );

  std::cout << "Point intersections: " << points.size() << std::endl;
  std::cout << "Segment intersections: " << segments.size() << std::endl;

  return 0;
}


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/sqrt1pm1.hpp>
#include <scalar_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>
#include <boost/simd/constant/sqrt_2.hpp>
#include <boost/simd/constant/eps.hpp>
#include <boost/simd/constant/halfeps.hpp>

STF_CASE_TPL (" sqrt1pm1",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::sqrt1pm1;

  using r_t = decltype(sqrt1pm1(T()));

  // return type conformity test
  STF_TYPE_IS(r_t, T);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_ULP_EQUAL(sqrt1pm1(bs::Inf<T>()), bs::Inf<r_t>(), 0);
  STF_ULP_EQUAL(sqrt1pm1(bs::Minf<T>()), bs::Nan<r_t>(), 0);
  STF_ULP_EQUAL(sqrt1pm1(bs::Nan<T>()), bs::Nan<r_t>(), 0);
#endif
  STF_ULP_EQUAL(sqrt1pm1(bs::Mone<T>()), bs::Mone<r_t>(), 0);
  STF_ULP_EQUAL(sqrt1pm1(bs::One<T>()), bs::Sqrt_2<r_t>()-bs::One<r_t>(), 2);
  STF_ULP_EQUAL(sqrt1pm1(bs::Zero<T>()), bs::Zero<r_t>(), 0);
  STF_ULP_EQUAL(sqrt1pm1(bs::Eps<T>()), bs::Halfeps<r_t>(), 0.5);
}


//    Copyright 2017 Rainer Gemulla
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
/** \file
 *
 * Illustrates matrix factorization with SGD. We first creates factors and then a data matrix
 * from these factors. THis process ensures that we know the best factorization of the input.
 * We then try to reconstruct the factors using SGD.
 */
#include <iostream>

#include <boost/math/distributions/normal.hpp>
#include <boost/random/uniform_real.hpp>

#include <util/evaluation.h>
#include <util/io.h>

#include <mf/mf.h>

using namespace std;
using namespace mf;
using namespace rg;
using namespace boost::numeric::ublas;

log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("main"));

int main(int argc, char* argv[]) {
#ifndef NDEBUG
	LOG4CXX_WARN(logger, "Warning: Debug mode activated (runtimes may be slow).");
#endif

	// parameters for the factorization
	mf_size_type size1 = 10000;
	mf_size_type size2 = 10000;
	mf_size_type nnz = 1000000;
	double sigma = 1; // standard deviation
	double lambda = 1/sigma/sigma;
	mf_size_type r = 10;

	// parameters for SGD
	double eps0 = 0.1;
	unsigned epochs = 20;
	SgdOrder order = SGD_ORDER_WOR;
	typedef UpdateTruncate<UpdateNzsl> Update;
	typedef RegularizeL2 Regularize;
	typedef SumLoss<NzslLoss, L2Loss> Loss;
	typedef NzslLoss TestLoss;
	Update update = Update(UpdateNzsl(), -10*sigma, 10*sigma); // truncate for numerical stability
	Regularize regularize = Regularize(lambda);
	Loss loss((NzslLoss()), L2Loss(lambda));
	TestLoss testLoss;
	mf_size_type testNnz = nnz/100;
	BalanceType balanceType = BALANCE_NONE;
	BalanceMethod balanceMethod = BALANCE_SIMPLE;

	// generate original factors by sampling from a normal(0,sigma) distribution
	Random32 random; // note: this takes a default seed (not randomized!)
	DenseMatrix wIn(size1, r);
	DenseMatrixCM hIn(r, size2);
	generateRandom(wIn, random, boost::normal_distribution<>(0, sigma));
	generateRandom(hIn, random, boost::normal_distribution<>(0, sigma));

	// generate a sparse matrix by selecting random entries from the generated factors
	// and add small Gaussian noise
	SparseMatrix v;
	generateRandom(v, nnz, wIn, hIn, random);
	addRandom(v, random, boost::normal_distribution<>(0, 0.1));
	LOG4CXX_INFO(logger, "Data matrix: "
		<< v.size1() << " x " << v.size2() << ", " << v.nnz() << " nonzeros");
	LOG4CXX_INFO(logger, "Loss with original factors: " << loss((FactorizationData<>(v, wIn, hIn))));

	// create a test matrix (without noise)
	SparseMatrix vTest;
	generateRandom(vTest, testNnz, wIn, hIn, random);
	LOG4CXX_INFO(logger, "Test matrix: "
		<< v.size1() << " x " << v.size2() << ", " << vTest.nnz() << " nonzeros");

	// take a small sample and remove empty rows/columns
	ProjectedSparseMatrix Vsample;
	projectRandomSubmatrix(random, v, Vsample, v.size1()/5, v.size2()/5);
	projectFrequent(Vsample, 0);
	LOG4CXX_INFO(logger, "Sample matrix: "
		<< Vsample.data.size1() << " x " << Vsample.data.size2()
		<< ", " << Vsample.data.nnz() << " nonzeros");

	// generate initial factors by sampling from a uniform[-0.5,0.5] distribution
	DenseMatrix w(size1, r);
	DenseMatrixCM h(r, size2);
	generateRandom(w, random, boost::uniform_real<>(-0.5, 0.5));
	generateRandom(h, random, boost::uniform_real<>(-0.5, 0.5));

	// initialize the SGD
	Timer t;
	SgdRunner sgdRunner(random);
	SgdJob<Update,Regularize> job(v, w, h, update, regularize, order);
	FactorizationData<> testJob(vTest, w, h);
	DecayAuto<Update,Regularize,Loss> decay(job, loss, Vsample, eps0, 4);
	Trace trace;

	// print the test loss
	LOG4CXX_INFO(logger, "Initial test loss: "
			<< testLoss(FactorizationData<>(vTest,w,h)));

	// run SGD to try to reconstruct the original factors
	t.start();
	sgdRunner.run(job, loss, epochs, decay, trace, balanceType, balanceMethod, &testJob, &testLoss);
	t.stop();
	LOG4CXX_INFO(logger, "Total time: " << t);

	// print the test loss
	LOG4CXX_INFO(logger, "Final test loss: "
			<< testLoss(FactorizationData<>(vTest,w,h)));

	// write trace to an R file
	LOG4CXX_INFO(logger, "Writing trace to " << "/tmp/sgd-trace.R");
	trace.toRfile("/tmp/sgd-trace.R", "sgd");

	return 0;
}


//////////////////////////////////////////////////////////////////////////////
///   Copyright 2003 and onward LASMEA UMR 6602 CNRS/U.B.P Clermont-Ferrand
///   Copyright 2009 and onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
///
///          Distributed under the Boost Software License, Version 1.0
///                 See accompanying file LICENSE.txt or copy at
///                     http://www.boost.org/LICENSE_1_0.txt
//////////////////////////////////////////////////////////////////////////////
#ifndef NT2_TOOLBOX_IEEE_FUNCTION_SCALAR_NEXTPOW2_HPP_INCLUDED
#define NT2_TOOLBOX_IEEE_FUNCTION_SCALAR_NEXTPOW2_HPP_INCLUDED
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/constant/real.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/meta/adapted_traits.hpp>
#include <boost/fusion/tuple.hpp>

#include <nt2/include/functions/frexp.hpp>
#include <nt2/include/functions/tofloat.hpp>
#include <nt2/include/functions/minusone.hpp>
#include <nt2/include/functions/abs.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                          (A0),
                          (arithmetic_<A0>)
                         )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::nextpow2_(tag::arithmetic_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> :
      meta::as_integer<typename std::tr1::result_of<meta::floating(A0)>::type, signed>{};

    NT2_FUNCTOR_CALL(1)
    {
      return nt2::nextpow2(tofloat(a0));
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is real_
/////////////////////////////////////////////////////////////////////////////
NT2_REGISTER_DISPATCH(tag::nextpow2_, tag::cpu_,
                          (A0),
                          (real_<A0>)
                         )

namespace nt2 { namespace ext
{
  template<class Dummy>
  struct call<tag::nextpow2_(tag::real_),
              tag::cpu_, Dummy> : callable
  {
    template<class Sig> struct result;
    template<class This,class A0>
      struct result<This(A0)> :
      meta::as_integer<typename std::tr1::result_of<meta::floating(A0)>::type, signed>{};

    NT2_FUNCTOR_CALL(1)
    {
      typedef typename meta::as_integer<A0, signed>::type int_type;
      A0 m;
      int_type p;
      boost::fusion::tie(m, p) = nt2::frexp(nt2::abs(a0));
      return (m == Half<A0>())  ? minusone(p) :  p;
    }
  };
} }

#endif
// modified by jt the 26/12/2010


/*
 This program is free software; you can redistribute it and/or modify it under
 the terms of the European Union Public Licence - EUPL v.1.1 as published by
 the European Commission.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE. See the European Union Public Licence - EUPL v.1.1
 for more details.

 You should have received a copy of the European Union Public Licence - EUPL v.1.1
 along with this program.

 Further information about the European Union Public Licence - EUPL v.1.1 can
 also be found on the world wide web at http://ec.europa.eu/idabc/eupl

*/

/*
 ------ Copyright (C) 2011 STA Steering Board (space.trajectory.analysis AT gmail.com) ----
*/
/*
------------------ Author: Guillermo Ortega  ----------------------------------------
 May 2011

 */

#include "serviceDistanceRateUnit.h"

#include <Eigen/Core>

using namespace Eigen;

#include "QDebug"

DialogServiceDistanceRateUnitFrame::DialogServiceDistanceRateUnitFrame( QWidget * parent, Qt::WindowFlags f) : QFrame(parent,f)
{
	setupUi(this);
    distanceRateUnitWidget = DialogServiceDistanceRateUnitFrame::comboBoxDistanceRateUnitsChoice;
    myPastUnits = 0;
    comboBoxDistanceRateUnitsChoice->setCurrentIndex(myPastUnits);
}

DialogServiceDistanceRateUnitFrame::~DialogServiceDistanceRateUnitFrame()
{
}

// Index meaning is as follows:
// index = 0  is Kilometers/s
// index = 1  is meters/s
// index = 2  is centi-meters/s
// index = 3  is mili-meters/s
// index = 4 is Astronomical Units/s

// Matrix coefficients as follows
//  Km->Km, Km->m, Km->cm, Km->mm, Km->AU,
//  m->Km,   m->m,  m->cm,  m->mm,  m->AU,
//  cm->Km, cm->m, cm->cm, cm->mm, cm-> AU,
//  mm->Km, mm->m, mm->cm, mm->mm, mm->AU,
//  AU->Km, AU->m, AU->cm, AU->mm, AU->AU
// The matrice has to be transposed !!

static double distanceRateConversionMatrixCoeffs[25] =
{1.0,            0.001,           0.00001,        0.000001,       1.495978707e+08,
 1000.0,         1.0,             0.01,           0.001,          1.495978707e+11,
 100000.0,       100.0,           1.0,            0.1,            1.495978707e+13,
 1000000.0,      1000.0,          10.0,           1.0,            1.495978707e+14,
 0.6684587e-19,  0.6684587e-12,   0.6684587e-13,  0.6684587e-16,  1.0};

static const Matrix<double, 5, 5> distanceRateConversionMatrix(distanceRateConversionMatrixCoeffs);

double DialogServiceDistanceRateUnitFrame::convertDistanceRate(int fromDistanceRateUnit, int toDistanceRateUnit, double distance)
{
    double finalDistanceRate = distance * distanceRateConversionMatrix(fromDistanceRateUnit, toDistanceRateUnit);
    return finalDistanceRate;
}

//// Sets the input distance, the output distance and the current index inside the method
void DialogServiceDistanceRateUnitFrame::setInputDistanceRate(double niceInputDistanceRate)
{
    myPastDistanceRate = niceInputDistanceRate;
}

// Index meaning is as follows:
// index = 0  is Kilometers/s
// index = 1  is meters/s
// index = 2  is centi-meters/s
// index = 3  is mili-meters/s
// index = 4 is Astronomical Units/s
void DialogServiceDistanceRateUnitFrame::on_comboBoxDistanceRateUnitsChoice_currentIndexChanged(int myIndex)
{
    myFutureUnits = myIndex;
    myFutureDistanceRate = convertDistanceRate(myPastUnits, myFutureUnits, myPastDistanceRate);
    myPastDistanceRate = myFutureDistanceRate;
    myPastUnits = myFutureUnits;
    myRealDistanceRateForXMLSchema = convertDistanceRate (myPastUnits, 0, myFutureDistanceRate);
}


/*    Copyright (c) 2010-2018, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 *
 *    References
 *      Montenbruck, O., Gill, E. Satellite Orbits: Models, Methods, Applications, Springer, 2005.
 *      The Mathworks, Inc. DOPRI78, Symbolic Math Toolbox, 2012.
 *
 *    Notes
 *      All the test for this integrator are based on the data generated using the Symbolic Math
 *      Toolbox (MathWorks, 2012). Ideally, another source of data should be used to complete the
 *      testing.
 *
 *      The single step and full integration error tolerances were picked to be as small as
 *      possible, without causing the tests to fail. These values are not deemed to indicate any
 *      bugs in the code; however, it is important to take these discrepancies into account when
 *      using this numerical integrator.
 *
 */

#define BOOST_TEST_MAIN

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

#include "Tudat/Mathematics/NumericalIntegrators/rungeKuttaVariableStepSizeIntegrator.h"
#include "Tudat/InputOutput/matrixTextFileReader.h"
#include "Tudat/Mathematics/NumericalIntegrators/numericalIntegrator.h"
#include "Tudat/Mathematics/NumericalIntegrators/reinitializableNumericalIntegrator.h"
#include "Tudat/Mathematics/NumericalIntegrators/rungeKuttaCoefficients.h"
#include "Tudat/Mathematics/NumericalIntegrators/UnitTests/numericalIntegratorTests.h"
#include "Tudat/Mathematics/NumericalIntegrators/UnitTests/numericalIntegratorTestFunctions.h"

#include "Tudat/InputOutput/basicInputOutput.h"
#include "Tudat/Mathematics/BasicMathematics/linearAlgebra.h"

#include <limits>
#include <string>

#include <Eigen/Core>

namespace tudat
{
namespace unit_tests
{

BOOST_AUTO_TEST_SUITE( test_runge_kutta_87_dormand_and_prince_integrator )

using linear_algebra::flipMatrixRows;

using numerical_integrators::NumericalIntegratorXdPointer;
using numerical_integrators::ReinitializableNumericalIntegratorXdPointer;
using numerical_integrators::RungeKuttaVariableStepSizeIntegratorXd;
using numerical_integrators::RungeKuttaCoefficients;

using numerical_integrator_test_functions::computeNonAutonomousModelStateDerivative;

//! Test Runge-Kutta 87 Dormand-Prince integrator using benchmark data from (The MathWorks, 2012).
BOOST_AUTO_TEST_CASE( testRungeKutta87DormandAndPrinceIntegratorUsingMatlabData )
{
    using namespace numerical_integrator_tests;

    // Read in benchmark data (generated using Symbolic Math Toolbox in Matlab
    // (The MathWorks, 2012)). This data is generated using the DOPRI78 numerical integrator.
    const std::string pathToForwardIntegrationOutputFile = input_output::getTudatRootPath( )
            + "/Mathematics/NumericalIntegrators/UnitTests"
            + "/matlabOutputRungeKutta87DormandPrinceForward.txt";
    const std::string pathToDiscreteEventIntegrationOutputFile = input_output::getTudatRootPath( )
            + "/Mathematics/NumericalIntegrators/UnitTests"
            + "/matlabOutputRungeKutta87DormandPrinceDiscreteEvent.txt";

    // Store benchmark data in matrix.
    const Eigen::MatrixXd matlabForwardIntegrationData =
            input_output::readMatrixFromFile( pathToForwardIntegrationOutputFile, "," );
    Eigen::MatrixXd matlabBackwardIntegrationData = matlabForwardIntegrationData;
    flipMatrixRows( matlabBackwardIntegrationData );
    const Eigen::MatrixXd matlabDiscreteEventIntegrationData =
            input_output::readMatrixFromFile( pathToDiscreteEventIntegrationOutputFile, "," );

    // Set integrator parameters.

    // All of the following parameters are set such that the input data is fully accepted by the
    // integrator, to determine the steps to be taken.
    const double zeroMinimumStepSize = std::numeric_limits< double >::epsilon( );
    const double infiniteMaximumStepSize = std::numeric_limits< double >::infinity( );
    const double infiniteRelativeErrorTolerance = std::numeric_limits< double >::infinity( );
    const double infiniteAbsoluteErrorTolerance = std::numeric_limits< double >::infinity( );

    // The following parameters set how the error control mechanism should work.
    const double relativeErrorTolerance = 1.0e-15;
    const double absoluteErrorTolerance = 1.0e-15;

    // Case 1: Execute integrateTo() to integrate one step forward in time.
    {
        // Declare integrator with all necessary settings.
        NumericalIntegratorXdPointer integrator
                = std::make_shared< RungeKuttaVariableStepSizeIntegratorXd >(
                    RungeKuttaCoefficients::get( RungeKuttaCoefficients::rungeKutta87DormandPrince ),
                    &computeNonAutonomousModelStateDerivative,
                    matlabForwardIntegrationData( FIRST_ROW, TIME_COLUMN_INDEX ),
                    ( Eigen::VectorXd( 1 )
                      << matlabForwardIntegrationData( FIRST_ROW,
                                                       STATE_COLUMN_INDEX ) ).finished( ),
                    zeroMinimumStepSize,
                    infiniteMaximumStepSize,
                    infiniteRelativeErrorTolerance,
                    infiniteAbsoluteErrorTolerance );

        executeOneIntegrateToStep( matlabForwardIntegrationData, 1.0e-15, integrator );
    }

    // Case 2: Execute performIntegrationStep() to perform multiple integration steps until final
    //         time.
    {
        // Declare integrator with all necessary settings.
        NumericalIntegratorXdPointer integrator
                = std::make_shared< RungeKuttaVariableStepSizeIntegratorXd >(
                    RungeKuttaCoefficients::get( RungeKuttaCoefficients::rungeKutta87DormandPrince ),
                    &computeNonAutonomousModelStateDerivative,
                    matlabForwardIntegrationData( FIRST_ROW, TIME_COLUMN_INDEX ),
                    ( Eigen::VectorXd( 1 )
                      << matlabForwardIntegrationData( FIRST_ROW,
                                                       STATE_COLUMN_INDEX ) ).finished( ),
                    zeroMinimumStepSize,
                    infiniteMaximumStepSize,
                    infiniteRelativeErrorTolerance,
                    infiniteAbsoluteErrorTolerance );

        performIntegrationStepToSpecifiedTime( matlabForwardIntegrationData,
                                               1.0e-15, 1.0e-15, integrator );
    }

    // Case 3: Execute performIntegrationStep() to perform multiple integration steps until initial
    //         time (backwards).
    {
        // Declare integrator with all necessary settings.
        NumericalIntegratorXdPointer integrator
                = std::make_shared< RungeKuttaVariableStepSizeIntegratorXd >(
                    RungeKuttaCoefficients::get( RungeKuttaCoefficients::rungeKutta87DormandPrince ),
                    &computeNonAutonomousModelStateDerivative,
                    matlabBackwardIntegrationData( FIRST_ROW, TIME_COLUMN_INDEX ),
                    ( Eigen::VectorXd( 1 )
                      << matlabBackwardIntegrationData( FIRST_ROW,
                                                        STATE_COLUMN_INDEX ) ).finished( ),
                    zeroMinimumStepSize,
                    infiniteMaximumStepSize,
                    infiniteRelativeErrorTolerance,
                    infiniteAbsoluteErrorTolerance );

        performIntegrationStepToSpecifiedTime( matlabBackwardIntegrationData,
                                               1.0e-15, 1.0e-14, integrator );
    }

    // Case 4: Execute integrateTo() to integrate to specified time in one step.
    {
        // Note that this test has a strange issue that the if the absolute error tolerance is set
        // to 1.0e-15, the last step that the integrateTo() function takes does not result in the
        // expected final time of 1.0. As a temporary solution, the absolute error tolerance has
        // been multiplied by 10.0, which seems to solve the problem. This error indicated a
        // possible problem with the implementation of the integrateTo() function, which needs to
        // be investigated in future.

        // Declare integrator with all necessary settings.
        NumericalIntegratorXdPointer integrator
                = std::make_shared< RungeKuttaVariableStepSizeIntegratorXd >(
                    RungeKuttaCoefficients::get( RungeKuttaCoefficients::rungeKutta87DormandPrince ),
                    &computeNonAutonomousModelStateDerivative,
                    matlabForwardIntegrationData( FIRST_ROW, TIME_COLUMN_INDEX ),
                    ( Eigen::VectorXd( 1 )
                      << matlabForwardIntegrationData( FIRST_ROW,
                                                       STATE_COLUMN_INDEX ) ).finished( ),
                    zeroMinimumStepSize,
                    infiniteMaximumStepSize,
                    relativeErrorTolerance,
                    absoluteErrorTolerance * 10.0 );

        executeIntegrateToToSpecifiedTime( matlabForwardIntegrationData, 1.0e-15, integrator,
                                           matlabForwardIntegrationData(
                                               matlabForwardIntegrationData.rows( ) - 1,
                                               TIME_COLUMN_INDEX ) );
    }

    // Case 5: Execute performIntegrationstep() to integrate to specified time in multiple steps,
    //         including discrete events.
    {
        // Declare integrator with all necessary settings.
        ReinitializableNumericalIntegratorXdPointer integrator
                = std::make_shared< RungeKuttaVariableStepSizeIntegratorXd >(
                    RungeKuttaCoefficients::get( RungeKuttaCoefficients::rungeKutta87DormandPrince ),
                    &computeNonAutonomousModelStateDerivative,
                    matlabForwardIntegrationData( FIRST_ROW, TIME_COLUMN_INDEX ),
                    ( Eigen::VectorXd( 1 )
                      << matlabForwardIntegrationData( FIRST_ROW,
                                                       STATE_COLUMN_INDEX ) ).finished( ),
                    zeroMinimumStepSize,
                    infiniteMaximumStepSize,
                    infiniteRelativeErrorTolerance,
                    infiniteAbsoluteErrorTolerance );

        performIntegrationStepToSpecifiedTimeWithEvents( matlabDiscreteEventIntegrationData,
                                                         1.0e-15, 1.0e-12, integrator );
    }
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests
} // namespace tudat