/****************************************************************************
* TMesh                                                                  *
*                                                                           *
* Consiglio Nazionale delle Ricerche                                        *
* Istituto di Matematica Applicata e Tecnologie Informatiche                *
* Sezione di Genova                                                         *
* IMATI-GE / CNR                                                            *
*                                                                           *
* Authors: Marco Attene                                                     *
* Copyright(C) 2012: IMATI-GE / CNR                                         *
* All rights reserved.                                                      *
*                                                                           *
* This program is dual-licensed as follows:                                 *
*                                                                           *
* (1) You may use TMesh as free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as published  *
* by the Free Software Foundation; either version 3 of the License, or      *
* (at your option) any later version.                                       *
* In this case the 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             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
* (2) You may use TMesh as part of a commercial software. In this case a *
* proper agreement must be reached with the Authors and with IMATI-GE/CNR   *
* based on a proper licensing contract.                                     *
*                                                                           *
****************************************************************************/

#ifndef MATRIX_H
#define MATRIX_H

#include <stdio.h>
#include <float.h>
#include "coordinates.h"
#include "list.h"

namespace T_MESH
{

//////////////////////////////////////////////////////////////////////////
//
// Generic 3x3 matrix
//
//////////////////////////////////////////////////////////////////////////

//! Generic 3x3 matrix.

//! Elements are stored in a row-dominant order, thus
//! for example, M[4] is the first element of the second row.

class Matrix3x3
{
 public:
 double M[9];	//!< Actual values of the matrix

 Matrix3x3() {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=M[6]=M[7]=M[8]=0.0;} //!< Contructs a null matrix

 //! Constructs a fully initialized matrix.
 Matrix3x3(const double& a11, const double& a12, const double& a13,
           const double& a21, const double& a22, const double& a23, 
           const double& a31, const double& a32, const double& a33);

 //! Constructs a 3x3 matrix as the product of Transpose(v1,v2,v3) and (w1,w2,w3).
 Matrix3x3(const double& v1, const double& v2, const double& v3,
           const double& w1, const double& w2, const double& w3); 

 //! Constructs a 3x3 matrix as the product of Transpose(a,b,c) and (a,b,c).
 Matrix3x3(const double& a, const double& b, const double& c);

 //! Returns TRUE if the matrix is symmetric
 bool isSymmetric() const {return (M[2]==M[4] && M[3]==M[7] && M[6]==M[8]);}


 //! Initializes all elements to 'd'
 void operator=(const double& d) {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=M[6]=M[7]=M[8]=d;}

 void operator+=(const Matrix3x3&); //!< Sum another matrix
 void operator-=(const Matrix3x3&); //!< Subtract another matrix
 void operator*=(const double&);    //!< Multiply by a scalar
 void operator/=(const double& d) {operator *=(1.0/d);}       //!< Divide by a scalar
 Matrix3x3 operator+(const Matrix3x3&) const; //!< Returns the sum of this and another matrix
 Matrix3x3 operator*(const double&) const;    //!< Returns the product of this matrix with a scalar
 Matrix3x3 operator*(const Matrix3x3&) const; //!< Returns the product of this and another matrix (rows by columns)
 Matrix3x3 operator~() const;		 //!< Returns the transpose of this matrix

 //! Return the matrix transpose
 Matrix3x3 transpose() const;

 //! Returns Transpose(a,b,c)*M*(a,b,c)

 //! Returns the (scalar) result of multiplying the matrix on
 //! the left and on the right by the vector (a,b,c).
 double lrMultiply(const double& a, const double& b, const double& c) const;

 //! Returns the (scalar) result of v*M*w
 double lrMultiply(const double& v1, const double& v2, const double& v3,
                   const double& w1, const double& w2, const double& w3) const;
};


//////////////////////////////////////////////////////////////////////////
//
// Symmetric 3x3 matrix
//
//////////////////////////////////////////////////////////////////////////

//! Symmetric 3x3 matrix

//! Compact storage:	\n
//! M[0] M[1] M[3]	\n
//! M[1] M[2] M[4]	\n
//! M[3] M[4] M[5]	\n

class SymMatrix3x3
{
 public:
 double M[6];	//!< Actual values of the matrix

 SymMatrix3x3() {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=0.0;} //!< Constructs a null matrix.

 //! Constructs a fully initialized matrix.
 SymMatrix3x3(const double&a11, const double&a12, const double&a22,
              const double&a13, const double&a23, const double&a33);

 //! Constructs a symmetric matrix as the product of Transpose(a,b,c) and (a,b,c).
 SymMatrix3x3(const double& a, const double& b, const double& c);

 //! Constructs a symmetric 3x3 matrix as a copy of an existing 3x3 matrix S.

 //! If 'S' is not symmetric, its upper triangular part is reflected to the lower.

 SymMatrix3x3(const Matrix3x3& S);

 bool operator==(const SymMatrix3x3& s) //!< True iff all entries are equal
  {return (M[0]==s.M[0] && M[1]==s.M[1] && M[2]==s.M[2] && M[3]==s.M[3] && M[4]==s.M[4] && M[5]==s.M[5]);}
 bool operator!=(const SymMatrix3x3& s) //!< True iff at least one different entry
  {return (M[0]!=s.M[0] || M[1]!=s.M[1] || M[2]!=s.M[2] || M[3]!=s.M[3] || M[4]!=s.M[4] || M[5]!=s.M[5]);}

 void operator+=(const SymMatrix3x3&); //!< Sum another matrix
 void operator-=(const SymMatrix3x3&); //!< Subtract another matrix
 void operator*=(const double&);       //!< Multiply by a scalar
 void operator/=(const double& d) {operator *=(1.0/d);}       //!< Divide by a scalar
 SymMatrix3x3 operator+(const SymMatrix3x3&) const; //!< Returns the sum of this and another matrix
 SymMatrix3x3 operator*(const double&) const; //!< Returns the product of this matrix with a scalar

 //! Initializes all elements to 'd'
 void operator=(const double& d) {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=d;}

 //! Returns the determinant
 double determinant() const {return (M[0]*M[2]*M[5])+(2.0*M[1]*M[3]*M[4])-(M[0]*M[4]*M[4])-(M[2]*M[3]*M[3])-(M[5]*M[1]*M[1]);}

 //! Returns TRUE iff the matrix is made of all zeroes.
 bool isNull() const {return (M[0]==0 && M[1]==0 && M[2]==0 && M[3]==0 && M[4]==0 && M[5]==0);}

 //! Returns Transpose(a,b,c)*M*(a,b,c)

 //! Returns the (scalar) result of multiplying the matrix on
 //! the left and on the right by the vector (a,b,c).
 double lrMultiply(const double& a, const double& b, const double& c) const;


 //! Returns the (scalar) result of v*M*w
 double lrMultiply(const double& v1, const double& v2, const double& v3,
                   const double& w1, const double& w2, const double& w3) const;

 bool invert(); //!< Inverts this matrix. Returns FALSE if not invertible, TRUE otherwise

 //! Returns the matrix trace
 double trace() const {return M[0]+M[2]+M[5];}

 //! Compute eigenvalues and eigenvectors of the matrix (Jacobi method).

 //! The calling function is responsible of verifying that the matrix
 //! is diagonalizable. Also, eigen_vals and eigen_vecs must be allocated
 //! prior to calling this method.\n
 //! eigen_vals are sorted in ascending order (i.e., eigen_vals[0] is the smallest one).\n
 //! eigen_vecs are sorted accordingly to the order of eigen_vals, that is,
 //! (eigen_vecs[0], eigen_vecs[1], eigen_vecs[2]) is the eigenvector corresponding to
 //! eigen_vals[0].
 void diagonalize(double eigen_vals[3], double eigen_vecs[9]) const;

 //! Compute the eigenvalues l1, l2 and l3.

 //! This method is much faster and precise than 'diagonalize', as it uses
 //! an analytical direct method instead of an iterative approach.
 void getEigenvalues(double *l1, double *l2, double *l3) const;

 //! Compute the eigenvector (a,b,c) corresponding to the minimum eigenvalue.

 //! This method is much faster and precise than 'diagonalize', as it uses
 //! an analytical direct method instead of an iterative approach.
 //! Returns the corresponding eigenvalue
 double getMinEigenvector(double *a, double *b, double *c) const;

 //! This method is much faster and precise than 'diagonalize', as it uses
 //! an analytical direct method instead of an iterative approach.
 void getMaxEigenvector(double *a, double *b, double *c) const;

 //! Prints the contents of the matrix to the specified FILE id.

 //! If no 'id' is specifyed, results are printed to stdout.

 void print(FILE *id =stdout) const;
};


//////////////////////////////////////////////////////////////////////////
//
// Symmetric 4x4 matrix
//
//////////////////////////////////////////////////////////////////////////

//! Symmetric 4x4 matrix.

//! Compact storage:	\n
//! a2 ab ac ad		\n
//! ab b2 bc bd		\n
//! ac bc c2 cd		\n
//! ad bd cd d2		\n

class SymMatrix4x4
{
 public:
 coord a2,ab,ac,ad,b2,bc,bd,c2,cd,d2;	 	    //!< Actual matrix coeffs.

 SymMatrix4x4() {a2=ab=ac=ad=b2=bc=bd=c2=cd=d2=0;}  //!< Constructs a null matrix

 //! Extend a 3x3 symmetric matrix to homogeneous coordinates (ad=bd=cd=0 and d2=1).
 SymMatrix4x4(const SymMatrix3x3&);

 //! Quadric (a,b,c,d)*Transpose(a,b,c,d)
 SymMatrix4x4(const coord& a, const coord& b, const coord& c, const coord& d);

 bool operator==(const SymMatrix4x4&);	 	    //!< True iff equal
 bool operator!=(const SymMatrix4x4&);	 	    //!< True iff not equal

 void operator+=(const SymMatrix4x4&);	 	    //!< Sum another matrix
 SymMatrix4x4 operator+(const SymMatrix4x4&) const; //!< Returns the sum of this and another matrix
 SymMatrix4x4 operator*(const coord&) const;	    //!< Returns the product of this matrix by a scalar

 //! Adds the quadric (a,b,c,d)*Transpose(a,b,c,d)
 void add(const coord& a, const coord& b, const coord& c, const coord& d);  

 //! Returns Transpose(a,b,c,d)*M*(a,b,c,d)

 //! Returns the (scalar) result of multiplying the matrix on
 //! the left and on the right by the vector (a,b,c,d).
 coord lrMultiply(const coord& a, const coord& b, const coord& c, const coord& d) const;

 //! \brief Computes the vector (a,b,c) that minimizes the quantity lrMultiply(a,b,c,1).
 //! Returns FALSE if such a vector is not unique.
 bool getMinimizer(coord *a, coord *b, coord *c) const;

 bool invert();	//!< Inverts this matrix. Returns FALSE if not invertible, TRUE otherwise
};


//////////////////////////////////////////////////////////////////////////
//
// Generic 4x4 matrix
//
//////////////////////////////////////////////////////////////////////////

//! Generic 4x4 matrix.

class Matrix4x4
{
 public:
 double matrix[4][4]; //!< Actual matrix coefficients

 //! Constructs an undefined matrix
 Matrix4x4();

 //! Constructs a diagonal matrix with 'd' values on the diagonal
 Matrix4x4(const double& d);

 //! Constructs a fully initialized matrix (parameters are in row dominant order M[0][0], M[0][1], ...).
 Matrix4x4(
	   const double&, const double&, const double&, const double&,
	   const double&, const double&, const double&, const double&,
	   const double&, const double&, const double&, const double&,
	   const double&, const double&, const double&, const double&
	  );

 //! Rotation matrix from a quaternion
 void setRotation(const double &, const double&, const double&, const double&);

 //! Translation matrix from a vector
 void setTranslation(const double &, const double&, const double&);

 Matrix4x4 operator*(const Matrix4x4&) const; //!< Returns the product of this and another matrix (rows by columns)

 void transform(double *, double *, double *); //! Transform the vector by left-multiplication with the matrix
};

} //namespace T_MESH

#endif // MATRIX_H

