// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2010 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WGENERICMATRIX_H_
#define WGENERICMATRIX_H_

#include <Wt/WDllDefs.h>
#ifdef _MSC_VER
// Avoid 64-bit related warnings on MSVC
#pragma warning( push )
#pragma warning( disable : 4244 )
#pragma warning( disable : 4267 )
#endif
#define BOOST_SERIALIZATION_NO_LIB
#include <boost/numeric/ublas/matrix.hpp>
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/io.hpp>

#include <ostream>

namespace Wt {

/*! \class WGenericMatrix Wt/WGenericMatrix Wt/WGenericMatrix
 * \brief A value class that describes a matrix
 *
 * This class represents a fixed-size dense (!= sparse) matrix. It
 * can be templatized to the number of rows and columns, and to the
 * datatype stored (integer types, floatin point types, complex types, ...)
 *
 * The row order of this matrix class is row-major. This means that when
 * accessing the raw data store linearly, you will first encounter all
 * elements of the first row, then the second row, and so on.
 *
 * This template class is used in Wt as base class for transformation
 * matrices, but can also be used as a general matrix class. Efficiency
 * for this use case was considered when this class was implemented, but
 * we recommend that you use a more specialized matrix class library
 * if the algorithms you need exceed what's offered here (for example,
 * if you intend to do many linear algebra computations, you may
 * consider boost ublass, MTL, ...).
 */
template<typename T, std::size_t Rows, std::size_t Cols>
class WGenericMatrix
{
public:
  typedef boost::numeric::ublas::bounded_matrix<T, Rows, Cols,
    boost::numeric::ublas::row_major> MatrixType;
  typedef typename boost::numeric::ublas::bounded_matrix<T, Rows, Cols,
    boost::numeric::ublas::row_major>::array_type ArrayType;

  /*! \brief Construct a identity matrix
   *
   * An identity matrix in this context is a matrix where m(i,i) = 1
   * and m(i,j) = 0, for i != j.
   */
  WGenericMatrix()
  {
    setToIdentity();
  }

  /*! \brief Copy Constructor
   */
  WGenericMatrix(const WGenericMatrix<T, Rows, Cols> &other): m_(other.m_) {}

  /*! \brief Constructs a matrix from an array of elements.
   *
   * The input array is assumed to be in row-major order. If elements is 0,
   * the matrix data is not initialized.
   */
  explicit WGenericMatrix(const T* elements)
  {
    if (elements) {
      for(unsigned int i = 0; i < Rows; ++i)
        for(unsigned int j = 0; j < Cols; ++j)
          m_(i, j) = elements[i * Rows + j];
    }
  }

  /*! \brief Returns a const pointer to the internal data store.
   *
   * The array can be indexed with []. You can iterate over the
   * entire data store by using begin() and end() iterators. The
   * row order of the data is row major.
   */
  const ArrayType &constData() const { return m_.data(); }

  /*! \brief Export the matrix data
   *
   * Stores the matrix in an array of Rows*Cols elements of type T,
   * pointed to by data. The data will be stored in row major order.
   */
  void copyDataTo(T *data)
  {
    for(unsigned int i = 0; i < Rows; ++i)
      for (unsigned int j = 0; j < Cols; ++j)
        data[i * Rows + j] = m_(i, j);
  }

  /*! \brief Returns a reference to the internal data store.
   *
   * The array can be indexed with []. You can iterate over the
   * entire data store by using begin() and end() iterators. The
   * row order of the data is row major.
   */
  ArrayType &data() { return m_.data(); }

  /*! \brief Returns a const reference to the internal data store.
   *
   * The array can be indexed with []. You can iterate over the
   * entire data store by using begin() and end() iterators. The
   * row order of the data is row major.
   */
  const ArrayType &data() const { return m_.data(); }

  /*! \brief Fills every element of the matrix with the given value
   */
  void fill(T value)
  {
    for (unsigned i = 0; i < Rows * Cols; ++i)
      m_.data()[i] = value;
  }

  /*! \brief Identity check.
   *
   * Returns true if the transform represents an identity transformation.
   */
  bool isIdentity() const
  {
    using namespace boost::numeric::ublas;
    identity_matrix<T> I(Rows > Cols ? Rows : Cols);
    for(unsigned i = 0; i < Rows; ++i)
      for (unsigned j = 0; j < Cols; ++j)
        if (m_(i, j) != I(i, j))
          return false;
    return true;
  }

  /*! \brief Set this matrix to the identity matrix
   *
   * An identity matrix is in this context a matrix where m(i,i) = 1
   * and m(i,j) = 0, for i != j.
   */
  void setToIdentity()
  {
    #ifndef WT_TARGET_JAVA
    using namespace boost::numeric::ublas;
    m_ = project(identity_matrix<T>(Rows > Cols ? Rows : Cols),
        range(0, Rows), range(0, Cols));
    #endif
  }

  /*! \brief Returns the transposed of the matrix
   */
  WGenericMatrix<T, Cols, Rows> transposed() const
  {
    return WGenericMatrix<T, Cols, Rows>(boost::numeric::ublas::trans(m_));
  }

  /*! \brief Equality operator.
   *
   * Returns \c true if the matrices are exactly the same.
   */
  bool operator==(const WGenericMatrix<T, Rows, Cols>& rhs) const
  {
    for(unsigned i = 0; i < Rows; ++i)
      for (unsigned j = 0; j < Cols; ++j)
        if (rhs.m_(i, j) != m_(i, j))
          return false;
    return true;
  }

  /*! \brief Inequality operator.
   *
   * Returns \c true if the transforms are different.
   */
  bool operator!=(const WGenericMatrix<T, Rows, Cols> &rhs) const {
    return !(*this == rhs);
  }

  /*! \brief Returns the element at the given position
   */
  const T &operator()(int row, int column) const
  {
    return m_(row, column);
  }

  /*! \brief Returns the element at the given position
   */
  T &operator()(int row, int column) { return m_(row, column); }

  /*! \brief Returns the element at the given position
   */
  const T &at(int row, int column) const
  {
    return m_(row, column);
  }

  /*! \brief Returns the element at the given position
   */
  T &at(int row, int column) { return m_(row, column); } 

#ifdef WT_TARGET_JAVA
  void setElement(int row, int column, float v);
#endif

  /*! \brief Multiply every element of the matrix with the given factor
   */
  WGenericMatrix<T, Rows, Cols> &operator*=(const T &factor)
  {
#ifndef WT_TARGET_JAVA
    m_ *= factor;
    return *this;
#endif
  }

  /*! \brief Divide every element of the matrix by the given factor
   */
  WGenericMatrix<T, Rows, Cols> &operator/=(const T &factor)
  {
#ifndef WT_TARGET_JAVA
    m_ /= factor;
    return *this;
#endif
  }

  /*! \brief Add the given matrix to this matrix
   */
  WGenericMatrix<T, Rows, Cols> &operator+=(
    const WGenericMatrix<T, Rows, Cols> &rhs)
  {
#ifndef WT_TARGET_JAVA
    m_ += rhs.m_;
    return *this;
#endif
  }

  /*! \brief Substract the given matrix from this matrix
   */
  WGenericMatrix<T, Rows, Cols> &operator-=(
    const WGenericMatrix<T, Rows, Cols> &rhs)
  {
#ifndef WT_TARGET_JAVA
    m_ -= rhs.m_;
    return *this;
#endif
  }

  MatrixType &impl() { return m_; }
  const MatrixType &impl() const { return m_; }
  WGenericMatrix(const MatrixType &m): m_(m) {}

private:
  MatrixType m_;
};

/*! \brief Multiply two matrices
 */
template<typename T, std::size_t A, std::size_t B, std::size_t C>
inline WGenericMatrix<T, A, C> operator*(const WGenericMatrix<T, A, B> &l,
  const WGenericMatrix<T, B, C> &r)
{
#ifndef WT_TARGET_JAVA
  using namespace boost::numeric::ublas;
  return WGenericMatrix<T, A, C>(prod(l.impl(), r.impl()));
#endif
}

/*! \brief Print the matrix to an ostream
 */
template<typename T, std::size_t Rows, std::size_t Cols>
std::ostream &operator<<(std::ostream &os,
  const WGenericMatrix<T, Rows, Cols> &m)
{
#ifndef WT_TARGET_JAVA
  return os << m.impl();
#endif
}

/*! \brief Multiply every element in the matrix with the given factor
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator*(const T &factor,
  const WGenericMatrix<T, Rows, Cols> &m)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(factor * m.impl());
#endif
}

/*! \brief Multiply every element in the matrix with the given factor
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator*(
  const WGenericMatrix<T, Rows, Cols> &m, const T &factor)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(m.impl() * factor);
#endif
}

/*! \brief Divide every element in the matrix by the given factor
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator/(
  const WGenericMatrix<T, Rows, Cols> &m, const T &factor)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(m.impl() / factor);
#endif
}

/*! \brief Add two matrices together
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator+(
  const WGenericMatrix<T, Rows, Cols> &l,
  const WGenericMatrix<T, Rows, Cols> &r)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(l.impl() + r.impl());
#endif
}

/*! \brief Substract two matrices
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator-(
  const WGenericMatrix<T, Rows, Cols> &l,
  const WGenericMatrix<T, Rows, Cols> &r)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(l.impl() - r.impl());
#endif
}

/*! \brief Negate every element in the matrix
 */
template<typename T, std::size_t Rows, std::size_t Cols>
inline WGenericMatrix<T, Rows, Cols> operator-(
  const WGenericMatrix<T, Rows, Cols> &m)
{
#ifndef WT_TARGET_JAVA
  return WGenericMatrix<T, Rows, Cols>(-m.impl());
#endif
}

}
#endif // WGENERICMATRIX_H_
