#include <iostream>
#include <fstream>
#include <vector>
#include <Eigen/Dense>

using namespace std;
using namespace Eigen;

// lee la matriz desde el archivo de texto path. Números separados por espacios
MatrixXd load_matrix(string path) {
	vector <double> raw_data;
	ifstream file(path);
	string line, element;
	stringstream ss;
	int rows=0,cols=0;

	while ( getline(file,line) ) { // leer línea del archivo
		ss = stringstream(line); // convertir la línea en un stream
		while( ss >> element ) { // leer cada elemento de la línea, separado por espacios
			raw_data.push_back(stod(element)); // convertir a double y echar al vector
		}
		rows++; // por cada fila terminada se aumenta el número de filas
	}
	file.close();
	cols = raw_data.size()/rows;
	return Map<Matrix<double, Dynamic, Dynamic, RowMajor>>(raw_data.data(),rows,cols);
}

// guarda la matriz como un archivo de texto en path. Números separados por espacios
void save_matrix(string path, MatrixXd matrix) {
	ofstream file(path);
	file << matrix; // echar matriz al archivo
	file.close();
}

// retorna una copia de la matriz M sin la fila dada por el índice
MatrixXd remove_row(MatrixXd M, int remove_index) {
	int rows = M.rows()-1;
	int cols = M.cols();
	M.block(remove_index,0,rows-remove_index,cols) = M.bottomRows(rows-remove_index);
	M.conservativeResize(rows,cols);
	return M;
}
// retorna una copia de la matriz M sin la columna dada por el índice
MatrixXd remove_col(MatrixXd M, int remove_index) {
	int rows = M.rows();
	int cols = M.cols()-1;
	M.block(0,remove_index,rows,cols-remove_index) = M.rightCols(cols-remove_index);
	M.conservativeResize(rows,cols);
	return M;
}
// retorna una copia de la matriz M sin la columna J y sin la fila I
MatrixXd minor_matrix(MatrixXd M, int I, int J) {
	return remove_col(remove_row(M,I),J);
}

// calcula el determinante de una matriz cuadrada M
double det(MatrixXd M){
	int rows = M.rows();
	int cols = M.cols();

	double d = 0.0;

	if (rows != cols) {
		cerr << "cannot take det of non-square matrix:" << endl << M << endl;
		abort();
	}
	else if (rows == 1 && cols == 1) {
		d = M(0,0);
	} else {
		for(int j=0; j<cols; j++){
			d += pow((-1.0),j) * M(0,j) * det(minor_matrix(M,0,j));
		}
	}
	return d;
}

// retorna el cofactor i,j de la matriz M
double cofactor(MatrixXd M, int i, int j){
	return pow(-1.0,i+j)*det(minor_matrix(M,i,j));
}

MatrixXd cramer_inverse(MatrixXd M) {
	double M_det = det(M);
	if (M_det == 0) {
		cout << "cannot take inverse of singular matrix:" << endl << M << endl;
		abort();
	}
	MatrixXd Adj(M); // Adjugate matrix
	int n = M.rows();

	for (int i=0; i<n; i++) {
		for (int j=0; j<n; j++) {
			Adj(i,j) = cofactor(M,j,i); // note j,i, the transpose of the cofactor matrix
		}
	}

	return Adj/M_det;
}

// matriz elemental nxn para cambiar la fila i por la j
MatrixXd elemental_swap(int i, int j, int n){
	MatrixXd E = MatrixXd::Identity(n,n);
	E(i,i) = 0.;
	E(j,j) = 0.;
	E(i,j) = 1.;
	E(j,i) = 1.;
	return E;
}
// matriz elemental nxn para multiplicar la fila i por la constante k
MatrixXd elemental_multiplication(double k, int i, int n){
	MatrixXd E = MatrixXd::Identity(n,n);
	E(i,i) = k;
	return E;
}
// matriz elemental nxn para sumar k veces la fila j a la fila i
MatrixXd elemental_addition(double k, int i, int j, int n){
	MatrixXd E = MatrixXd::Identity(n,n);
	E(i,j) = k;
	return E;
}

MatrixXd gauss_jordan_inverse(MatrixXd M) {
	if (det(M) == 0) {
		cout << "cannot take inverse of singular matrix:" << endl << M << endl;
		abort();
	}
	int n = M.rows();
	int k;
	MatrixXd E = MatrixXd::Identity(n,n);
	MatrixXd E_new;

	// grant pivots
	for (int i=0; i<n; i++) {
		// k is the first row that is nonzero on column i
		k = i;
		while (M(k,i) == 0.0) { k++;}
		E_new = elemental_swap(k,i,n);
		E = E_new * E;
		M = E_new * M;
	}
	// put in triangular form
	for (int j=0; j<n; j++) {
		for (int i=j+1; i<n; i++) {
			if (M(i,j) != 0.0) {
				E_new = elemental_addition(-M(i,j)/M(j,j),i,j,n);
				E = E_new * E;
				M = E_new * M;
			}
		}
	}
	// force ones in diagonal
	for (int i=0; i<n; i++) {
		E_new = elemental_multiplication(1/M(i,i),i,n);
		E = E_new * E;
		M = E_new * M;
	}
	// backsubstitute
	for (int j=n-1; j>=0; j--) {
		for (int i=0; i<j; i++) {
			//cout << i << " " << j << endl;
			E_new = elemental_addition(-M(i,j),i,j,n);
			E = E_new * E;
			M = E_new * M;
		}
	}

	return E;
}

int main(int argc, char *argv[]) {

	string input_filename;

	if (argc == 2) {
		input_filename = (string)argv[1];
	} else {
		cout << "Insert filename for input matrix: ";
		cin >> input_filename;
	}

	MatrixXd m = load_matrix(input_filename);

	save_matrix("eigen.dat",m.inverse());
	save_matrix("gauss_jordan.dat",gauss_jordan_inverse(m));
	save_matrix("cramer.dat",cramer_inverse(m));
}


#include <iostream>
#include <vector>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/assignment.hpp>
#include <boost/math/special_functions/binomial.hpp>

namespace recursionAlgorithms
{
      using namespace std;
      using namespace boost::numeric::ublas;

      /*9.1
            count ways to cover N steps by hopping either
            1,2 or 3 steps at a time
      */
      int waysToHopSlow(int N)
      {
            if (N==1 || N==0) { return 1; }
            if (N<0) { return 0; }
            return waysToHopSlow(N-1)+waysToHopSlow(N-2)+waysToHopSlow(N-3);
      };

      int waysToHopFast(int N, std::vector<int> &cache)
      {
            if (N==0 || N==1) { return 1; }
            if (N<0) { return 0; }
            if (cache[N]>0) { return cache[N]; }
            cache[N] = waysToHopFast(N-1,cache)
                        + waysToHopFast(N-2,cache)
                        + waysToHopFast(N-3,cache);
            return cache[N];
      };

      /*9.2
            Return the number of ways connecting the top-left corner
            of a MxN matrix/grid to the bottom-right corner,
            going only downward and rightward.
            Off-limits grid points are allow by specifying a -1 value
            in the cache input matrix
      */
      int ways2D(int r, int c, matrix<int> &cache)
      {
            if (r == cache.size1()-1 && c == cache.size2()-1 ) { return 1;}
            if (r >= cache.size1() || c >= cache.size2() || cache(r,c) == -1)
            { return 0; }
            if (cache(r,c)>0) { return cache(r,c); }

            cache(r,c) = ways2D(r,c+1,cache) + ways2D(r+1,c,cache);
            return cache(r,c);
      };

      template <typename dataType>
      void printMatrix(matrix<dataType> &mat)
      {
            for (int row=0;row<mat.size1();row++)
            {
                  for (int col=0;col<mat.size2()-1;col++)
                  { cout << mat(row,col) << " "; }
                  cout << mat(row,mat.size2()-1) << endl;
            }
      };
};

int main()
{
      using namespace recursionAlgorithms;
      using namespace boost::math;

      //matrix<int> input(12,1);
      //input <<= 1,0,1,2,2,1,3,3,3,0,10,11;

      // test 9.1
      //int Nin = 35;
      //std::vector<int> cache(Nin,0);
      //cout << waysToHopSlow(Nin) << endl;
      //cout << waysToHopFast(Nin,cache) << endl;

      // test 9.2
      int M,N;
      M = 10; N= 10;
      matrix<int> input(M,N);
      //input(1,1) = -1;
      printMatrix(input);
      cout << "calculated number of ways = "<< ways2D(0,0,input) << endl;
      cout << "true number of ways = "  << binomial_coefficient<double>(M+N-2,M-1) << endl;
      printMatrix(input);

      return 0;
}


#include <iostream>

#include <Eigen/Dense>
#include <unsupported/Eigen/FFT>

const std::complex<double> I(0,1); // imaginary unit
const double PI = 3.14159265359;

struct Newton {
	Newton(const Eigen::VectorXd &x) : _x(x), _a(x.size()) { }
	void Interpolate(const Eigen::VectorXd &y);
	double operator()(double x) const;

private:
	Eigen::VectorXd _x;	// nodes
	Eigen::VectorXd _a;	// coefficients
};

// Compute the coefficients in the Newton basis.
void Newton::Interpolate(const Eigen::VectorXd &y) {
	int size = _x.size();

	Eigen::MatrixXd M = Eigen::MatrixXd::Zero(size, size);
	M.col(0) = Eigen::VectorXd::Ones(size);

	for(int col = 1; col < size; col++) {
		for(int row = col; row < size; row++) {
			M(row, col) = M(row, col - 1) * (_x(row) - _x(col - 1));
		}
	}

	_a = M.triangularView<Eigen::Lower>().solve(y);
}

// Evaluate the interpolant at x.
double Newton::operator()(double x) const {
	double p = 0;

	for(int i = _x.size() - 1; i >= 0; i--) {
		p = p * (x - _x(i)) + _a(i);
	}

	return p;
}

struct Lagrange {
	Lagrange(const Eigen::VectorXd &x);
	void Interpolate(const Eigen::VectorXd &y) { _y = y; }
	double operator()(double x) const;

private:
	Eigen::VectorXd _x;	// nodes
	Eigen::VectorXd _l;	// weights
	Eigen::VectorXd _y;	// coefficients
};

// Compute the weights l for given nodes x.
Lagrange::Lagrange(const Eigen::VectorXd &x) : _x(x), _l(x.size()), _y(x.size()) {
	for(int i = 0; i < x.size(); i++) {
		double lambda_i = 1;

		for(int j = 0; j < x.size(); j++) {
			if(i != j) {
				lambda_i *= (x(i) - x(j));
			}
		}

		_l(i) = 1 / lambda_i;
	}
}

// Evaluate the interpolant at x.
double Lagrange::operator()(double x) const {
	double p = 0;
	double omega = 1;

	for(int i = 0; i < _x.size(); i++) {
		omega *= (x - _x(i));
	}

	for(int i = 0; i < _x.size(); i++) {
		p += _y(i) * omega * _l(i) / (x - _x(i));
	}

	return p;
}

struct Chebychev {
	Chebychev(const Eigen::VectorXd &x) : _x(x), _a(x.size()) {};
	void Interpolate(const Eigen::VectorXd &y);
	double operator()(double x) const;

private:
	Eigen::VectorXd _x; // nodes
	Eigen::VectorXd _a; // coefficients
};

void Chebychev::Interpolate(const Eigen::VectorXd &y) {
	// the lecture notes model the data output points as [y_0, y_1, ..., y_n]^T
	int n = y.size() - 1;
	Eigen::VectorXcd b(2*n + 2);

	for(int k = 0; k < b.size(); k++) {
		// z_k from lecture notes (5.40)
		double z_k = k > n ? y(2 * n + 1 - k) : y(k);

		// b(k) from lecture notes (5.41)
		b(k) = z_k * std::exp(-PI * n * k / (n + 1) * I);
	}

	Eigen::FFT<double> fft;
	Eigen::VectorXcd c = fft.inv(b);
	Eigen::VectorXd beta(c.size());

	// extract beta's according to lecture notes (5.41) and index substitution k = j + n
	// to only deal with non-negative indices
	for(int k = 0; k < c.size(); k++) {
		// possible error in lecture notes should be j = -n, ..., n + 1 instead of j = 0, ..., 2n + 1
		beta(k) = (c(k) * std::exp(PI * (k - n) / (2 * (n + 1)) * I)).real();
	}

	// from lecture notes (5.38)
	// we have to acces beta(j + n) because of the substitution we did above
	// at the end we are only interested in alpha_j's from j = 0,...,n like in (5.32)
	for(int j = 0; j <= n; j++) {
		if(j == 0) {
			_a(j) = beta(j + n);
		}
		else {
			_a(j) = 2 * beta(j + n);
		}
	}
}

// Evaluate the interpolant at x.
double Chebychev::operator()(double x) const {
	// Clenshaw algorithm from lecture notes (5.35) and (5.36)
	int n = _a.size() - 1;
	Eigen::VectorXd beta(n + 3);
	beta(n + 2) = 0;
	beta(n + 1) = 0;

	for(int k = n; k >= 0; k--) {
		beta(k) = (k == 0 ? 2 : 1) * _a(k) + 2 * x * beta(k + 1) - beta(k + 2);
	}

	return 0.5 * (beta(0) - beta(2));
}

// Runge function
Eigen::VectorXd r(const Eigen::VectorXd &x) {
	return (1.0 / (1.0 + 25.0 * x.array() * x.array())).matrix();
}

int main() {
	int n = 5;

	/*
	Eigen::VectorXd x;
	x.setLinSpaced(5, -1.0, 1.0);
	Eigen::VectorXd y = r(x);

	Newton p(x);
	p.Interpolate(y); // correct result: p._a = [0.0384615, 0.198939, 1.5252, -3.31565, 3.31565]

	Lagrange q(x);    // correct result: p._l = [0.666667, -2.66667, 4, -2.66667, 0.666667]
	q.Interpolate(y);
	*/

	// Use Chebychev nodes instead of linearly spaced nodes
	Eigen::VectorXd x(n + 1);

	for(int i = 0; i < x.size(); i++) {
		x(i) = std::cos((2 * i + 1) * PI / (2 * (n + 1)));
	}

	Eigen::VectorXd y = r(x);

	Newton p(x);
	p.Interpolate(y);

	Lagrange q(x);
	q.Interpolate(y);

	Chebychev c(x);
	c.Interpolate(y);

	// Compute difference of p and q.
	int m = 22;
	double offset = 0.08333333333;
	x.setLinSpaced(m, -1.0 + offset, 1.0 - offset);

	double norm2 = .0;
	for (int i = 0; i < m; ++i) {
		double d = p(x(i)) - q(x(i));
		norm2 += d * d;
	}

	// By uniquenss of the interpolation polynomial, we expect p = q.
	std::cout << "This number should be close to zero: " << norm2 << std::endl;

	norm2 = 0;
	for(int i = 0; i < m; ++i) {
		double d = c(x(i)) - p(x(i));
		norm2 += d * d;
	}

	// By uniquenss of the interpolation polynomial, we expect c = p.
	std::cout << "This number should be close to zero: " << norm2 << std::endl;

	norm2 = 0;
	for(int i = 0; i < m; ++i) {
		double d = c(x(i)) - q(x(i));
		norm2 += d * d;
	}

	// By uniquenss of the interpolation polynomial, we expect c = q.
	std::cout << "This number should be close to zero: " << norm2 << std::endl;

	return 0;
}


/*
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
*/
#include <iostream>
#include <math.h>
#include <Eigen/Dense>

long long int n_max = 20;

long long int calc_gcd(long long int a, long long int b) {
    while (b != 0) {
        long long int remainder = b;
        b = a % b;
        a = remainder;
    }
    return a;
}

long long int calc_lcm(long long int a, long long int b) {
    return a*b/calc_gcd(a, b);
}

int main()
{
    long long int result = 1;
    for (long long int i = 1; i <= n_max; i++) {
        result = calc_lcm(result, i);
        std::cout << "i: " << i << " gcd: " << calc_gcd(result, i) << std::endl;
        std::cout << "i: " << i << " lcm: " << result << std::endl;
    }

    std::cout << result << std::endl;
}


/*
 * Asignatura: Métodos Computacionales en Ciencias
 * Nombre: María Camila Casas Díaz
 * Código: 201813057
 */

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <Eigen/Dense>

using namespace Eigen;
using namespace std;

MatrixXd LeerMatrizIngresada(string path) {
	vector <double> data;
	fstream file(path);
	string line, value;
	int filas=0;
	int columnas=0;

	while (getline(file,line)) {
		stringstream ss (line);
		while( ss >> value ) {
			data.push_back(stod(value));
		}
		filas++;
	}
	file.close();
	columnas = data.size()/filas;
	return Map<Matrix<double,Dynamic,Dynamic,RowMajor>>(data.data(),filas,columnas);
}

double determinante(MatrixXd Matriz) {
	double det = Matriz.determinant();
	return det;
}

MatrixXd MatrizFilaElimiadaI(MatrixXd Matriz, int i) {
	int filas = Matriz.rows()-1;
	int columnas = Matriz.cols();
	Matriz.block(i,0,filas-i,columnas) = Matriz.bottomRows(filas-i);
	Matriz.conservativeResize(filas,columnas);
	return Matriz;
}

MatrixXd MatrizColumnaEliminadaJ(MatrixXd Matriz, int j) {
	int filas = Matriz.rows();
	int columnas = Matriz.cols()-1;
	Matriz.block(0,j,filas,columnas-j) = Matriz.rightCols(columnas-j);
	Matriz.conservativeResize(filas,columnas);
	return Matriz;
}

MatrixXd submatriz(MatrixXd Matriz, int i, int j) {
	return MatrizFilaElimiadaI(MatrizColumnaEliminadaJ(Matriz,j),i);
}

double cofactor(MatrixXd Matriz, int i, int j) {
	return pow(-1,i+j)*determinante(submatriz(Matriz,i,j));
}

MatrixXd Multiplicacion(double k, int i, int n){
	MatrixXd Matriz = MatrixXd::Identity(n,n);
	Matriz(i,i) = k;
	return Matriz;
}

MatrixXd Suma(double k, int i, int j, int n){
	MatrixXd Matriz = MatrixXd::Identity(n,n);
	Matriz(i,j) = k;
	return Matriz;
}

MatrixXd Intercambio(int i, int j, int n){
	MatrixXd Matriz = MatrixXd::Identity(n,n);
	Matriz(i,i) = 0;
	Matriz(i,j) = 1;
	Matriz(j,j) = 0;
	Matriz(j,i) = 1;
	return Matriz;
}

MatrixXd InversaGaussJordan(MatrixXd Matriz) {

	if (determinante(Matriz) == 0) {
		cout << "La Matriz es singular, det = " << determinante(Matriz) << endl; abort();
	}
	int n = Matriz.rows(), k;
	MatrixXd MatrizIdentidad = MatrixXd::Identity(n,n);
	MatrixXd Cambios;

	for (int i=0; i<n; i++) {
		k = i;
		while (Matriz(k,i) == 0.0) {
			k++;
			}

		Cambios = Intercambio(k,i,n);
		MatrizIdentidad = Cambios * MatrizIdentidad;
		Matriz = Cambios * Matriz;
	}

	for (int j=0; j<n; j++) {
		for (int i=j+1; i<n; i++) {
			if (Matriz(i,j) != 0.0) {
				Cambios = Suma(-Matriz(i,j)/Matriz(j,j),i,j,n);
				MatrizIdentidad = Cambios * MatrizIdentidad;
				Matriz = Cambios * Matriz;
			}
		}
	}

	for (int i=0; i<n; i++) {
		Cambios = Multiplicacion (1/Matriz(i,i),i,n);
		MatrizIdentidad = Cambios * MatrizIdentidad;
		Matriz = Cambios * Matriz;
	}

	for (int j=n-1; j>=0; j--) {
		for (int i=0; i<j; i++) {
			Cambios = Suma (-Matriz(i,j),i,j,n);
			MatrizIdentidad = Cambios * MatrizIdentidad;
			Matriz = Cambios * Matriz;
		}
	}
	/*
	resultado;
	Matriz = MatrizIdentidad
	MatrizIdentidad = Inversa
	*/
	return MatrizIdentidad;
}

MatrixXd InversaCramer(MatrixXd Matriz) {
	MatrixXd adjunta(Matriz);
	int n = Matriz.cols();
	double det = determinante(Matriz);

	if (det == 0) {
		cout << "La Matriz es singular, det = " << determinante(Matriz) << endl; abort();
	}
	for (int i=0; i<n; i++) {
		for (int j=0; j<n; j++) {
			adjunta(i,j) = cofactor(Matriz,j,i);
		}
	}
	MatrixXd ICramer = adjunta/det;
	return ICramer;
}

MatrixXd InversaEigen(MatrixXd Matriz) {
	double det = determinante(Matriz);
	if (det == 0) {
		cout << "La Matriz es singular, det = " << determinante(Matriz) << endl; abort();
	}
	MatrixXd Ieigen = Matriz.inverse();
	return Ieigen;
}

void saveData(string archivo, MatrixXd matriz) {
	ofstream file(archivo);
	file << matriz; file.close();
}

int main() {
	string archivo;
	cout << "Inserte un Archivo que contenga la matriz cuya inversa quiera calcular: "; cin >> archivo;
	MatrixXd matriz = LeerMatrizIngresada(archivo);
	saveData("InversaGaussJordan.txt",InversaGaussJordan(matriz));
	saveData("InversaCramer.txt",InversaCramer(matriz));
	saveData("InversaEigen.txt",InversaEigen(matriz));

}


// http://eigen.tuxfamily.org/dox-devel/group__LeastSquares.html

#include <Eigen/Dense>
#include <iostream>

#include <optstats/lsqlinear.hpp>

using namespace std;
using namespace Eigen;

int main() {
  MatrixXf A = MatrixXf::Random(3, 2);
  cout << "Here is the matrix A:\n" << A << endl;
  VectorXf b = VectorXf::Random(3);
  cout << "Here is the right hand side b:\n" << b << endl;
  cout << "The least-squares solution is:\n"
       << A.bdcSvd(ComputeThinU | ComputeThinV).solve(b) << endl;

  // http://textbooks.math.gatech.edu/ila/least-squares.html
  // https://github.com/QBobWatson/ila
  //
  // https://math.stackexchange.com/questions/2591061/exponential-least-squares-equation
  //
  // https://eigen.tuxfamily.org/dox/GettingStarted.html
  // https://eigen.tuxfamily.org/dox/group__TutorialMatrixClass.html
  //
  // points: (0,6), (1,0) and (2,0)
  //
  // 6 = M . 0 + B
  // 0 = M . 1 + B
  // 0 = M . 2 + B
  //
  //     0  1                6
  // A = 1  1   e = M    b = 0    where  ê = -3
  //     2  1       B        0                5
  //
  // Solution is: M=-3 B=5,  for y=Mx + B
  //
  MatrixXf M2{{0, 1}, {1, 1}, {2, 1}};
  VectorXf b2{{6}, {0}, {0}};

  // The three methods discussed on this page are the SVD decomposition,
  // the QR decomposition and normal equations.
  // Of these, the SVD decomposition is generally the most accurate but the
  // slowest, normal equations is the fastest but least accurate,
  // and the QR decomposition is in between.

  cout << "The SVD linear least-squares solution is:\n"
       << M2.bdcSvd(ComputeThinU | ComputeThinV).solve(b2) << endl;
  // The SVD linear least-squares solution is: \n  -3 \n  5
  //
  cout << "The solution using the QR decomposition is:\n"
       << M2.colPivHouseholderQr().solve(b2) << endl;
  cout << "The solution using normal equations is:\n"
       << (M2.transpose() * M2).ldlt().solve(M2.transpose() * b2) << endl;
  // least squares solution is: y = -3x + 5

  // ( − 1,1 / 2 ) , ( 1, − 1 ) , ( 2, − 1 / 2 ) , ( 3,2 )
  // y = Bx²+Cx+D

  // 1/2  = B(-1)² + C(-1) + D
  // -1   = B(1)²  + C(1)  + D
  // -1/2 = B(2)²  + C(2)  + D
  // 2    = B(3)²  + C(3)  + D
  //
  //      1 -1  1      B        1/2
  // A =  1  1  1   x= C   b =  -1
  //      4  2  1      D       -1/2
  //      9  3  1                2
  //
  // best-fit parabola: B=53/88 C=-379/440 D=-41/44

  MatrixXf M3{{1, -1, 1}, {1, 1, 1}, {4, 2, 1}, {9, 3, 1}};
  VectorXf b3{{1 / 2.0}, {-1}, {-1 / 2.0}, {2}};
  cout << "The solution using normal equations is:\n"
       << (M3.transpose() * M3).ldlt().solve(M3.transpose() * b3) << endl;
  cout << "B=53/88=" << 53 / 88.0 << " C=-379/440=" << -379 / 440.0
       << " D=-82=" << -82 << std::endl;

  // BASIC EXPONENTIAL: y = a*e^{-bx}
  /*
1 8558
2 5411
3 2830
4 2267
5 760
6 549
7 249
8 67
9 47
10 43
*/

  int N = 10;

  // auto ones = MatrixXd::Ones(N, 1);

  // YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY
  // MatrixXf = ... MatrixXd ...

  MatrixXf M4 = MatrixXf::Zero(N, 2);
  for (unsigned i = 0; i < N; i++) {
    // M4.topRows(i) = {i, 1};
    // M4.col(1) = ones.col(0);
    M4(i, 0) = i;
    M4(i, 1) = 1.0;
  }

  //{{0, 1}, {1, 1}, {2, 1}};
  // VectorXf b2{{6}, {0}, {0}};

  // https://gist.github.com/gocarlos/c91237b02c120c6319612e42fa196d77
  // https://math.stackexchange.com/questions/2591061/exponential-least-squares-equation

  // example MI: https://mycurvefit.com

  VectorXf v = VectorXf::Zero(N);
  std::vector<float> vf = {8558, 5411, 2830, 2267, 760, 549, 249, 67, 47, 43};

  // BASIC EXPONENTIAL: y = a*e^{-bx}
  // ln(y) = ln(a) - bx
  // ZY = ln(y)
  // ZA = ln(a)
  // ZB = -b
  // ZY = ZB x + ZA

  for (unsigned i = 0; i < N; i++)
    v[i] = ::log(vf[i]); // log natural

  // VectorXf res = M4.bdcSvd(ComputeThinU | ComputeThinV).solve(v);
  VectorXf res = (M4.transpose() * M4).ldlt().solve(M4.transpose() * v);

  cout << "The solution using normal equations is:\n" << res << endl;
  double ZB = res[0];
  double ZA = res[1];
  double realA = ::exp(ZA);
  double realB = -ZB;

  cout << "y = " << realA << " * e^{" << -realB << " * x }" << endl;
  double ss_res = 0;
  double ss_tot = 0;
  double mean = 0;
  // https://en.wikipedia.org/wiki/Coefficient_of_determination
  for (unsigned i = 0; i < N; i++)
    mean += vf[i];
  mean = mean / N;

  for (unsigned i = 0; i < N; i++) {
    double yest = realA * ::exp(-realB * i);
    double ydiff = (vf[i] - mean) * (vf[i] - mean);
    double ydiff2 = (yest - vf[i]) * (yest - vf[i]);
    ss_res += ydiff2;
    ss_tot += ydiff;
    std::cout << "y[" << i << "] ~~ " << yest << " y=" << vf[i]
              << " (mean=" << mean << ") diff2=" << ydiff2
              << " / diff=" << ydiff << "(R2 = " << (1 - (ydiff2 / ydiff))
              << ")" << std::endl;
    std::cout << "SS_res = " << ss_res << std::endl;
    std::cout << "SS_tot = " << ss_tot << std::endl;
    double r2 = 1 - (ss_res / ss_tot);
    std::cout << "r2 = " << r2 << std::endl;
  }

  return 0;
}


#include "iostream"
using namespace std;

#include <ctime>
// include Core and Dense compuation parts from Eigen
#include <Eigen/Core>
#include <Eigen/Dense>

using namespace Eigen;

# define MATRIX_SIZE 50

/**************************************************
 * A demo for the basic types in Eigen
 * to install Eigen, use: sudo apt-get install libeigen3-dev
 * Official documentation: https://eigen.tuxfamily.org/index.php?title=Main_Page
 * https://eigen.tuxfamily.org/dox/GettingStarted.html
 * *************************************************/

int main(int argc, char** argv){
    // 1. Declaration
    // All metrices and vector in Eigen use type Eigen::Matrix, which is a template class.
    // The first 3 generic parameters are: data type, row, column.
    // Declare a 2*3 float matrix with
    Matrix<float, 2, 3> matrix_23;

    // Meanwhile, Eigen also provides many built-in types via typedef
    // but their bottom is still Eigen::Matrix<>
    // e.g. Vector3d is the same as Eigen::Matrix<double, 3, 1>
    Vector3d v_3d;
    Matrix<float, 3, 1> vd_3d;

    // Matrix3d is Eigen::Matrix<double, 3, 3>
    Matrix3d matrix_33 = Matrix3d::Zero();       // initialized to 0

    // Eigen also provides matrix with dynamic size
    // the size is not deteremined during complie time, and can be stored in some runtime variable
    // fixed size matrix would have better performance, so it's recommended if you know the dim.
    Matrix<double, Dynamic, Dynamic> matrix_dynamic;
    // shortcut of dynmaic size matrix: MatrixXd
    MatrixXd matrix_x;

    // 2. Operation
    // 2.1 initialization
    matrix_23 << 1, 2, 3, 4, 5, 6;
    // To print the matrix, can put it to the output stream directly
    cout << "matrix 2x3 from 1 to 6: \n" << matrix_23 << endl;

    // 2.2 Access the element using (i, j) instead of [][] in array
    cout << "print matrix 2x3 one by one" << endl;
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 3; j++)
            cout << matrix_23(i, j) << "\t";
        cout << endl;
    }

    // 2.3 Matrix multiplication
    v_3d << 3, 2, 1;
    vd_3d << 4, 5, 6;
    // Note: cannot mix two matrices with different types (e.g. matrix_23: float, v_3d: double)
    // the following is wrong: Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d
    // Need to do explicit casting first: matrix.cast<double>()
    Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d;
    cout << "[1,2,3;4,5,6]∗[3,2,1]=" << result.transpose() << endl;

    // both matrix_23 and vd_3d contain float, so the following is ok
    Matrix<float, 2, 1> result2 = matrix_23 * vd_3d;
    cout << "[1,2,3;4,5,6]∗[4,5,6]: " << result2.transpose() << endl;

    // 2.4 Other matrix operations
    // + - * / are allowed. Note that / is usually matrix / scalar
    // The operations reduing the matrix into a value in Eigen are called "Reduction operations"
    matrix_33 = Matrix3d::Random();
    cout << "random matrix: \n" << matrix_33 << endl;
    cout << "transpose: \n" << matrix_33.transpose() << endl;
    cout << "sum: " << matrix_33.sum() << endl;
    cout << "trace: " << matrix_33.trace() << endl;
    cout << "times 10: \n" << 10 * matrix_33 << endl;
    cout << "inverse: \n" << matrix_33.inverse() << endl;
    cout << "det: " << matrix_33.determinant() << endl;

    // Eigenvalues
    // Real symmetric matrix is guaranteed to be diagonalizable. Here use A^T*A as an example
    SelfAdjointEigenSolver<Matrix3d> eigen_solver(matrix_33.transpose() * matrix_33);
    cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl;
    cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl;

    // Solve equations
    // Solve matrixNN * x = v_Nd, N = 60 as defined before by macro

    // Method 1: Take the inverse directly and do multiplication, which is time-consumptive
    Matrix<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN = MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);  // dynamic size, determined during runtime
    // We still consider A^T*A as an example
    matrix_NN = matrix_NN * matrix_NN.transpose();
    Matrix<double, MATRIX_SIZE, 1> v_Nd = MatrixXd::Random(MATRIX_SIZE, 1);
    // do the time counting
    clock_t time_start = clock();
    // Take the inverse directly
    Matrix<double, MATRIX_SIZE, 1> x = matrix_NN.inverse() * v_Nd;
    cout << "time of normal inverse is " << 1000 * (clock() - time_start) / (double) CLOCKS_PER_SEC << "ms" << endl;
    cout << "x = " << x.transpose() << endl;

    // Method 2: Use matrix decomposition to solve (e.g. QR decomposition)
    time_start = clock();
    x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
    cout << "time of QR decomp is: " << 1000*(clock() - time_start) / (double) CLOCKS_PER_SEC << "ms" << endl;
    cout << "x = " << x.transpose() << endl;

    // Method 3: for positive definite matrix, can use Cholesky decomp. to solve
    time_start = clock();
    x = matrix_NN.ldlt().solve(v_Nd);
    cout << "time of ldlt decomp is: " << 1000*(clock() - time_start) / (double) CLOCKS_PER_SEC << "ms" << endl;
    cout << "x = " << x.transpose() << endl;

    // 3. Other stuffs
    // 3.0 Partial reductions
    MatrixXf mat(2, 4);
    mat << 1, 2, 6, 9,
           3, 1, 7, 2;
    // take the maxCoeff() at each column
    cout << "Column's maximum: " << endl << mat.colwise().maxCoeff() << endl;
    cout << "Column's maximum: " << endl << mat.rowwise().maxCoeff().transpose() << endl;

    // 3.1 Broadcasting
    // similar to partial reductions. The difference is that broadcasting constructs an expression where a vector
    // (column or row) is interpreted as a matrix by replicating it in one direction
    VectorXf v(2);
    v << 0,
         1;
    // add v to each column of m
    mat.colwise() += v;

    cout << "Broadcasting result: " << endl;
    cout << mat << endl;

    // example of broadcasting: find the nearest neighbor
    // Broadcasting can also be combined with other operations, such as Matrix or Array operations, reductions and partial reductions
    // finds the nearest neighbour of a vector v within the columns of matrix m.
    // Note: use squaredNorm() to compute the squared Euclidean distance

    // For more info, refer to: https://eigen.tuxfamily.org/dox/group__TutorialReductionsVisitorsBroadcasting.html
    MatrixXf m(2, 4);
    v << 2,
         3;
    m << 1, 23, 6, 9,
         3, 11, 7, 2;
    MatrixXf::Index index;
    // find the nearest neighbor
    (m.colwise() - v).colwise().squaredNorm().minCoeff(&index);

    cout << "Nearest neighbor is column " << index << ":" << endl;
    cout << m.col(index) << endl;

    return 0;
}


#include "MatrixMultiply.hpp"

#include <exception>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <numeric>

scottgs::MatrixMultiply::MatrixMultiply()
{
	;
}

scottgs::MatrixMultiply::~MatrixMultiply()
{
	;
}

scottgs::FloatMatrix scottgs::MatrixMultiply::operator()(const scottgs::FloatMatrix& lhs, const scottgs::FloatMatrix& rhs) const
{
	// Verify acceptable dimensions
	if (lhs.size2() != rhs.size1())
		throw std::logic_error("matrix incompatible lhs.size2() != rhs.size1()");

	scottgs::FloatMatrix result(lhs.size1(),rhs.size2());

	// YOUR ALGORIHM WITH COMMENTS GOES HERE:

	/*
		##### Method 3 #####
        Based on Method 2 store the sizes into variables
    */

	int r = lhs.size1();
	int c = rhs.size2();
	int size = lhs.size2();	// get the size for multiplication
	scottgs::FloatMatrix rhs_trans = boost::numeric::ublas::trans(rhs);

	for (int i = 0; i < r*c; i++) {
		float val = 0;
		for (int s = 0; s < size; s++) {
			val += (lhs.data()[(i/c)*size + s] * rhs_trans.data()[(i%c)*size + s]);
		}
		result.data()[i] = val;
	}

	/*
		##### Method 2 #####
        Transposed right hand side matrix, and using data() to access values
    */
	// scottgs::FloatMatrix rhs_trans = boost::numeric::ublas::trans(rhs);

	// for (int i = 0; i < lhs.size1()*rhs.size2(); i++) {
	// 	float val = 0;
	// 	for (int s = 0; s < lhs.size2(); s++) {
	// 		val += (lhs.data()[(i/rhs.size2())*lhs.size2() + s] * rhs_trans.data()[(i%rhs.size2())*lhs.size2() + s]);
	// 	}
	// 	result.data()[i] = val;
	// }

	/*
		##### Method 1 #####
        Use simple three nested loop to implement
    */

	// for (int r = 0; r < result.size1(); r++) {
	// 	for (int c = 0; c < result.size2(); c ++) {
	// 		float val {0};

	// 		// Start to compute
	// 		for (int s = 0; s < lhs.size2(); s++) {
	// 			val += (lhs(r, s) * rhs(s, c));
	// 		}
	// 		result(r, c) = val;
	// 	}
	// }

	return result;
}

scottgs::FloatMatrix scottgs::MatrixMultiply::multiply(const scottgs::FloatMatrix& lhs, const scottgs::FloatMatrix& rhs) const
{
	// Verify acceptable dimensions
	if (lhs.size2() != rhs.size1())
		throw std::logic_error("matrix incompatible lhs.size2() != rhs.size1()");

	return boost::numeric::ublas::prod(lhs,rhs);
}


/*
	Student T Test Implementation.
*/

#ifndef T_TEST_HPP
#define T_TEST_HPP

#include "iostream"
#include <vector>

#include <boost/math/distributions/students_t.hpp>

using namespace std;
using namespace boost::math;

template<typename T>
T mean(std::vector<T> data)
{
	if(data.empty())
		return 0;

	T sum;
	for (typename std::vector<T>::const_iterator it = data.begin(); it != data.end(); ++it)
	{
		sum += *it;
	}

	return (sum/(int)data.size());
}

template<typename T>
T sample_variance(std::vector<T> data, T mean)
{
	T _sample_variance, _sample_square_dist;
	for (typename std::vector<T>::const_iterator it = data.begin(); it != data.end(); ++it)
	{
		_sample_square_dist += pow((*it - mean), 2);
	}

	_sample_variance = _sample_square_dist / ((int)data.size() - 1);

	return _sample_variance;
}

template<typename T>
std::pair<T, T> t_statistics(std::vector<T> sample1, std::vector<T> sample2)
{
	T _mean_diff, _t_statistic, _degrees_of_freedom, _sample_1_mean, _sample_2_mean, _sample_1_variance,
		_sample_2_variance, _sample_1_avg_variance, _sample_2_avg_variance, _sample_avg_variance;
	int _sample_1_size, _sample_2_size;

	_sample_1_size = (int)sample1.size();
	_sample_2_size = (int)sample2.size();

	_sample_1_mean = mean<T>(sample1);
	_sample_2_mean = mean<T>(sample2);

	_sample_1_variance = sample_variance<T>(sample1, _sample_1_mean);
	_sample_2_variance = sample_variance<T>(sample2, _sample_2_mean);

	_mean_diff = abs(_sample_1_mean - _sample_2_mean);

	_sample_1_avg_variance = _sample_1_variance/_sample_1_size;
	_sample_2_avg_variance = _sample_2_variance/_sample_2_size;
	_sample_avg_variance   = (_sample_1_avg_variance + _sample_2_avg_variance);

	_t_statistic = _mean_diff/( sqrt(_sample_avg_variance) );
	_degrees_of_freedom = pow(_sample_avg_variance, 2) / ( (pow(_sample_1_avg_variance, 2)/(_sample_1_size - 1)) + (pow(_sample_2_avg_variance, 2)/(_sample_2_size - 1)) );

	return std::pair<T, T>(_t_statistic, _degrees_of_freedom);
}

template<typename T>
double similarity_probability(std::vector<T> sample1, std::vector<T> sample2)
{
	if(sample1.empty() || sample2.empty())
	{
		std::cout << "Empty sample(s) sent for calculating similarity." << endl;
		return (T)0;
	}

	std::pair<T, T> _t_statistics = t_statistics<T>(sample1, sample2);

	students_t dist(_t_statistics.second);

 	double _similarity_probability = cdf(complement(dist, fabs(_t_statistics.first)));

 	return (2*_similarity_probability);
}

#endif


/* ------------------------------------------------------------
 * @file: fact_cholesky.cpp
 * @dependencias: armadillo, math
 * @version 3.6
 * ------------------------------------------------------------*/

// Compile and run:
// [1] $ g++ fact_cholesky.cpp -o fc.out -std=c++11
// [2] $ ./fc.out

#include <armadillo>
#include <math.h>

using namespace std;
using namespace arma;

/**
 * @brief Verifica que una matriz sea simétrica.
 * @param A Matriz de tamaño nxn.
 * @return True si es simétrica, false si no lo es.
 */
bool is_simetric(mat A){
    return approx_equal(A, A.t(),"absdiff", 0);
}

// Source: https://www.tutorialspoint.com/cplusplus-program-to-compute-determinant-of-a-matrix
// Nota: Se implementó porque la función de armadillo arma::det(mat) presentó problemas.
/**
 * @brief Calcula el determinante de una matriz cuadrada.
 * @param A Matriz de tamaño nxn.
 * @param n Tamaño de la matriz.
 * @return El valor del determinante de la matriz.
 */
double determinant(mat A, int n) {
    double det = 0;
    mat sub_matrix(10,10);

    if (n == 1){
        // Caso 0: Si la matriz es de orden 1
        return A(0,0);
    } else if (n == 2){
        // Caso base: Determinante de una matriz 2x2
        return (A(0,0) * A(1,1) - A(1,0) * A(0,1));
    } else {
        // Caso de reducciones de la matriz
        for (int x = 0; x < n; x++){
            int sub_i = 0;
            for (int i = 1; i < n; i++){
                int sub_j = 0;
                for (int j = 0; j < n; j++){
                    if (j == x)
                        continue;
                    sub_matrix(sub_i,sub_j) = A(i,j);
                    sub_j++;
                }
                sub_i++;
            }
            det += (pow(-1,x)* A(0,x) * determinant(sub_matrix, n-1));
        }
    }
    return det;
}

/**
 * @brief Determina si una matrix dada es Definida Positiva.
 * @param A Matriz de tamaño nxn.
 * @return True si es definida positiva, false si no es.
 */
bool is_positive_definite(mat A){
    int n = A.n_rows;

    for (int i = 0; i < n; i++){
        mat A_t = A.submat(0,0,i,i);
        double det_result = determinant(A_t,i+1);
        //cout << "Det(A_t): "<< det_result<< endl;
        if (det_result < 0){
            return false;
        }
    }
    return true;
}

/**
 * @brief Resuelve un sistema de ecuaciones por el método de la sustitución
 *        hacia atrás.
 * @param A Matriz de tamaño nxn. Debe ser triangular superior.
 * @param b Vector de tamaño n.
 * @return El vector con la solución al sistema de ecuaciones.
 */
vec back_substitution(mat A, vec b){
    int n = A.n_rows;
    vec x(n);
    x.zeros();

    for (int i = n-1; i >= 0; i--){
        double sum = 0;
        for (int j = i+1; j < n; j++){
            sum += A(i,j) * x(j);
        }
        x(i) = (1/A(i,i)) * (b(i) - sum);
    }

    return x;
}

/**
 * @brief Resuelve un sistema de ecuaciones por el método de la sustitución
 *        hacia adelante.
 * @param A Matriz de tamaño nxn. Debe ser triangular inferior.
 * @param b Vector de tamaño n.
 * @return El vector con la solución al sistema de ecuaciones.
 */
vec forward_substitution(mat A, vec b){
    int n = A.n_rows;
    vec x(n);
    x.zeros();

    for (int i = 0; i < n; i++){
        double sum = 0;
        for (int j = 0; j < i; j++){
            sum += A(i,j) * x(j);
        }
        x(i) = (1/A(i,i)) * (b(i) - sum);
    }

    return x;
}

/**
 * @brief Soluciona un sistema de ecuaciones utilizando la Factorización de Cholesky.
 * @param A Matriz de coeficientes. Deber ser cuadrada, simétrica y positiva definida.
 * @param b Vector de términos independientes.
 */
void fact_cholesky(mat A, vec b){

    if (is_simetric(A) && is_positive_definite(A)){
        int n = A.n_rows;
        mat L(n,n);
        L.zeros();

        // Calcular la matriz L
        for (int i = 0; i < n; i++){

            for (int j = 0; j < i+1; j++){
                double sum = 0;

                if (i == j){
                    for (int k = 0; k < j; k++)
                        sum += pow(L(j,k),2);
                    L(j,j) = sqrt(A(j,j) - sum);
                } else {
                    for (int k = 0; k < j; k++)
                        sum += L(i,k) * L(j,k);
                    L(i,j) = (A(i,j) - sum) / L(j,j);
                }
            }
        }

        // Resolve Ly=b, para luego solucionar L_t x = y.
        vec y = forward_substitution(L,b);
        vec x = back_substitution(L.t(),y);

        // Presentar la solución del Sistema.
        x.print("x: ");

    } else
        cout<< "[Error 504] El sistema no cumple  con las condiciones para resolverse por Cholesky."<< endl;
}

int main(int argc, char const *argv[]){

    // Test Cholesky
    // ----------------------------
    mat A(4,4);
    vec b(4);

    A = {{ 25, 15, -5,-10},
         { 15, 10,  1, -7},
         { -5,  1, 21,  4},
         {-10, -7,  4, 18}};

    b = {-25, -19, -21, -5};

    cout << "Factorización Cholesky" << endl;
    A.print("A:\n");
    b.print("b:\n");
    fact_cholesky(A,b);

    // Solución del ejemplo -> x = [-1, -1, -1, -1]

    return 0;
}

Catalogo/03. Sistemas de Ecuaciones/C++/fact_cholesky.cpp


#include <cmath>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <armadillo>

using namespace  std;
using namespace arma;

vec ConjugateGradient(mat A, vec b, vec x0, int dim){
  int IterMax, i;
  const double tolerance = 1.0e-14;
  vec x(dim),r(dim),p(dim),t(dim);
  double c,alpha,d;

  IterMax = dim;
  x = x0;
  r = b - A*x;
  p = r;
  c = dot(r,r);
  i = 0;
  while (i <= IterMax || sqrt(dot(p,p)) < tolerance ){
    if(sqrt(dot(p,p))<tolerance){
      cerr << "An error has occurred in ConjugateGradient: execution of function terminated" << endl;
      break;
    }
    t = A*p;
    alpha = c/dot(p,t);
    x = x + alpha*p;
    r = r - alpha*t;
    d = dot(r,r);
    if(sqrt(d) < tolerance)
      break;
    p = r + (d/c)*p;
    c = d;
    i++;
  }
  return x;
}

//   Main function begins here
int main(int  argc, char * argv[]){
  int dim = 2;
  vec x(dim),xsd(dim), b(dim),x0(dim);
  mat A(dim,dim);

  // Set our initial guess
  x0(0) = x0(1) = 0;
  // Set the matrix
  A(0,0) =  3;    A(1,0) =  2;   A(0,1) =  2;   A(1,1) =  6;

  cout << "The Matrix A that we are using: " << endl;
  A.print();
  cout << endl;

  vec y(dim);
  y(0) = 2.;
  y(1) = -2.;

  cout << "The exact solution is: " << endl;
  y.print();
  cout << endl;
  b = A*y;

  cout << "The right hand side, b, of the expression Ax=b: " << endl;
  b.print();
  cout << endl;

  xsd = ConjugateGradient(A,b,x0, dim);

  cout << "The approximate solution using Conjugate Gradient is: " << endl;
  xsd.print();
  cout << endl;

}


#pragma once
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <boost/math/common_factor_rt.hpp>

//a namespace to hold various GCD algorithms.  I was thinking about making the namespace
//a non-constructable class because some algorithms use sub-functions which should not
//be accessible anywhere except the function that calls it.  However, there is a low chance of
//name clashing.  Perhaps if there was risk, we should just put sub-functions into another namespace..
//NOTE: look up later, "does inlining actually do anything now-adays?"
namespace GCD{

	//Euclidean algorithms
	template <typename IntegerType> IntegerType Euclid_Recursive(IntegerType a, IntegerType b);
	template <typename IntegerType> IntegerType Euclid_Iterative(IntegerType a, IntegerType b);
	template <typename IntegerType> IntegerType Euclid_Unrolled(IntegerType a, IntegerType b);
	template <typename IntegerType> IntegerType inline Euclid_Bit(IntegerType a, IntegerType b);

	//Binary algorithms
	template <typename IntegerType> IntegerType Binary_Recursive(IntegerType a, IntegerType b);
	template <typename IntegerType> IntegerType Binary_Iterative(IntegerType a, IntegerType b);
	template <typename IntegerType> IntegerType Binary_Iterative_V2(IntegerType a, IntegerType b);

	//Counting Trailing Zeros algorithm
	template <typename IntegerType> IntegerType CTZ(IntegerType a, IntegerType b);

	//Brute Force algorithms
	template <typename IntegerType> IntegerType Brute_Force(IntegerType a, IntegerType b);

	//Human algorithm
	template <typename IntegerType> IntegerType Factor(IntegerType a, IntegerType b);

	//Boost implementation (looks similar to Euclid_Unrolled)
	template <typename IntegerType> inline IntegerType Boost(IntegerType a, IntegerType b);

	//utility functions
	template <typename IntegerType> void Sieve(std::vector<IntegerType> & primes, IntegerType n);
	template <typename IntegerType> void Factor(IntegerType n, std::vector<IntegerType> & factors, const std::vector<IntegerType> & primes);
	template <typename IntegerType> void Common_Factors(const std::vector<IntegerType> & A, const std::vector<IntegerType> & B, std::vector<IntegerType> & common);
	template <typename IntegerType> IntegerType School(IntegerType a, IntegerType b, const std::vector<IntegerType> & primes);
	template <typename IntegerType> IntegerType Count_Trailing_Zeros(IntegerType n);

};

//Euclidean algorithms
template <typename IntegerType> IntegerType GCD::Euclid_Recursive(IntegerType a, IntegerType b){
    if (b==0) {
       return a;
    } else  {
       return Euclid_Recursive(b, a%b);
    }
}
template <typename IntegerType> IntegerType GCD::Euclid_Iterative(IntegerType a, IntegerType b){
	IntegerType c;
	while (b != 0){
		c = b;
		b = a%b;
		a = c;
	}
	return a;
}
template <typename IntegerType> IntegerType GCD::Euclid_Unrolled(IntegerType a, IntegerType b){

	while (b != 0){

		a = a%b;
		if(a==0){return b;}

		b = b%a;
		if(b==0){return a;}
	}

	return a;
}
template <typename IntegerType> inline IntegerType GCD::Euclid_Bit(IntegerType a, IntegerType b){
	while(b) b ^= a ^= b ^= a %= b;
	return a;
}

//Binary algorithms
template <typename IntegerType> IntegerType GCD::Binary_Recursive(IntegerType a, IntegerType b){

    if (a == b){return a;}
    if (a == 0){return b;}
    if (b == 0){return a;}

    if (~a & 1) {
        if (b & 1){
			return Binary_Recursive(a >> 1, b);
		}
        else {
			return Binary_Recursive(a >> 1, b >> 1) << 1;
		}
    }

    if (~b & 1){
		return Binary_Recursive(a, b >> 1);
	}

    if (a > b){
		return Binary_Recursive((a - b) >> 1, b);
	}

    return Binary_Recursive((b - a) >> 1, a);
}
template <typename IntegerType> IntegerType GCD::Binary_Iterative(IntegerType a, IntegerType b){

	//if (a == 0 && b == 0){return 0;} //convention-- *NEVERMIND already covered
	if (a == 0){ return b;}
	if (b == 0){ return a;}

	IntegerType c;
	for (c = 0; ((a | b) & 1) == 0; ++c) {
		a >>= 1;
		b >>= 1;
	}

	while ((a & 1) == 0){
		a >>= 1;
	}

	do {

		while ((b & 1) == 0){
			b >>= 1;
		}

		if (a > b) {
			IntegerType d = b; b = a; a = d;} //inline swap
		b = b - a;
		} while (b != 0);

	return a << c;
}
template <typename IntegerType> IntegerType GCD::Binary_Iterative_V2(IntegerType a, IntegerType b){

	IntegerType c = 0;

	while ( a && b && a!=b ) {
		bool ea = !(a & 1);
		bool eb = !(b & 1);

		if ( ea && eb ) {
			++c;
			a >>= 1;
			b >>= 1;
		}
		else if ( ea && !eb ) a >>= 1;
		else if ( !ea && eb ) b >>= 1;
		else if ( a>=b ) a = (a-b)>>1;
		else {
		IntegerType tmp = a;
		a = (b-a)>>1;
		b = tmp;
		}
	}

	return !a? b<<c : a<<c;
}

//Counting Trailing Zeros
template <typename IntegerType> IntegerType GCD::CTZ(IntegerType a, IntegerType b){

	if (a == 0){return b;}
    if (b == 0){return a;}

    IntegerType c = Count_Trailing_Zeros(a|b);

    a >>= Count_Trailing_Zeros(a);

    while (true){
        b >>= Count_Trailing_Zeros(b);
        if (a == b){break;}
        if (a > b){IntegerType d = b; b = a; a = d;} //inline swap
        if (a == 1){break;}
        b -= a;
    }

    return a << c;
}

//Brute Force algorithms
template <typename IntegerType> IntegerType GCD::Brute_Force(IntegerType a, IntegerType b){

    IntegerType c = std::min(a,b);
	if (c == 0){return std::max(a,b);} //<- fix a floating point error when a number is zero
    while (a%c != 0 or b%c !=0) {
       --c;
    }

    return c;
}

//human algorithm
template <typename IntegerType> IntegerType GCD::Factor(IntegerType a, IntegerType b){
    IntegerType g;
	if (a == 0 && b ==0){return 0;}
    std::vector<IntegerType> primeList;
    Sieve(primeList, std::max(a,b));
    return School(a,b,primeList);
}

//Boost implementation (looks similar to Euclid_Unrolled)
template <typename IntegerType> inline IntegerType GCD::Boost(IntegerType a, IntegerType b){
	return boost::math::gcd(a,b);
}

//utility functions
template <typename IntegerType> void GCD::Sieve(std::vector<IntegerType> & primes, IntegerType n){
    using namespace std;
	IntegerType i,j;
    vector<bool> A;

    primes.erase(primes.begin(), primes.end());

    for(i=0;i<=n;i++) {
       A.push_back(true);
    }

    for(i=2;i<=sqrt(n);i++) {
       if(A[i]) {
          primes.push_back(i);
          for(j=i*i;j<=n;j+= i) {
	     A[j] = false;
	  }
       }
    }

    for(j=i;j<=n;j++) {
        if(A[j]) {
	   primes.push_back(j);
	}
    }

    return;
}
template <typename IntegerType> void GCD::Factor(IntegerType n, std::vector<IntegerType> & factors, const std::vector<IntegerType> & primes){
    IntegerType i;
    IntegerType num;

    factors.erase(factors.begin(), factors.end());

    i = 0;
    num = n;

    while(num > 1) {
       if (num%primes[i] == 0) {
           factors.push_back(primes[i]);
	   num = num/primes[i];
       } else {
          i++;
       }
    }

    return;
}
template <typename IntegerType> void GCD::Common_Factors(const std::vector<IntegerType> & A, const std::vector<IntegerType> & B, std::vector<IntegerType> & common){

     IntegerType i,j;

     common.erase(common.begin(),common.end());

     i = 0;
     j = 0;
     while (i < A.size() and j < B.size()) {
        if (A[i] == B[j]) {
	   // same, common, push it and move both
           common.push_back(A[i]);
	   i++;
	   j++;
	} else if (A[i] > B[j]) {
	   // A is too big, so bump B;
	   j++;
	} else {
	   i++;
	}
     }

     return;
}
template <typename IntegerType> IntegerType GCD::School(IntegerType a, IntegerType b, const std::vector<IntegerType> & primes){
	using namespace std;
	vector<IntegerType> aFactors, bFactors, common;
    IntegerType gcd=1;
    IntegerType i;

    Factor(a, aFactors,primes);
    Factor(b, bFactors,primes);
    Common_Factors(aFactors, bFactors, common);

    for(i=0;i<common.size();i++) {
      gcd *= common[i];
    }

    return gcd;

}
template <typename IntegerType> IntegerType GCD::Count_Trailing_Zeros(IntegerType n){

	if (n == 0) {
        return -1;
    }
    IntegerType count = 0;
    while (n % 2 == 0) {
        count++;
        n >>= 1;
    }

    return count;
}


/*
* Level_9 Group B:
* Source file for OptionExtras methods
*
* option operation function, reference to OptionExtras.cpp in the cource example code
*
* @file OptionExtras.cpp
* @author Chunyu Yuan
* @version 1.0 02/28/2021
*
*/

#include <string> //library for using string
#include <vector> //library for using vector

#include "OptionExtras.hpp" //header file for OptionExtras methods
#include <boost/math/distributions/normal.hpp> //header file for using normal distribution
#include <cmath> // library for using math formula
#include <iostream> // Standard Input / Output Streams Library

using namespace boost::math;//namespace declaration for using boost::math
using namespace std; //namespace declaration for using std

//A simple mesher on a 1d domain
vector<double> GenerateMeshArray(double begin, double end, int n)
{
	vector<double> vec;
	// NB Full array (includes end points)
	double h = (end - begin) / (n - 1);
	//for loop to iterate to add valve to vector
	for (int i = 0; i < n; i++)
	{
		vec.push_back(begin + h * i);
	}

	return vec;
}

//price of call with T,K,sig, r, U, b. For formula information, please check course materials
double call_price(double T, double K, double sig, double r, double U, double b)
{
	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	double d2 = d1 - tmp;

	//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);

	return (U * exp((b - r) * T) * cdf(normalDist, d1)) - (K * exp(-r * T) * cdf(normalDist, d2));
}

//price of put with T,K,sig, r, U, b. For formula information, please check course materials
double put_price(double T, double K, double sig, double r, double U, double b)
{
	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	double d2 = d1 - tmp;
	//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);

	return (K * exp(-r * T) * cdf(normalDist, -d2)) - (U * exp((b - r) * T) * cdf(normalDist, -d1));
}
//delta of call with T,K,sig, r, U, b. For formula information, please check course materials
double call_delta(double T, double K, double sig, double r, double U, double b)
{//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);

	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	return exp((b - r) * T) * cdf(normalDist, d1);
}
//delta of put with T,K,sig, r, U, b. For formula information, please check course materials
double put_delta(double T, double K, double sig, double r, double U, double b)
{
	return call_delta(T, K, sig, r, U, b) - exp((b - r) * T);
}
//gamma of call with T,K,sig, r, U, b. For formula information, please check course materials
double call_gamma(double T, double K, double sig, double r, double U, double b)
{//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);
	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	return pdf(normalDist, d1) * exp((b - r) * T) / (U * sig * sqrt(T));
}
//gamma of put with T,K,sig, r, U, b. For formula information, please check course materials
double put_gamma(double T, double K, double sig, double r, double U, double b)
{
	return call_gamma(T, K, sig, r, U, b);
}

////vega of call with T,K,sig, r, U, b. For formula information, please check course materials
double call_vega(double T, double K, double sig, double r, double U, double b)
{//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);
	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	return U * sqrt(T) * exp((b - r) * T) * pdf(normalDist, d1);
}
////vega of put with T,K,sig, r, U, b. For formula information, please check course materials
double put_vega(double T, double K, double sig, double r, double U, double b)
{
	return call_vega(T, K, sig, r, U, b);
}
//theta of call with T,K,sig, r, U, b. For formula information, please check course materials
double call_theta(double T, double K, double sig, double r, double U, double b)
{//using boost library's normal distribution
	normal_distribution<> normalDist(0, 1);
	double tmp = sig * sqrt(T);
	double d1 = (log(U / K) + (b + (sig * sig) * 0.5) * T) / tmp;
	double d2 = d1 - tmp;
	return -U * sig * exp((b - r) * T) * pdf(normalDist, d1) / (2 * sqrt(T))
		- (b - r) * U * exp((b - r) * T) * cdf(normalDist, d1) - r * K * exp(-r * T) * cdf(normalDist, d2);
}
//theta of put with T,K,sig, r, U, b. For formula information, please check course materials
double put_theta(double T, double K, double sig, double r, double U, double b)
{
	return call_theta(T, K, sig, r, U, b) + r * K * exp(-r * T) + U * exp((b - r) * T) * (b - r);
}


#include "MatricesAndVectors.h"
#include <stdio.h>		/* printf */
#include <math.h>		/* pow, sqrt */
#include <Eigen/Dense>
using Eigen::Matrix;
/* Create a 3x3 diagonal matrix from a 3x1 vector */
// Matrix3f Diag3(double vec[3]){
// 	Mat3x3 output;
// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			if (i == j)
// 				output.M[i][j] = vec[i];
// 			else
// 				output.M[i][j] = 0;
// 		}
// 	}

// 	return output;
// }

// Multiply 3x3 matrices: M_out = M1*M2
// Mat3x3 MultiplyMat3x3(Mat3x3 M1, Mat3x3 M2){
// 	Mat3x3 M_out;

// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			M_out.M[i][j] = 0;
// 			for (int k = 0; k < 3; k++){
// 				M_out.M[i][j] += M1.M[i][k] * M2.M[k][j];
// 			}
// 		}
// 	}

// 	return M_out;
// }

// Sum 3x3 matrices: M_out = M1 + M2
// Mat3x3 AddMat3x3(Mat3x3 M1, Mat3x3 M2){
// 	Mat3x3 M_out;

// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			M_out.M[i][j] = M1.M[i][j] + M2.M[i][j];
// 		}
// 	}

// 	return M_out;
// }

// // Subtract 3x3 matrices: M_out = M1 - M2
// Mat3x3 SubtractMat3x3(Mat3x3 M1, Mat3x3 M2){
// 	Mat3x3 M_out;

// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			M_out.M[i][j] = M1.M[i][j] - M2.M[i][j];
// 		}
// 	}

// 	return M_out;
// }

// // Transpose 3x3 matrix: M_out = M_in'
// Mat3x3 TransposeMat3x3(Mat3x3 M_in){
// 	Mat3x3 M_out;

// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			M_out.M[i][j] = M_in.M[j][i];
// 		}
// 	}

// 	return M_out;
// }

// Calculate Skew symmetric matrix from vector
// Mat3x3 skew(Vec3 V){
// 	Mat3x3 M;
// 	M.M[0][0] = 0;
// 	M.M[0][1] = -V(2);
// 	M.M[0][2] = V(1);

// 	M.M[1][0] = V(2);
// 	M.M[1][1] = 0;
// 	M.M[1][2] = -V(0);

// 	M.M[2][0] = -V(1);
// 	M.M[2][1] = V(0);
// 	M.M[2][2] = 0;

// 	return M;

// }

// Inverse operation for Skew symmetric matrices
Matrix<float, 3, 1> invSkew(Matrix<float, 3, 3> Mat){
	Matrix<float, 3, 1> w;

	w << -Mat(1,2),
		  Mat(0,2),
		 -Mat(0,1);

	return w;
}

// Vec3 invSkew(Mat3x3 Mat){
// 	Vec3 w;

// 	w(0) = -Mat.M[1][2];
// 	w(1) = Mat.M[0][2];
// 	w(2) = -Mat.M[0][1];

// 	return w;
// }

//Cross product between two vectors
// Vec3 cross(Vec3 V1, Vec3 V2){

// 	return MultiplyMat3x3Vec3(skew(V1), V2);

// }

//Calculate the p-norm of a 3x1 vector
// double p_normVec3(Vec3 V, int p){
// 	return pow(pow(V(0), p) + pow(V(1), p) + pow(V(2), p), 1.0 / p);
// }

Matrix<float, 3, 1> normalizeVec3(Matrix<float, 3, 1> V){
	double normV = V.norm();
	if (normV > 0){
		return V*(1.0 / normV);
	}
	else{
		return V;
	}
}

//Inner product between two matrices
// double innerProd(Vec3 V1, Vec3 V2){
// 	return V1(0)*V2(0) + V1(1)*V2(1) + V1(2)*V2(2);
// }

/* Print 3x3 matrices for debugging*/
// void PrintMat3x3(Mat3x3 Mat){
// 	for (int i = 0; i < 3; i++){
// 		for (int j = 0; j < 3; j++){
// 			printf("%f\t", Mat.M[i][j]);
// 		}
// 		printf("\n");
// 	}
// 	printf("\n");
// }

/* Print 4x4 matrices for debugging*/
void PrintMat4x4(Matrix<float, 4, 4> Mat){
	for (int i = 0; i < 4; i++){
		for (int j = 0; j < 4; j++){
			printf("%f\t", Mat(i,j));
		}
		printf("\n");
	}
	printf("\n");
}

/* Print 3x1 vectors for debugging*/
void PrintVec3(Matrix<float, 3, 1> V, char const *Text){
	printf("%s = \t", Text);
	for (int i = 0; i < 3; i++){
		printf("%f \t", V(i));
	}
	printf("\n");
}

/* Print 4x1 vectors for debugging*/
void PrintVec4(Matrix<float, 4, 1> V, char const *Text){
	printf("%s = \t", Text);
	for (int i = 0; i < 4; i++){
		printf("%f \t", V(i));
	}
	printf("\n");
}

// Multiply 3x3 matrix by a 3x1 vectos: V_out = M*V
// Vec3 MultiplyMat3x3Vec3(Mat3x3 Mat, Vec3 V){
// 	Vec3 V_out;

// 	for (int i = 0; i < 3; i++){
// 		V_out.v[i] = 0;
// 		for (int k = 0; k < 3; k++){
// 			V_out.v[i] += Mat.M[i][k] * V.v[k];
// 		}
// 	}

// 	return V_out;
// }

// Scale 3x1 vector: V_out = c.V_in, where c is a constant
// Vec3 ScaleVec3(Vec3 V_in, float c){
// 	Vec3 V_out;

// 	for (int i = 0; i < 3; i++){
// 		V_out.v[i] = c*V_in.v[i];
// 	}

// 	return V_out;
// }

// Add 3x1 vectors: V_out = V1 + V2
// Vec3 Add3x1Vec(Vec3 V1, Vec3 V2){
// 	Vec3 V_out;
// 	V_out(0) = V1(0) + V2(0);
// 	V_out(1) = V1(1) + V2(1);
// 	V_out(2) = V1(2) + V2(2);

// 	return V_out;
// }

// Subtract 3x1 vectors: V_out = V1 - V2
// Vec3 Subtract3x1Vec(Vec3 V1, Vec3 V2){
// 	Vec3 V_out;
// 	V_out(0) = V1(0) - V2(0);
// 	V_out(1) = V1(1) - V2(1);
// 	V_out(2) = V1(2) - V2(2);

// 	return V_out;
// }

// Multiply 4x4 matrix by a 4x1 vector: V_out = M*V
// Vec4 MultiplyMat4x4Vec4(Mat4x4 Mat, Vec4 V){
// 	Vec4 V_out;

// 	for (int i = 0; i < 4; i++){
// 		V_out.v[i] = 0;
// 		for (int k = 0; k < 4; k++){
// 			V_out.v[i] += Mat.M[i][k] * V.v[k];
// 		}
// 	}

// 	return V_out;
// }

// //Concatenate three vectors into a 3x3 matrix
// Mat3x3 Concatenate3Vec3_2_Mat3x3(Vec3 V1, Vec3 V2, Vec3 V3){
// 	Mat3x3 M;
// 	M.M[0][0] = V1(0);
// 	M.M[1][0] = V1(1);
// 	M.M[2][0] = V1(2);

// 	M.M[0][1] = V2(0);
// 	M.M[1][1] = V2(1);
// 	M.M[2][1] = V2(2);

// 	M.M[0][2] = V3(0);
// 	M.M[1][2] = V3(1);
// 	M.M[2][2] = V3(2);

// 	return M;
// }

//Verify if any of the terms in a Vec3 is NaN
int isNanVec3(Matrix<float, 3, 1> V){
	if(isnan(V(0)) || isnan(V(1)) || isnan(V(2))){
		printf("Not a number found!\n");
		return 1;
	}
	else{
		return 0;
	}

}


// CPP program to print fermat numbers
#include <bits/stdc++.h>
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
#define llu int128_t
using namespace std;

/* Iterative Function to calculate (x^y) in O(log y) */
llu power(llu x, llu y)
{
    llu res = 1; // Initialize result

    while (y > 0) {
        // If y is odd, multiply x with the result
        if (y & 1)
            res = res * x;

        // n must be even now
        y = y >> 1; // y = y/2
        x = x * x; // Change x to x^2
    }
    return res;
}

// Function to find nth fermat number
llu Fermat(llu i)
{
    // 2 to the power i
    llu power2_i = power(2, i);

    // 2 to the power 2^i
    llu power2_2_i = power(2, power2_i);

    return power2_2_i + 1;
}

// Function to find first n Fermat numbers
void Fermat_Number(llu n)
{

    for (llu i = 0; i < n; i++) {

        // Calculate 2^2^i
        cout << Fermat(i);

        if(i!=n-1)
            cout << ", ";
    }
}

// Driver code
int main()
{
    llu n = 7;

    // Function call
    Fermat_Number(n);

    return 0;
}