// file: $isip/class/math/matrix/MMatrix/MMatrixMethods.h
// version: $Id: MMatrixMethods.h 10304 2005-12-14 06:25:15Z srinivas $
//

// make sure definitions are only made once
//
#ifndef ISIP_MMATRIX_METHODS
#define ISIP_MMATRIX_METHODS

// isip include files
//
#ifndef ISIP_MVECTOR
#include <MVector.h>
#endif

#ifndef ISIP_MSCALAR
#include <MScalar.h>
#endif

#ifndef ISIP_LONG
#include <Long.h>
#endif

#ifndef ISIP_CHAR
#include <Char.h>
#endif

// forward class definitions
//
template<class TScalar, class TIntegral> class MMatrix;
class VectorLong;

// MMatrixMethods: a friend class to the MMatrix template.
// its purpose is mainly to provide a mechanism to get source code
// out of the header file and into implementation files.
//
class MMatrixMethods {
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // static methods
  //
  template <class TMatrix, class TScalar, class TIntegral>
  static boolean diagnose(Integral::DEBUG debug_level);

  // assign methods
  //
  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean assign(MMatrix<TScalar, TIntegral>& obj,
			const MMatrix<TAScalar, TAIntegral>& arg);
  
  // i/o methods
  //
  template <class TScalar, class TIntegral>
  static long sofSize(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>
  static boolean read(MMatrix<TScalar, TIntegral>& obj, Sof& sof, long tag, 
		      const String& name);

  template <class TScalar, class TIntegral>
  static boolean write(const MMatrix<TScalar, TIntegral>& obj, 
		       Sof& sof, long tag, const String& name);

  template <class TScalar, class TIntegral>
  static boolean readData(MMatrix<TScalar, TIntegral>& obj, 
			  Sof& sof, const String& pname, 
			  long size = SofParser::FULL_OBJECT, 
			  boolean param = true, boolean nested = false);

  template <class TScalar, class TIntegral>
  static boolean writeData(const MMatrix<TScalar, TIntegral>& obj, 
			   Sof& sof, const String& name);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  extensions to required methods
  //
  //---------------------------------------------------------------------------

  // method: eq
  //
  template <class TScalar, class TIntegral>
  static boolean eq(const MMatrix<TScalar, TIntegral>& obj, TIntegral value);

  // array conversion methods
  //
  template <class TScalar, class TIntegral, class TAIntegral>
  static boolean assign(MMatrix<TScalar, TIntegral>& obj, 
			long nrows, long ncols, const TAIntegral* arg, 
			Integral::MTYPE type);
  
  template <class TScalar, class TIntegral>  
  static boolean assignComplexDiagnose(MMatrix<TScalar, TIntegral>& obj, 
			long nrows, long ncols, const double* arg, 
			Integral::MTYPE type);
  
  // conversion from string methods
  //
  template <class TScalar, class TIntegral>
  static boolean assign(MMatrix<TScalar, TIntegral>& obj,
			long nrows, long ncols, const String* arg,
			Integral::MTYPE type);
    
  template <class TScalar, class TIntegral>
  static boolean assign(MMatrix<TScalar, TIntegral>& obj,
			long nrows, long ncols, const String& arg, 
			Integral::MTYPE type, const Char delim);

  // copy methods
  //
  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean copy(MMatrix<TScalar, TIntegral>& obj,
		      const MMatrix<TAScalar, TAIntegral>& arg);
      
  // swap methods
  //
  template <class TScalar, class TIntegral>
  static boolean swap(MMatrix<TScalar, TIntegral>& obj, 
		      MMatrix<TScalar, TIntegral>& matrix);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  operator overloads, get, find, and set methods
  //
  //---------------------------------------------------------------------------

  // methods to get values from a matrix
  //
  template <class TScalar, class TIntegral>
  static TIntegral getValue(const MMatrix<TScalar, TIntegral>& obj, 
			    long row_index, long col_index);

  template <class TScalar, class TIntegral>
  static boolean getValue(const MMatrix<TScalar, TIntegral>& obj, 
			  TScalar& value, long row_index, long col_index);

  template <class TScalar, class TIntegral>
  static boolean getRow(const MMatrix<TScalar, TIntegral>& obj, 
			MVector<TScalar, TIntegral>& vector,
			long row_index);
  
  template <class TScalar, class TIntegral>
  static boolean getColumn(const MMatrix<TScalar, TIntegral>& obj, 
			   MVector<TScalar, TIntegral>& vector,
			   long col_index);
  
  template <class TScalar, class TIntegral>
  static boolean getDiagonal(const MMatrix<TScalar, TIntegral>& obj, 
			     MVector<TScalar, TIntegral>& vector);
  
  template <class TScalar, class TIntegral>
  static boolean getLower(const MMatrix<TScalar, TIntegral>& obj, 
			  MMatrix<TScalar, TIntegral>& arg);
  
  template <class TScalar, class TIntegral>
  static boolean getUpper(const MMatrix<TScalar, TIntegral>& obj, 
			  MMatrix<TScalar, TIntegral>& arg);
  
  template <class TScalar, class TIntegral>
  static boolean getMinor(const MMatrix<TScalar, TIntegral>& obj, 
			  MMatrix<TScalar, TIntegral>& arg, 
			  long row_index, long col_index);

  // methods to set values
  //
  template <class TScalar, class TIntegral>
  static boolean setValue(MMatrix<TScalar, TIntegral>& obj, 
			  long row_index, long col_index, TIntegral value);

  template <class TScalar, class TIntegral>
  static boolean setRow(MMatrix<TScalar, TIntegral>& obj, long row_index,
			const MVector<TScalar, TIntegral>& vector);

  template <class TScalar, class TIntegral>
  static boolean setColumn(MMatrix<TScalar, TIntegral>& obj, long col_index,
			   const MVector<TScalar, TIntegral>& vector);

  template <class TScalar, class TIntegral>
  static boolean setDiagonal(MMatrix<TScalar, TIntegral>& obj, 
			     const MVector<TScalar, TIntegral>& values);
  
  template <class TScalar, class TIntegral>
  static boolean setLower(MMatrix<TScalar, TIntegral>& obj,
			  const MMatrix<TScalar, TIntegral>& source);

  template <class TScalar, class TIntegral>
  static boolean setUpper(MMatrix<TScalar, TIntegral>& obj,
			  const MMatrix<TScalar, TIntegral>& source);

  template <class TScalar, class TIntegral>
  static boolean setBlock(MMatrix<TScalar, TIntegral>& obj,
			  long start_row, long start_col,
			  long num_rows, long num_cols, TIntegral value);

  // methods to make matrices
  //
  template <class TScalar, class TIntegral>
  static boolean makeDiagonal(MMatrix<TScalar, TIntegral>& obj,
			      const MVector<TScalar, TIntegral>& values,
			      Integral::MTYPE type);

  template <class TScalar, class TIntegral>
  static boolean makeLower(MMatrix<TScalar, TIntegral>& obj,
			   const MMatrix<TScalar, TIntegral>& source,
			   Integral::MTYPE type);
  
  template <class TScalar, class TIntegral>
  static boolean makeUpper(MMatrix<TScalar, TIntegral>& obj,
			   const MMatrix<TScalar, TIntegral>& source,
			   Integral::MTYPE type);
  
  // methods to find values
  //
  template <class TScalar, class TIntegral>
  static boolean nextZero(const MMatrix<TScalar, TIntegral>& obj,
			  long& row_index, long& col_index,
			  long row_start, long col_start);
     
  template <class TScalar, class TIntegral>
  static boolean nextNonZero(const MMatrix<TScalar, TIntegral>& obj,
			     TIntegral& value,
			     long& row_index, long& col_index,
			     long row_start, long col_start);

  template <class TScalar, class TIntegral>
  static long findRow(const MMatrix<TScalar, TIntegral>& obj, 
		      const MVector<TScalar, TIntegral>& vector);
    
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  property checking methods
  //
  //---------------------------------------------------------------------------
  
  // change/get type methods
  //
  template <class TScalar, class TIntegral>
  static boolean isTypePossible(const MMatrix<TScalar, TIntegral>& obj, 
				Integral::MTYPE type);

  template <class TScalar, class TIntegral>
  static boolean isSquare(const MMatrix<TScalar, TIntegral>& obj, 
			  const MMatrix<TScalar, TIntegral>& matrix);

  template <class TScalar, class TIntegral>
  static boolean isOrthogonal(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>
  static boolean isIdentity(const MMatrix<TScalar, TIntegral>& obj);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  relational and logical methods
  //
  //---------------------------------------------------------------------------

  // relational and boolean methods
  //
  template <class TScalar, class TIntegral>
  static boolean gt(const MMatrix<TScalar, TIntegral>& obj, TIntegral value);

  template <class TScalar, class TIntegral>
  static boolean lt(const MMatrix<TScalar, TIntegral>& obj, TIntegral value);

  template <class TScalar, class TIntegral>
  static boolean ge(const MMatrix<TScalar, TIntegral>& obj, TIntegral value);

  template <class TScalar, class TIntegral>
  static boolean le(const MMatrix<TScalar, TIntegral>& obj, TIntegral value);

  template <class TScalar, class TIntegral>
  static long numEqual(const MMatrix<TScalar, TIntegral>& obj,
		       TIntegral value);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  concatenation methods 
  //
  //--------------------------------------------------------------------------
  
  // concatenation methods
  //
  template <class TScalar, class TIntegral>
  static boolean concatByRow(MMatrix<TScalar, TIntegral>& obj, const
			     MMatrix<TScalar, TIntegral>& additional_rows);

  template <class TScalar, class TIntegral>	
  static boolean concatByColumn(MMatrix<TScalar, TIntegral>& obj, const
				MMatrix<TScalar, TIntegral>& additional_cols);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  ordering methods 
  //
  //--------------------------------------------------------------------------

  // ordering methods
  //
  template <class TScalar, class TIntegral>
  static boolean reorderRows(MMatrix<TScalar, TIntegral>& obj, 
			     const MVector<Long, long>& indexes);

  template <class TScalar, class TIntegral>
  static boolean reorderColumns(MMatrix<TScalar, TIntegral>& obj, 
				const MVector<Long, long>& indexes);

  template <class TScalar, class TIntegral>
  static boolean swapRows(MMatrix<TScalar, TIntegral>& obj, 
			  const MMatrix<TScalar, TIntegral>& arg, 
			  long row1, long row2);

  template <class TScalar, class TIntegral>
  static boolean swapColumns(MMatrix<TScalar, TIntegral>& obj, 
			     const MMatrix<TScalar, TIntegral>& arg, 
			     long col1, long col2);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  basic mathematical methods
  //
  //---------------------------------------------------------------------------

  template <class TScalar, class TIntegral>
  static boolean add(MMatrix<TScalar, TIntegral>& obj,
		     TIntegral value);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean add(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TAScalar, TAIntegral>& m1);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean add(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TScalar, TIntegral>& m1, 
		     const MMatrix<TAScalar, TAIntegral>& m2);

  template <class TScalar, class TIntegral>
  static boolean add(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TScalar, TIntegral>& source_matrix, 
		     TIntegral value);

  template <class TScalar, class TIntegral>
  static boolean sub(MMatrix<TScalar, TIntegral>& obj,
		     TIntegral value);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean sub(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TAScalar, TAIntegral>& m1);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean sub(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TScalar, TIntegral>& m1, 
		     const MMatrix<TAScalar, TAIntegral>& m2);

  template <class TScalar, class TIntegral>
  static boolean sub(MMatrix<TScalar, TIntegral>& obj,
		     const MMatrix<TScalar, TIntegral>& source_matrix, 
		     TIntegral value);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean mult(MMatrix<TScalar, TIntegral>& obj,
		      const MMatrix<TScalar, TIntegral>& m1, 
		      const MMatrix<TAScalar, TAIntegral>& m2);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  other mathematical methods
  //
  //--------------------------------------------------------------------------
  
  template <class TScalar, class TIntegral>
  static TIntegral min(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>
  static TIntegral min(const MMatrix<TScalar, TIntegral>& obj, 
		       long& row_index, long& col_index);

  template <class TScalar, class TIntegral>
  static TIntegral max(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static TIntegral max(const MMatrix<TScalar, TIntegral>& obj,
		       long& row_index, long& col_index);

  template <class TScalar, class TIntegral>
  static double minMag(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static double minMag(const MMatrix<TScalar, TIntegral>& obj, 
			  long& row_index, long& col_index);
  
  template <class TScalar, class TIntegral>
  static double maxMag(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static double maxMag(const MMatrix<TScalar, TIntegral>& obj, 
			  long& row_index, long& col_index);

  // random number generation methods
  //
  template <class TScalar, class TIntegral>
  static boolean rand(MMatrix<TScalar, TIntegral>& obj,
		      Random& generator);
  
  template <class TScalar, class TIntegral>
  static boolean rand(MMatrix<TScalar, TIntegral>& obj,
		      TIntegral min_val, TIntegral max_val,
		      Random& generator);
  
  template <class TScalar, class TIntegral>
  static boolean grand(MMatrix<TScalar,TIntegral>& obj,
		       TIntegral mean, TIntegral stdev, Random& generator);
  
  template <class TScalar, class TIntegral>
  static boolean randIndicesSparse(MMatrix<TScalar,TIntegral>& obj,
				   Random& generator);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  linear algebra related methods 
  //
  //--------------------------------------------------------------------------
  
  // determinant and inverse methods
  //
  template <class TScalar, class TIntegral>
  static TIntegral determinant(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static TIntegral determinantLU(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static TIntegral determinantMinor(const MMatrix<TScalar, TIntegral>& obj); 

  template <class TScalar, class TIntegral>
  static boolean inverse(MMatrix<TScalar, TIntegral>& obj, 
			 const MMatrix<TScalar, TIntegral>& matrix);

  template <class TScalar, class TIntegral>
  static boolean transpose(MMatrix<TScalar, TIntegral>& obj, 
			   const MMatrix<TScalar, TIntegral>& matrix);

  template <class TScalar, class TIntegral>
  static long rank(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>
  static TIntegral trace(const MMatrix<TScalar, TIntegral>& obj);
  
  // decomposition methods
  //
  template <class TScalar, class TIntegral>
  static boolean decompositionLU(const MMatrix<TScalar,TIntegral>& input,
				 MMatrix<TScalar, TIntegral>& l,
				 MMatrix<TScalar, TIntegral>& u,
				 MVector<Long, long>& index,
				 long& sign,
				 double stabilize);

  template <class TScalar, class TIntegral>
  static boolean decompositionCholesky(const MMatrix<TScalar,TIntegral>& input,
				       MMatrix<TScalar, TIntegral>& l);
    
  template <class TScalar, class TIntegral>
  static boolean decompositionSVD(const MMatrix<TScalar,TIntegral>& input,
  				  MMatrix<TScalar, TIntegral>& u,
  				  MMatrix<TScalar, TIntegral>& w,
  				  MMatrix<TScalar, TIntegral>& v,
				  boolean);

  template <class TScalar, class TIntegral>
  static boolean decompositionQR(const MMatrix<TScalar, TIntegral>& input,
				 MMatrix<TScalar, TIntegral>& q,
				 MMatrix<TScalar, TIntegral>& r);

  // methods to compute eigenvalues and eigenvectors
  //
  template <class TScalar, class TIntegral>
  static boolean eigen(const MMatrix<TScalar,TIntegral>& obj,
		       MVector<TScalar, TIntegral>& eigvals,
		       MMatrix<TScalar, TIntegral>& eigvects);

  template <class TScalar, class TIntegral>  
  static boolean eigenComputeVector(MMatrix<TScalar, TIntegral>& obj,
				    MVector<TScalar, TIntegral>& eigvect,
				    TIntegral eigval);

  template <class TScalar, class TIntegral>    
  static boolean eigenBalance(MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>    
  static boolean eigenEliminateHessenberg(MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>    
  static boolean eigenHessenbergQR(MMatrix<TScalar, TIntegral>& obj,
				   MVector<TScalar, TIntegral>& eig_val_real,
				   MVector<TScalar, TIntegral>& eig_val_imag);

  template<class TScalar, class TIntegral>
  static boolean luSolve(MVector<TScalar, TIntegral>& out_vec,
			 const MMatrix<TScalar, TIntegral>& l,
			 const MMatrix<TScalar, TIntegral>& u,
			 const VectorLong& index,
			 const MVector<TScalar, TIntegral>& in_vec);
 
  template<class TScalar, class TIntegral>
  static boolean choleskySolve(MVector<TScalar, TIntegral>& out_vec,
			       const MMatrix<TScalar, TIntegral>& l,
			       const MVector<TScalar, TIntegral>& in_vec);
 
  template<class TScalar, class TIntegral>
  static boolean svdSolve(MVector<TScalar, TIntegral>& out_vec,
			  const MMatrix<TScalar, TIntegral>& u,
			  const MMatrix<TScalar, TIntegral>& w,
			  const MMatrix<TScalar, TIntegral>& v,
			  const MVector<TScalar, TIntegral>& in_vec,
			  boolean zero_singulars);
 
  template <class TScalar, class TIntegral>
  static boolean multv(const MMatrix<TScalar, TIntegral>& obj, 
		       MVector<TScalar, TIntegral>& output_vector,
		       const MVector<TScalar, TIntegral>& input_vector);

  template <class TScalar, class TIntegral>
  static boolean vmult(const MMatrix<TScalar, TIntegral>& obj, 
		       MVector<TScalar, TIntegral>& output_vector,
		       const MVector<TScalar, TIntegral>& input_vector);

  template <class TScalar, class TIntegral>
  static boolean quadratic(TIntegral& output, 
			   const MMatrix<TScalar, TIntegral>& obj, 
			   const MVector<TScalar, TIntegral>& input_vector);

  template <class TScalar, class TIntegral>
  static boolean quadratic(MMatrix<TScalar, TIntegral>& output,
			   const MMatrix<TScalar, TIntegral>& input_mat1, 
			   const MMatrix<TScalar, TIntegral>& input_mat2);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean outerProduct(MMatrix<TScalar, TIntegral>& out_matrix,
			      const MMatrix<TScalar, TIntegral>& input_mat1, 
			      const MMatrix<TAScalar, TAIntegral>& input_mat2);

  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static boolean outerProduct(MMatrix<TScalar, TIntegral>& out_matrix,
			      const MVector<TScalar, TIntegral>& input_vec1, 
			      const MVector<TAScalar, TAIntegral>& input_vec2);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  summation methods
  //
  //--------------------------------------------------------------------------

  template <class TScalar, class TIntegral>
  static TIntegral sum(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>
  static TIntegral sumSquare(const MMatrix<TScalar, TIntegral>& obj);

  template <class TScalar, class TIntegral>	
  static TIntegral sumColumn(const MMatrix<TScalar, TIntegral>& obj, 
			     long col_index);

  template <class TScalar, class TIntegral>	
  static TIntegral sumRow(const MMatrix<TScalar, TIntegral>& obj, 
			  long row_index);

  template <class TScalar, class TIntegral>
  static TIntegral var(const MMatrix<TScalar, TIntegral>& obj);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  methods that are private in MMatrix
  //
  //--------------------------------------------------------------------------

  template <class TScalar, class TIntegral>
  static boolean reverseIndex(const MMatrix<TScalar, TIntegral>& obj,
			   long& row, long& col, long vec_index);
  
  template <class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static TIntegral multiplyRowByColumn(const MMatrix<TScalar, TIntegral>& obj, 
				       const MMatrix<TScalar, TIntegral>&
				       matrix_for_row, 
				       const MMatrix<TAScalar, TAIntegral>&
				       matrix_for_col, 
				       long row_index, long col_index);
  
  template<class TScalar, class TIntegral, class TAScalar, class TAIntegral>
  static TIntegral multiplyRowByRow(const MMatrix<TScalar, TIntegral>& obj, 
				    const MMatrix<TScalar, TIntegral>& m1, 
				    const MMatrix<TAScalar, TAIntegral>& m2,
				    long row_m1, long row_m2);

  template <class TScalar, class TIntegral>
  static boolean assignStream(MMatrix<TScalar, TIntegral>& obj, 
			      long nrows, long ncols, 
			      const MVector<TScalar, TIntegral>& vec, 
			      Integral::MTYPE type);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  diagnostic methods
  //
  //--------------------------------------------------------------------------

  // test general methods

  template <class TScalar, class TIntegral>
  static boolean diagnose0(Integral::DEBUG level);

  // test single matrix->scalar methods
  //
  template <class TScalar, class TIntegral>
  static boolean diagnose1(Integral::DEBUG level);

  // test single matrix->vector methods
  //
  template <class TScalar, class TIntegral>
  static boolean diagnose2(Integral::DEBUG level);

  // test single matrix->matrix methods
  //
  template <class TScalar, class TIntegral>
  static boolean diagnose3(Integral::DEBUG level);

  // test (matrix, matrix)->matrix methods (real)
  //
  template <class TScalar, class TIntegral>
  static boolean diagnose4(Integral::DEBUG level);

  // test (matrix, matrix)->matrix methods (complex)
  //
  template <class TScalar, class TIntegral>
  static boolean diagnose5(Integral::DEBUG level);

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
  
  // destructor/constructor(s):
  // the constructors and destructors are declared private so that a
  // compilation error will be generated if the user tries to construct an
  // object of this type
  //
  ~MMatrixMethods();
  MMatrixMethods();
  MMatrixMethods(const MMatrixMethods& arg);

  // method: name
  //  make this method private so it is never called outside this class
  //
  static const String& name();
};

// end of include file
//
#endif
