/*
 * Copyright (c) 1999-2019 Hartmut Seichter
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#ifndef PW_CORE_MATRIX_HPP
#define PW_CORE_MATRIX_HPP

#include <pw/core/globals.hpp>
#include <pw/core/math.hpp>
#include <pw/core/matrixbase.hpp>

#include <numeric>

namespace pw {

template <std::size_t R,std::size_t C, typename T, bool RowMajor = false>
struct matrix_ : matrixbase_<T, matrix_<R, C, T>>
{
	T data[R*C];

	using matrixbase_<T, matrix_<R, C, T>>::matrixbase_;

	static const std::size_t rows = R;
	static const std::size_t cols = C;

    typedef matrix_<R,1,T> col_type;
	typedef matrix_<1,C,T> row_type;
    typedef matrix_<C,R,T> transpose_type;

    matrix_(const matrix_<R,C,T>& other)
	{
		*this = other;
	}

    matrix_<R,C,T>& operator = (const matrix_<R,C,T>& other)
    {
        for (size_t i = 0; i < other.size();i++) (*this)[i] = other[i];
        return *this;
    }

    matrix_(std::initializer_list<T> args)
	{
		typename std::initializer_list<T>::iterator it = args.begin();
		for (;it != args.end();it++) data[it-args.begin()] = *it;
	}

	template <typename... Arguments>
	matrix_& set(Arguments ...values)
	{
		static_assert(sizeof...(Arguments) == R*C, "Incorrect number of arguments");
		data = {values... };
		return *this;
	}


//	//! get cell count
//	inline std::size_t coefficients() const { return this->size(); }

	inline size_t offset(size_t r,size_t c) const {
		return (RowMajor) ? r * C + c : c * R + r;
	}

	inline T& operator () (std::size_t r, std::size_t c) {
		return data[offset(r,c)];
	}

	inline const T& operator () (std::size_t r, std::size_t c) const {
		return data[offset(r,c)];
	}

	inline const T* ptr() const { return &data[0]; }

	//! set identity
	inline matrix_& set_identity()
	{
		for (std::size_t r = 0;r < rows; r++)
			for (std::size_t c = 0; c < cols; c++)
				(*this)(r,c) = (c == r) ? T(1) : T(0);
		return *this;
	}

	template<std::size_t Rs,std::size_t Cs,bool RowMajorSlice = RowMajor>
	auto slice(std::size_t r,std::size_t c) const
	{
		matrix_<Rs,Cs,T,RowMajorSlice> s;
		for (std::size_t ri = 0;ri < Rs;ri++)
			for (std::size_t ci = 0;ci < Cs;ci++)
				s(ri,ci) = (*this)(ri+r,ci+c);
		return s;
	}

	template<std::size_t Rs,std::size_t Cs,bool RowMajorSlice = RowMajor>
    matrix_& set_slice(const matrix_<Rs,Cs,T,RowMajorSlice>& s,
                       std::size_t r,std::size_t c)
	{
		for (std::size_t ri = 0;ri < Rs;ri++)
			for (std::size_t ci = 0;ci < Cs;ci++)
				(*this)(ri+r,ci+c) = s(ri,ci);
		return *this;
	}


	template<std::size_t Rs,std::size_t Cs,bool RowMajorSlice = RowMajor>
	auto minor(std::size_t r0,std::size_t c0)  const
	{
		matrix_<Rs,Cs,T,RowMajorSlice> m;
		size_t r = 0;
		for (size_t ri = 0; ri < R; ri++) {
			size_t c = 0;
			if (ri == r0)
				continue;
			for (size_t ci = 0; ci < C; ci++)
			{
				if (ci == c0)
					continue;
				m(r,c) = (*this)(ri,ci);
				c++;
			}
			r++;
		}
		return m;
	}

	T determinant() const {
		T det(0);
		for (size_t c = 0; c < C; c++)
			det += ((c % 2 == 0) ? (*this)(0,c) : -(*this)(0,c))
					* this->minor<R-1,C-1,RowMajor>(0,c).determinant();
		return det;
	}

	auto transposed() const {
        transpose_type res;
		for (size_t r = rows;r-->0;)
			for (size_t c = cols;c-->0;)
				res(c,r) = (*this)(r,c);
		return res;
	}

	auto inverse() const {
		T invDet = T(1) / this->determinant();
		matrix_<R,C,T,RowMajor> inv;
		for (int j = 0; j < C; j++)
			for (int i = 0; i < R; i++)
			{
				const T minorDet = this->minor<R-1,C-1,RowMajor>(j,i).determinant();
				const T coFactor = ((i + j) % 2 == 1) ? -minorDet : minorDet;
				inv(i, j) = invDet * coFactor;
			}
		return inv;
	}

	inline bool row_major() const {
		return RowMajor;
	}

	inline bool square() const { return R == C; }


	inline const matrix_ operator + (const matrix_ &other) const {
		matrix_ res(*this);
		for (size_t r = 0; r < R;r++)
			for (size_t c = 0; c < C;c++)
				res(r,c) += other(r,c);
		return res;
	}

	inline const matrix_ operator - (const matrix_ &other) const {
		matrix_ res(*this);
		for (size_t r = 0; r < R;r++)
			for (size_t c = 0; c < C;c++)
				res(r,c) -= other(r,c);
		return res;
	}

    auto row(size_t row_) const {
        row_type r;
        for (size_t i = 0; i < cols; i++) r[i] = (*this)(row_,i);
        return r;
	}

    auto column(size_t col_) const {
        col_type c;
        for (size_t i = 0; i < rows; i++) c[i] = (*this)(i,col_);
        return c;
    }
};

template <> inline
float matrix_<1,1,float>::determinant() const
{
	return (*this)(0,0);
}

template <> inline
double matrix_<1,1,double>::determinant() const
{
	return (*this)(0,0);
}


template <typename T, std::size_t R, std::size_t Ca,std::size_t Cb>
auto operator * (const matrix_<R, Ca, T>& A,
                 const matrix_<R, Cb, T>& B
                 )
{
    matrix_<R,Cb,T> result; result.zero(); // zero the output
    for (size_t r = 0; r < R; r++)
        for (size_t c = 0; c < Cb; c++)
            for (size_t iI = 0; iI < R; iI++)
                result(r,c) += A(r,iI) * B(iI,c); // inner product
    return result;
}


//
//
//

template <typename T> using matrix2x2_ = matrix_<2, 2, T>;
template <typename T> using matrix3x3_ = matrix_<3, 3, T>;
template <typename T> using matrix4x4_ = matrix_<4, 4, T>;

using matrix2x2f = matrix2x2_<float>;
using matrix2x2d = matrix2x2_<double>;
using matrix2x2  = matrix2x2_<real_t>;

using matrix3x3f = matrix3x3_<float>;
using matrix3x3d = matrix3x3_<double>;
using matrix3x3  = matrix3x3_<real_t>;

using matrix4x4f = matrix4x4_<float>;
using matrix4x4d = matrix4x4_<double>;
using matrix4x4  = matrix4x4_<real_t>;



}


#if __OLD

template <unsigned int R, unsigned int C, typename T>
class matrix_ : public matrixbase<T> {

	T m[R*C];

public:

    using typename matrixbase<T>::value_type;
    using typename matrixbase<T>::size_type;

	matrix_();

	matrix_(const matrix_& mtc);

	matrix_& operator = (const matrix_& other);

    matrix_(std::initializer_list<T> il)
    {
        this->set(il);
    }

    matrix_ set(std::initializer_list<T> il)
    {
        int i = 0;
        for (T e : il) (*this).at(i++) = e;
    }

	matrix_ transposed() const;

	inline matrix_& operator *= (const T& b) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) *= b; return *this; }
	inline matrix_& operator /= (const T& b) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) /= b; return *this; }
	inline matrix_& operator += (const T& b) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) += b; return *this; }
	inline matrix_& operator -= (const T& b) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) -= b; return *this; }

	inline matrix_& operator += (const matrix_& other) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) += other.at(i); return *this; }
	inline matrix_& operator -= (const matrix_& other) { for (unsigned int i = this->cells(); i--> 0;) this->at(i) -= other.at(i); return *this; }

	inline const matrix_ normalized() const {
		const T one_over_n = T(1) / this->norm();
		return *this * one_over_n;
	}


	inline const matrix_
	get_inverse() const
	{
		matrix_ resMat;

		for ( unsigned int r = 0; r < C; ++r) {
			for ( unsigned int j = 0; j < R; ++j) {
				short sgn = ( (r+j)%2) ? -1 : 1;
				matrix_<R-1,C-1,T> minor;
				this->get_minor(minor,r,j);
				resMat.at(r,j) = minor.determinant() * sgn;
			}
		}

		resMat = resMat.transposed();
		resMat *= (static_cast<T>(1)/this->determinant());
		return resMat;
	}

	inline
	matrix_& invert() {
		*this = this->get_inverse();
		return *this;
	}

	void get_minor(matrix_<R-1,C-1,T>& res, unsigned int r0, unsigned int c0) const;

	T determinant() const;

	T squared_norm() const;

	T norm() const;

	matrix_<R,C,T>& operator *= (const matrix_<R,C,T>& rhs);

	matrix_<R,C,T>& copy_from_data(const T* src) { for (unsigned int i = 0; i < R*C; ++i) { (*this).at(i) = src[i]; } return *this; }

	const matrix_<C,R,T> reshape() const {
		matrix_<C,R,T> m;
		for (unsigned int r = 0; r < R; ++r)
			for (unsigned int c = 0; c < C; ++c)
				m(r,c) = (*this)(c,r);
		return m;
	}

	const matrix_<R,1,T> get_column(unsigned int col) const {
		matrix_<R,1,T> c; for (unsigned int r = 0; r < R; ++r) c(r,0) = (this)(r,col);
		return c;
	}

	const matrix_<1,C,T> get_row(unsigned int row) const {
		matrix_<1,C,T> r; for (unsigned int c = 0; c < C; ++c) r(0,c) = (this)(row,c);
		return r;
	}

	void normalize();

    template <unsigned int bC>
    matrix_<R,bC,T> operator * (const matrix_<R,bC,T>& B) const
    {
        // aC == bR
        // set all null
        matrix_<R,bC,T> res;
        res.fill(0);

        // compute all resulting cells
        for (unsigned int r = 0; r < R; ++r) {
            for (unsigned int c = 0; c < bC; ++c) {
                // building inner product
                for (unsigned int iI = 0; iI < R;iI++) {
                    res.at(r,c) += (*this).at(r,iI) * B.at(iI,c);
                }
            }
        }
        return res;
    }
};

/////////////////////////////////////////////////////////////////////////////

template <unsigned int aR, unsigned int aC, typename T>
inline matrix_<aR,aC,T> operator * (const matrix_<aR,aC,T>& a, const T& b)
{
	matrix_<aR,aC,T> res;
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) * b;
	return res;
}

template <unsigned int aR, unsigned int aC, typename T>
inline matrix_<aR,aC,T> operator / (const matrix_<aR,aC,T>& a, const T& b)
{
	matrix_<aR,aC,T> res; T oneOverB(1./b);
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) * oneOverB;
	return res;
}

template <unsigned int aR, unsigned int aC, typename T>
inline matrix_<aR,aC,T> operator + (const matrix_<aR,aC,T>& a, const T& b)
{
	matrix_<aR,aC,T> res;
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) + b;
	return res;
}

template <unsigned int aR, unsigned int aC, typename T>
inline matrix_<aR,aC,T> operator - (const matrix_<aR,aC,T>& a, const T& b)
{
	matrix_<aR,aC,T> res;
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) - b;
	return res;
}

template <unsigned int R, unsigned int C, typename T>
inline matrix_<R,C,T> operator + (const matrix_<R,C,T>& a, const matrix_<R,C,T>& b)
{
	matrix_<R,C,T> res;
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) + b.at(i);
	return res;
}

template <unsigned int R, unsigned int C, typename T>
inline matrix_<R,C,T> operator - (const matrix_<R,C,T>& a, const matrix_<R,C,T>& b)
{
	matrix_<R,C,T> res;
	for (unsigned int i = res.cells(); i--> 0;) res.at(i) = a.at(i) - b.at(i);
	return res;
}



//template <unsigned int aR,unsigned int aCbR, unsigned int bC, typename T>
//matrix_<aR,bC,T> static inline
//mul(const matrix_<aR,aCbR,T>& A, const matrix_<aCbR,bC,T>& B)
//{
//	// aC == bR
//	// set all null
//	matrix_<aR,bC,T> res;
//	res.fill(0);

//	// compute all resulting cells
//	for (unsigned int r = 0; r < aR; ++r) {
//		for (unsigned int c = 0; c < bC; ++c) {
//			// building inner product
//			for (unsigned int iI = 0; iI < aCbR;iI++) {
//				res.at(r,c) += A.at(r,iI) * B.at(iI,c);
//			}
//		}
//	}
//	return res;
//}


/////////////////////////////////////////////////////////////////////////////




/////////////////////////////////////////////////////////////////////////////

template <unsigned int R, unsigned int C,typename T>
matrix_<R,C,T>& matrix_<R,C,T>::operator *= (const matrix_& rhs)
{
    *this = *this * rhs;
	return *this;
}

/////////////////////////////////////////////////////////////////////////////

template <unsigned int R, unsigned int C,typename T>
matrix_<R,C,T>::matrix_()
	: matrixbase<T>(R,C,&m[0],true)
{
}

template <unsigned int R, unsigned int C,typename T>
matrix_<R,C,T>::matrix_(const matrix_ &mtc) :
	matrixbase<T>(R,C,&m[0],true)
{
	*this = mtc;
}

template <unsigned int R, unsigned int C,typename T>
matrix_<R,C,T> &matrix_<R,C,T>::operator = (const matrix_<R,C,T> &other)
{
	if (this != &other)
		for (unsigned int r = 0; r < R;++r)
			for (unsigned int c = 0; c < C;++c)
				(*this).at(r,c) = other.at(r,c);
	return *this;
}

template <unsigned int R, unsigned int C,typename T>
matrix_<R,C,T> matrix_<R,C,T>::transposed() const
{
	matrix_<C,R,T> res;
	for (unsigned int r = this->rows();r-->0;)
		for (unsigned int c = this->cols();c-->0;)
			res.at(c,r) = this->at(r,c);
	return res;
}

template <unsigned int R, unsigned int C,typename T>
void matrix_<R,C,T>::get_minor(matrix_<R-1,C-1,T>& res,unsigned int r0, unsigned int c0) const
{
	unsigned int r = 0;
	for (unsigned int ri = 0; ri < R; ri++)
	{
		unsigned int c = 0;
		if (ri == r0) continue;
		for (unsigned int ci = 0; ci < C; ci++)
		{
			if (ci == c0) continue;
			res.data()[r*(C-1)+c] = this->data()[ri*C + ci];//(*this)(ri,ci);
			c++;
		}
		r++;
	}
}

template <unsigned int R, unsigned int C,typename T> inline
T matrix_<R,C,T>::determinant() const
{
	T res(0);

	matrix_<R-1,C-1,T> minor;

	// using Laplace Expansion at compile time
	for (unsigned int c = 0; c < C; c++) {
		this->get_minor(minor,0,c);
		res += ((c % 2 == 0) ? m[c] : -m[c]) *  minor.determinant();
	}

	return res;
}

template <unsigned int R, unsigned int C,typename T> inline
T matrix_<R,C,T>::squared_norm() const
{
	T res(0);

	for (unsigned int r = 0; r < R; ++r)
		for (unsigned int c = 0; c < C; ++c)
			res += ((*this).at(r,c) * (*this).at(r,c));
	return res;
}


template <unsigned int R, unsigned int C,typename T> inline
T matrix_<R,C,T>::norm() const
{
	using std::sqrt;

	return sqrt(this->squared_norm());
}

template <unsigned int R, unsigned int C,typename T> inline
void matrix_<R,C,T>::normalize()
{
	T n = norm();
	if (n > 0) {
		for (unsigned int r = 0; r < R; ++r)
			for (unsigned int c = 0; c < C; ++c)
				(*this)(r,c) /= n;
	}
}

/////////////////////////////////////////////////////////////////////////////

// explicit specializations

// 4x4

template <typename T>
class matrix4x4_ : public matrix_<4,4,T>
{
public:

	using matrix_<4,4,T>::matrix_;


    matrix4x4_(const matrix_<4,4,T>& i)
	{
		*this = i;
	}

    matrix4x4_& operator = (const matrix_<4,4,T>& rhs) {
		if (this != &rhs){
			this->at(0,0) = rhs.at(0,0);this->at(0,1) = rhs.at(0,1);this->at(0,2) = rhs.at(0,2);this->at(0,3) = rhs.at(0,3);
			this->at(1,0) = rhs.at(1,0);this->at(1,1) = rhs.at(1,1);this->at(1,2) = rhs.at(1,2);this->at(1,3) = rhs.at(1,3);
			this->at(2,0) = rhs.at(2,0);this->at(2,1) = rhs.at(2,1);this->at(2,2) = rhs.at(2,2);this->at(2,3) = rhs.at(2,3);
			this->at(3,0) = rhs.at(3,0);this->at(3,1) = rhs.at(3,1);this->at(3,2) = rhs.at(3,2);this->at(3,3) = rhs.at(3,3);
		}

		return *this;
	}



#if TACIT_PIXEL_STUFF_NEEDS_TO_MOVE_TO_SCENE

	matrix<4,4,T>&
	translate(const T& v1,const T& v2,const T& v3)
	{
		this->at(12) += v1;
		this->at(13) += v2;
		this->at(14) += v3;
		return *this;
	}

	matrix<4,4,T>&
	setTranslation(const T& v1,const T& v2,const T& v3)
	{
		this->identity();
		this->at(12) = v1;
		this->at(13) = v2;
		this->at(14) = v3;
		return *this;
	}

	matrix<4,4,T>&
	setScale(const T& v1,const T& v2,const T& v3)
	{
		this->identity();
		(*this)(0,0) = v1;
		(*this)(1,1) = v2;
		(*this)(2,2) = v3;
		return *this;
	}

	matrix<4,4,T>&
	scale(const T& v1,const T& v2,const T& v3)
	{
		(*this)(0,0) *= v1;
		(*this)(1,1) *= v2;
		(*this)(2,2) *= v3;
		return *this;
	}


	static
	matrix<4,4,T>
	Translation(const T& v1,const T& v2,const T& v3)
	{
		matrix44<T> res = matrix44<T>::Identity(); res.setTranslation(v1,v2,v3);
		return res;
	}

	inline static
	matrix<4,4,T>
	AngleAxis(const T& radianRotation,const matrix<3,1,T>& vec);



	inline static
	matrix<4,4,T> Frustum(T Left,T Right,T Bottom,T Top,T zNear,T zFar)
	{
		matrix<4,4,T> frustum;

		frustum.fill(0);

		frustum(0,0) = 2 * zNear/(Right-Left);
		frustum(1,1) = 2 * zNear/(Top-Bottom);

		frustum(0,2) =     (Right+Left)/(Right-Left);	//A
		frustum(1,2) =     (Top+Bottom)/(Top-Bottom);	//B
		frustum(2,2) = -   (zFar+zNear)/(zFar-zNear);	//C
		frustum(3,2) = -(2 * zFar*zNear)/(zFar-zNear);   //D

		frustum(2,3) = -1;

		return frustum;
	}


	inline static
	matrix<4,4,T> LookAt(const matrix<3,1,T>& eye,
						 const matrix<3,1,T>& target,
						 const matrix<3,1,T>& up);


	matrix<4,4,T>&
	rotate(const matrix<3,1,T>& vec, const T& rotation)
	{
		matrix44<T> rot = matrix44<T>::AngleAxis(rotation,vec); *this *= rot;
		return *this;
	}

#endif


};

//
// Specializations
//

template <> inline
float matrix_<1,1,float>::determinant() const
{
	return this->at(0);
}

template <> inline
double matrix_<1,1,double>::determinant() const
{
	return this->at(0);
}


#if TACIT_PIXEL_STUFF_NEEDS_TO_MOVE_TO_SCENE

template <typename T>
class matrix31 : public matrix<3,1,T> {
public:

	using matrix<3,1,T>::operator =;

	inline static
	matrix<3,1,T> Cross(const matrix<3,1,T>& vec1, const matrix<3,1,T>& vec2)
	{
		matrix<3,1,T> res;

		res.at(0) = vec1.at(1) * vec2.at(2) - vec2.at(1) * vec1.at(2);
		res.at(1) = vec1.at(2) * vec2.at(0) - vec2.at(2) * vec1.at(0);
		res.at(2) = vec1.at(0) * vec2.at(1) - vec2.at(0) * vec1.at(1);

		return res;
	}
};

template <typename T>
matrix<4,4,T>
matrix44<T>::AngleAxis(const T &radianRotation, const matrix<3,1,T> &vec)
{
	matrix44<T> R = matrix44<T>::Identity();

	if (vec.norm() < std::numeric_limits<T>::epsilon()) return R;

	T _fCos = (T) cos (radianRotation);

	matrix<3,1,T> _vCos(vec * (1 - _fCos));
	matrix<3,1,T> _vSin(vec * (T)sin(radianRotation));

	R.at(0) = (T) ((vec(0,0) * _vCos(0,0)) + _fCos);
	R.at(4) = (T) ((vec(0,0) * _vCos(1,0)) - _vSin(2,0));
	R.at(8) = (T) ((vec(0,0) * _vCos(2,0)) + _vSin(1,0));

	R.at(1) = (T) ((vec(1,0) * _vCos(0,0)) + _vSin(2,0));
	R.at(5) = (T) ((vec(1,0) * _vCos(1,0)) + _fCos);
	R.at(9) = (T) ((vec(1,0) * _vCos(2,0)) - _vSin(0,0));

	R.at(2) = (T)  ((vec(2,0) * _vCos(0,0)) - _vSin(1,0));
	R.at(6) = (T)  ((vec(2,0) * _vCos(1,0)) + _vSin(0,0));
	R.at(10)= (T)  ((vec(2,0) * _vCos(2,0)) + _fCos);

	return R;
}

template <typename T>
matrix<4,4,T>
matrix44<T>::LookAt(const matrix<3,1,T> &eye, const matrix<3,1,T> &target, const matrix<3,1,T> &up)
{
	matrix<4,4,T> lookat = matrix<4,4,T>::Identity();

	matrix<3,1,T> L; L = target - eye;
	L.normalize();
	matrix<3,1,T> S = matrix31<T>::Cross(L,up);
	S.normalize();
	matrix<3,1,T> Ud = matrix31<T>::Cross(S,L);
	Ud.normalize();

	lookat(0,0) = S.at(0);
	lookat(0,1) = S.at(1);
	lookat(0,2) = S.at(2);
	lookat(0,3) = T(0);

	lookat(1,0) = Ud.at(0);
	lookat(1,1) = Ud.at(1);
	lookat(1,2) = Ud.at(2);
	lookat(1,3) = T(0);

	lookat(2,0) = -L.at(0);
	lookat(2,1) = -L.at(1);
	lookat(2,2) = -L.at(2);
	lookat(3,2) = T(0);

	lookat(3,0) = eye.at(0);
	lookat(3,1) = eye.at(1);
	lookat(3,2) = eye.at(2);
	lookat(3,3) = 1;

	return lookat;

}

#endif


// predefined matricies

typedef matrix4x4_<real_t> matrix4x4;

typedef matrix4x4_<double> matrix4x4d;
typedef matrix4x4_<float> matrix4x4f;

}

#endif


#endif


