#pragma once

#include "mat.h"
#include "det.h"
#include "functions.h"
#include "up_tri_mat.h"

#include <limits>
#include <algorithm>

namespace cgv {
namespace math {

//qr decomposition returns false if matrix is singular
template<typename T>
bool qr(const mat<T> &a, mat<T> &q, up_tri_mat<T> &r) 
{
	mat<T> rr = a;
	
	bool sing =false;
	unsigned n = a.nrows();
	unsigned m = a.ncols();
	assert(n == m);
	q.resize(n,n);
	r.resize(n);
	unsigned i,j,k;
	vec<T> c(n), d(n);
	T scale,sigma,sum,tau;
	for (k=0;k<n-1;k++) 
	{
		scale=0.0;
		for (i=k;i<n;i++) 
			scale=std::max((T)scale,(T)std::abs(rr(i,k)));
		if (scale == 0.0) 
		{
			sing=true;
			c[k]=d[k]=0.0;
		} else {
			for (i=k;i<n;i++) rr(i,k) /= scale;
			for (sum=0.0,i=k;i<n;i++) sum += rr(i,k)*rr(i,k);
			sigma=sign((T)sqrt(sum),(T)rr(k,k));
			rr(k,k) += sigma;
			c[k]=sigma*rr(k,k);
			d[k] = -scale*sigma;
			for (j=k+1;j<n;j++) {
				for (sum=0.0,i=k;i<n;i++) 
					sum += rr(i,k)*rr(i,j);
				tau=sum/c[k];
				for (i=k;i<n;i++) 
					rr(i,j) -= tau*rr(i,k);
			}
		}
	}
	d[n-1]=rr(n-1,n-1);
	if (d[n-1] == 0.0) 
		sing=true;
	for (i=0;i<n;i++) {
		for (j=0;j<n;j++) q(i,j)=0.0;
		q(i,i)=1.0;
	}
	for (k=0;k<n-1;k++) 
	{
		if (c[k] != 0.0) 
		{
			for (j=0;j<n;j++) 
			{
				sum=0.0;
				for (i=k;i<n;i++)
					sum += rr(i,k)*q(j,i);
				sum /= c[k];
				for (i=k;i<n;i++)
					q(j,i) -= sum*rr(i,k);
			}
		}
	}
	for (i=0;i<n;i++) 
	{
		rr(i,i)=d[i];
		for (j=0;j<i;j++) rr(i,j)=0.0;
	}
	for(unsigned i = 0; i < n;i++)
		for(unsigned j = i; j < n; j++)
			r(i,j)=rr(i,j);
	return !sing;
}


//qr decomposition by modified gram schmidt 
template<typename T>
bool qr_mgs(const mat<T> &a, mat<T> &q, up_tri_mat<T> &r) 
{
	unsigned n = a.nrows();
	unsigned m = a.ncols();
	assert(n == m);
	q.resize(n,n);
	r.resize(n);

	for(unsigned j = 0; j < m; j++)
	{	
		vec<T> v = a.col(j);
		for(unsigned i = 0; i < j; i++)
		{
			r(i,j) = dot(q.col(i),v);
			v= v- r(i,j)*q.col(i);
		}
		r(j,j) = length(v);
		if(std::abs(r(j,j)) == 0)
			return false;
		q.set_col(j, v/r(j,j));
		
	}
	return true;
}







// rq decomposition using tricky column and row permutations
template <typename T>
bool rq(const mat<T> &a, up_tri_mat<T> &r, mat<T> &q)
{
	unsigned N = a.nrows();
	r.resize(N);
	q.resize(N,N);
	mat<T> p=transpose(a);
	p.fliplr();
	p.flipud();
	if(!qr(p,q,r)) return false;
	mat<T> rr =r;
	rr.transpose();
	rr.fliplr();
	rr.flipud();
	
	q.transpose();
	q.fliplr();
	q.flipud();
	
	if (det(q)<0)
	{

		rr.set_col(0,-rr.col(0));
		q.set_row(0,-q.row(0));
		
	}
	
	for(unsigned i = 0; i< r.nrows();i++)
		for(unsigned j = i; j< r.ncols();j++)
			r(i,j)=rr(i,j);

	return true;
}







}

}