#include "algebralineal.h"
#include <cmath>
using namespace std;
using namespace CALNUM;

template < class T > Vector < T > pow(const Vector < T > &v, const int &n)
{
	int             m = v.size();
	Vector < T > tmp(m);
	for (int i = 1; i <= m; i++)
		tmp(i) = pow(v(i), n);
	return tmp;
}
template < class T > Vector < T > sqrt(const Vector < T > &v)
{
	int             m = v.size();
	Vector < T > tmp(m);
	for (int i = 1; i <= m; i++)
		tmp(i) = sqrt(v(i));
	return tmp;
}
template < class T > Vector < T > fun(const Vector < T > &v, const int &n)
{
	int             m = v.size();
	Vector < T > tmp(m);
	for (int i = 1; i <= n; i++)
		tmp(i) = fun(v(i));
	return tmp;
}
template < class T > Vector < T > pol(const Vector < T > &a, const Vector < T > &v)
{
	int             m = v.size();
	Vector < T > tmp(m);
	for (int i = 1; i <= m; i++)
		tmp(i) = pol(a, v(i));
	return tmp;
}
template < class T > T pol(const Vector < T > &a, const T & x)
{
	int             n = a.size();
	T               tmp = a(n);
	for (int i = n - 1; i >= 1; i--)
		tmp = tmp * x + a(i);
	return tmp;
}
template < class T > T normsq(const Vector < T > &v)
{
	return v * v;
}

template < class T > Vector < T > difdiv(const Vector < T > &x, const Vector < T > &y)
{
	int             n = x.size() - 1;
	Vector < T > a = y;
	//n iteraciones
		for (int j = 1; j <= n; j++)
		//En iteracion k se cambian los n - k - 1 coeficientes de indice mas alto
			for (int i = n; i >= j; i--)
			a[i] = (a[i] - a[i - 1]) / (x[i] - x[i - j]);
	return a;
}

template < class T > T Lagrange(Vector < T > &x, Vector < T > &y, T & xx)
{
	//Calcula el valor en el punto xx del polinomio interpolador de orden n - 1
		// que interpola los n puntos cuyas abcisas y ordenadas son los vectores x e y

		int             n = x.dim();
	Vector < T > l(n);
	for (int i = 1; i <= n; i++)
		l(i) = y(i);

	for (int i = 1; i <= n; i++) {
		T               num = (xx - x(i));
		for (int j = 1; j <= n; j++)
			if (j != i)
				l(j) = l(j) * num / (x(j) - x(i));

	}
	T               p = 0;
	for (int i = 1; i <= n; i++)
		p = p + l(i);
	return p;
}

template < class T >
T Neville(const Vector < T > &x, const Vector < T > &y, const T & xx)
{
	int             n = x.size();
	//Construccion vector de diferencias
		Vector < T > xdif(n);
	for (int i = 0; i < n; i++)
		xdif[i] = xx - x[i];
	Vector < T > p = y;
	n--;
	//n iteraciones
		for (int j = 1; j <= n; j++)
		//En iteracion k se cambian los n - k - 1 coeficientes de indice mas alto
			for (int i = n; i >= j; i--)
			p[i] = (xdif[i] * p[i - 1] - xdif[i - j] * p[i]) / (xdif[i] - xdif[i - j]);
	return p[n];
}


template < class T > T
eval_difdiv(const T & xx, const Vector < T > &x, const Vector < T > &a)
{
	int             n = x.size() - 1;
//n iteraciones:evaluacion por Horner del polinomio
		T y = a[n];
	for (int i = n - 1; i >= 0; i--)
		y = a[i] + (xx - x[i]) * y;
	return y;
}


template < class T > Vector < T > gc(Matrix < T > &A, Vector < T > &b, T & prec, int &iter)
{
	//Metodo gradiente conjugado. Devuelve el vector solucion.
	                const int maxiter = iter;
	Vector < T > x(b.dim());
	x = T(0);
	Vector < T > r = b - A * x;
	Vector < T > p = r;
	T               zr = r * r;
	cout << "zr =" << zr << endl;
	const T         stp = prec * norm(b);
	if (zr == 0) {
		prec = 0;
		iter = 0;
		return x;
	}
	for (iter = 0; iter < maxiter; iter++) {
		cout << "iter=" << iter << endl;
		Vector < T > mp = A * p;
		T               alpha = zr / (mp * p);
		x += alpha * p;
		r -= alpha * mp;
		T               zrold = zr;
		zr = r * r;
		T               mod = sqrt(zr);
		cout << "norm (r) =" << mod << endl;
		if (mod <= stp)
			break;
		p = r + (zr / zrold) * p;
	}
	prec = sqrt(zr);
	if (iter == maxiter) {
		cout << "maximo numero de iteraciones" << endl;
		return x;
	}
	return x;
}
