//#####################################################################
// Copyright 2006-2007, Geoffrey Irving, Craig Schroeder, Tamar Shinar.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Header MATRIX_FORWARD
//#####################################################################
#ifndef __MATRIX_FORWARD__
#define __MATRIX_FORWARD__

#include <Core/Utilities/TYPE_UTILITIES.h>
namespace PhysBAM{

template<class T,class T_MATRIX> class MATRIX_BASE;
template<class T,int m_input,int n_input=m_input> class MATRIX;
template<class T,int d> class DIAGONAL_MATRIX;
template<class T,int d> class SYMMETRIC_MATRIX;
template<class T> class MATRIX_MXN;
template<class T> class MATRIX_VIEW;
template<class T> class SPARSE_MATRIX_FLAT_MXN;
template<class T,int mm,int nn=mm> class ZERO_MATRIX;
template<class T,int d> class IDENTITY_MATRIX;
template<class T,int d> class SCALE_MATRIX;

template<class T> struct IS_SCALAR_BLOCK;
template<class T,int m,int n> struct IS_SCALAR_BLOCK<MATRIX<T,m,n> > {static const bool value=IS_SCALAR_BLOCK<T>::value;};
template<class T,int m,int n,class RW> struct IS_BINARY_IO_SAFE<MATRIX<T,m,n>,RW> {static const bool value=(m>0) && (n>0) && IS_BINARY_IO_SAFE<T,RW>::value;};

template<class T_MATRIX> struct IS_MATRIX {static const bool value=false;};
template<class T> struct IS_MATRIX<MATRIX_MXN<T> > {static const bool value=true;};
template<class T> struct IS_MATRIX<MATRIX_VIEW<T> > {static const bool value=true;};
template<class T> struct IS_MATRIX<SPARSE_MATRIX_FLAT_MXN<T> > {static const bool value=true;};
template<class T,int m,int n> struct IS_MATRIX<ZERO_MATRIX<T,m,n> > {static const bool value=true;};
template<class T,int m,int n> struct IS_MATRIX<MATRIX<T,m,n> > {static const bool value=true;};
template<class T,int d> struct IS_MATRIX<SYMMETRIC_MATRIX<T,d> > {static const bool value=true;};
template<class T,int d> struct IS_MATRIX<IDENTITY_MATRIX<T,d> > {static const bool value=true;};
template<class T,int d> struct IS_MATRIX<SCALE_MATRIX<T,d> > {static const bool value=true;};
template<class T,int d> struct IS_MATRIX<DIAGONAL_MATRIX<T,d> > {static const bool value=true;};

template<class T_MATRIX> struct IS_SYMMETRIC_MATRIX {static const bool value=false;};
template<class T,int d> struct IS_SYMMETRIC_MATRIX<ZERO_MATRIX<T,d,d> > {static const bool value=true;};
template<class T,int d> struct IS_SYMMETRIC_MATRIX<SYMMETRIC_MATRIX<T,d> > {static const bool value=true;};
template<class T,int d> struct IS_SYMMETRIC_MATRIX<IDENTITY_MATRIX<T,d> > {static const bool value=true;};
template<class T,int d> struct IS_SYMMETRIC_MATRIX<SCALE_MATRIX<T,d> > {static const bool value=true;};

static constexpr int mat_zero=5;
static constexpr int mat_id=4;
static constexpr int mat_scale=3;
static constexpr int mat_diag=2;
static constexpr int mat_sym=1;
static constexpr int mat_gen=0;

constexpr int access_flags(int type)
{
    if(type==mat_gen || type==mat_zero) return 3; // get+lower/upper
    else if(type==mat_sym) return 1; // lower/upper
    else return 0;
}

struct NO_MATRIX{static constexpr int type=mat_zero;};
template<int i,int j> int get(const NO_MATRIX& a){return 0;}
inline int get(const NO_MATRIX& a,int i,int j){return 0;}
inline int diag(const NO_MATRIX& a,int i){return 0;}
inline int lower(const NO_MATRIX& a,int i,int j){return 0;}
inline int lower_(const NO_MATRIX& a,int i,int j){return 0;}
inline int upper(const NO_MATRIX& a,int i,int j){return 0;}
inline int upper_(const NO_MATRIX& a,int i,int j){return 0;}

template<class T,int type> struct MAT_TYPE_D_{typedef MATRIX_MXN<T> TYPE;};
template<class T,int type,int m,int n> struct MAT_TYPE_S_{typedef void TYPE;};
template<class T,int m,int n> struct MAT_TYPE_S_<T,mat_zero,m,n>{typedef ZERO_MATRIX<T,m,n> TYPE;};
template<class T,int d> struct MAT_TYPE_S_<T,mat_id,d,d>{typedef IDENTITY_MATRIX<T,d> TYPE;};
template<class T,int d> struct MAT_TYPE_S_<T,mat_scale,d,d>{typedef SCALE_MATRIX<T,d> TYPE;};
template<class T,int d> struct MAT_TYPE_S_<T,mat_diag,d,d>{typedef DIAGONAL_MATRIX<T,d> TYPE;};
template<class T,int d> struct MAT_TYPE_S_<T,mat_sym,d,d>{typedef SYMMETRIC_MATRIX<T,d> TYPE;};
template<class T,int m,int n> struct MAT_TYPE_S_<T,mat_gen,m,n>{typedef MATRIX<T,m,n> TYPE;};
template<class T,int type,int m,int n> using MAT_TYPE=
    std::conditional_t<(m>=0&&n>=0),
    typename MAT_TYPE_S_<T,type,m,n>::TYPE,
    typename MAT_TYPE_D_<T,type>::TYPE>;
}
#endif
