/***************************************************************************
**
**  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 MATRIXMULTIPLY_H
#define MATRIXMULTIPLY_H

#include "AbstractNumericalKey.h"
#include "AbstractNumericalCache.h"
#include "QGpCoreMathDLLExport.h"

namespace QGpCoreMath {

class QGPCOREMATH_EXPORT MatrixMultiplyKey : public AbstractNumericalKey
{
  DECLARE_NUMERICALKEY(MatrixMultiplyKey)
public:
  MatrixMultiplyKey(int nRows1, int commonDim, int nColumns2) {
    _nRows1=nRows1;
    _commonDim=commonDim;
    _nColumns2=nColumns2;
  }
  virtual bool operator==(const AbstractNumericalKey& o) {
    const MatrixMultiplyKey& mo=static_cast<const MatrixMultiplyKey&>(o);
    return _nRows1==mo._nRows1 && _commonDim==mo._commonDim && _nColumns2==mo._nColumns2;
  }
  virtual int hash() const {return 256*256*_nRows1+256*_nColumns2+_commonDim;}
  inline virtual AbstractNumericalCache * createCache();

  int nRows1() const {return _nRows1;}
  int commonDim() const {return _commonDim;}
  int nColumns2() const {return _nColumns2;}
  int indexCount() const {return _nRows1*_nColumns2*_commonDim;}
private:
  int _nRows1, _commonDim, _nColumns2;
};

class QGPCOREMATH_EXPORT MatrixMultiply : public AbstractNumericalCache
{
public:
  MatrixMultiply(MatrixMultiplyKey * key);
  ~MatrixMultiply();

  static inline const MatrixMultiply * begin(int nRows1, int commonDim, int nColumns2);
  virtual void init();

  class IndexMap
  {
  public:
    IndexMap() {}
    void set(int rIndex, int f1Index, int f2Index) {
      _resultIndex=rIndex;
      _factor1Index=f1Index;
      _factor2Index=f2Index;
    }

    int resultIndex() const {return _resultIndex;}
    int factor1Index() const {return _factor1Index;}
    int factor2Index() const {return _factor2Index;}
  private:
    int _resultIndex, _factor1Index, _factor2Index;
  };
  inline int indexCount() const;
  const IndexMap& indexMap(int i) const {return _indexes[i];}

  static inline bool isValid(uint nRows1, uint commonDim, uint nColumns2);
private:
  IndexMap * _indexes;
};

inline const MatrixMultiply * MatrixMultiply::begin(int nRows1, int commonDim, int nColumns2)
{
  return static_cast<const MatrixMultiply *>(
      AbstractNumericalCache::begin(new MatrixMultiplyKey(nRows1, commonDim, nColumns2)));
}

inline AbstractNumericalCache * MatrixMultiplyKey::createCache( )
{
  return new MatrixMultiply(this);
}

inline int MatrixMultiply::indexCount() const
{
  const MatrixMultiplyKey& k=static_cast<const MatrixMultiplyKey&>(key());
  return k.indexCount();
}

inline bool MatrixMultiply::isValid(uint nRows1, uint commonDim, uint nColumns2)
{
  return nRows1<256 && commonDim<256 && nColumns2<256;
}

} // namespace QGpCoreMath

#endif // MATRIXMULTIPLY_H
