/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-07-15
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef DOUBLEMATRIX_H
#define DOUBLEMATRIX_H

#include <QGpCoreTools.h>

#include "QGpCoreMathDLLExport.h"
#include "Matrix.h"
#include "Segment.h"
#include "MatrixIterator.h"

namespace QGpCoreMath {

class QGPCOREMATH_EXPORT DoubleMatrix: public Matrix<double>
{
public:
  DoubleMatrix() : Matrix<double>() {}
  DoubleMatrix(int ndim) : Matrix<double>(ndim) {}
  DoubleMatrix(int nrow, int ncol) : Matrix<double>(nrow, ncol) {}
  DoubleMatrix(const Matrix<double> m) : Matrix<double>(m) {}
  DoubleMatrix(const DoubleMatrix& m) : Matrix<double>(m) {}

  void rotation(AxisType ax, const Angle& angle);
  void random(double min=0.0, double max=1.0);
  bool invert();
  void singularValues(QVector<double>& values, DoubleMatrix * u=nullptr, DoubleMatrix * vt=nullptr) const;
  QVector<double> symetricEigenValues(bool& ok) const;
  DoubleMatrix leastSquare(const DoubleMatrix& b) const;
};

class QGPCOREMATH_EXPORT Matrix2x2: public DoubleMatrix
{
public:
  Matrix2x2() : DoubleMatrix(2, 2) {}
  Matrix2x2(const Matrix2x2& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {}
  Matrix2x2(const Matrix<double>& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {ASSERT(o.columnCount()==2 && o.rowCount()==2);}

  inline void rotation(const Angle& angle);
  Matrix2x2 operator*(const Matrix2x2& m) const {return DoubleMatrix::operator*(m);}
  inline Point2D operator*(const Point2D& p) const;
  double determinant() const;
};

inline void Matrix2x2::rotation(const Angle& angle)
{
  DoubleMatrix::rotation(ZAxis, angle);
}

inline Point2D Matrix2x2::operator*(const Point2D& p) const
{
  Point2D rp;
  const double * values=_d->values();
  rp.setX(values[0]*p.x()+values[2]*p.y());
  rp.setY(values[1]*p.x()+values[3]*p.y());
  return rp;
}

class QGPCOREMATH_EXPORT Matrix3x3: public DoubleMatrix
{
public:
  Matrix3x3() : DoubleMatrix(3, 3) {}
  Matrix3x3(const Matrix3x3& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {}
  Matrix3x3(const Matrix<double>& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {ASSERT(o.columnCount()==3 && o.rowCount()==3);}

  inline void rotation(AxisType ax, const Angle& angle);

  Matrix3x3 operator*(const Matrix3x3& m) const {return DoubleMatrix::operator*(m);}
  inline Point operator*(const Point& p) const;
  Segment operator*(const Segment& s) const;
};

inline void Matrix3x3::rotation(AxisType ax, const Angle& angle)
{
  identity(0.0);
  DoubleMatrix::rotation(ax, angle);
}

inline Point Matrix3x3::operator*(const Point& p) const
{
  TRACE;
  Point rp;
  const double * values=_d->values();
  rp.setX(values[0]*p.x()+values[3]*p.y()+values[6]*p.z());
  rp.setY(values[1]*p.x()+values[4]*p.y()+values[7]*p.z());
  rp.setZ(values[2]*p.x()+values[5]*p.y()+values[8]*p.z());
  return rp;
}

class QGPCOREMATH_EXPORT Matrix4x4: public DoubleMatrix
{
public:
  Matrix4x4() : DoubleMatrix(4, 4) {}
  Matrix4x4(const Matrix4x4& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {}
  Matrix4x4(const Matrix<double>& o) : DoubleMatrix(static_cast<const DoubleMatrix&>(o)) {ASSERT(o.columnCount()==4 && o.rowCount()==4);}

  inline void rotation(AxisType ax, const Angle& angle);
  Matrix3x3 rotationOnly() const;

  DoubleMatrix operator*(const DoubleMatrix& m) const {return DoubleMatrix::operator*(m);}
  inline Point operator*(const Point& p) const;
  Segment operator*(const Segment& s) const;
};

inline void Matrix4x4::rotation(AxisType ax, const Angle& angle)
{
  identity(0.0);
  DoubleMatrix::rotation(ax, angle);
}

inline Point Matrix4x4::operator*(const Point& p) const
{
  TRACE;
  Point rp;
  const double * values=_d->values();
  rp.setX(values[0]*p.x()+values[4]*p.y()+values[8]*p.z()+values[12]);
  rp.setY(values[1]*p.x()+values[5]*p.y()+values[9]*p.z()+values[13]);
  rp.setZ(values[2]*p.x()+values[6]*p.y()+values[10]*p.z()+values[14]);
  return rp;
}

} // namespace QGpCoreMath

#endif // DOUBLEMATRIX_H
