// Copyright (C) 2004, 2006 International Business Machines and others.
// All Rights Reserved.
// This code is published under the Eclipse Public License.
//
// Authors:  Carl Laird, Andreas Waechter     IBM    2004-08-13

#ifndef __IPSYMTMATRIX_HPP__
#define __IPSYMTMATRIX_HPP__

#include "IpUtils.hpp"
#include "IpSymMatrix.hpp"

namespace Ipopt
{

/* forward declarations */
class SymTMatrixSpace;

/** Class for symmetric matrices stored in triplet format.
 *
 *  In the triplet format, the nonzeros elements of a symmetric matrix
 *  is stored in three arrays, Irn, Jcn, and Values, all of length
 *  Nonzeros.  The first two arrays indicate the location of a
 *  non-zero element (as the row and column indices), and the last
 *  array stores the value at that location.  Off-diagonal elements
 *  need to be stored only once since the matrix is symmetric.  For
 *  example, the element \f$a_{1,2}=a_{2,1}\f$ would be stored only
 *  once, either with Irn[i]=1 and Jcn[i]=2, or with Irn[i]=2 and
 *  Jcn[i]=1.  Both representations are identical.  If nonzero
 *  elements (or their symmetric counter part) are listed more than
 *  once, their values are added.
 *
 *  The structure of the nonzeros (i.e. the arrays Irn and Jcn)
 *  cannot be changed after the matrix can been initialized.  Only
 *  the values of the nonzero elements can be modified.
 *
 *  Note that the first row and column of a matrix has index 1, not 0.
 */
class IPOPTLIB_EXPORT SymTMatrix: public SymMatrix
{
public:
   /**@name Constructors / Destructors */
   ///@{
   /** Constructor, taking the corresponding matrix space. */
   SymTMatrix(
      const SymTMatrixSpace* owner_space
   );

   /** Destructor */
   ~SymTMatrix();
   ///@}

   /**@name Changing the Values.*/
   ///@{
   /** Set values of nonzero elements.
    *
    *  The values of the nonzero
    *  elements is copied from the incoming Number array.
    *  Important: It is assume that the order of the values in Values
    *  corresponds to the one of Irn and Jcn given to the matrix space.
    */
   void SetValues(
      const Number* Values
   );
   ///@}

   /** @name Accessor Methods */
   ///@{
   /** Number of nonzero entries */
   Index Nonzeros() const;

   /** Obtain pointer to the internal Index array irn_ without the
    *  intention to change the matrix data.
    *
    *  @attention This does not produce a copy, and lifetime is not guaranteed!
    */
   const Index* Irows() const;

   /** Obtain pointer to the internal Index array jcn_ without the
    *  intention to change the matrix data.
    *
    *  @attention This does not produce a copy, and lifetime is not guaranteed!
    */
   const Index* Jcols() const;

   /** Obtain pointer to the internal Number array values_ with the
    *  intention to change the matrix data.
    *
    *  @attention This does not produce a copy, and lifetime is not guaranteed!
    */
   Number* Values();

   /** Obtain pointer to the internal Number array values_ without the
    *  intention to change the matrix data.
    *
    *  @attention This does not produce a copy, and lifetime is not guaranteed!
    */
   const Number* Values() const;
   ///@}

   /**@name Methods for providing copy of the matrix data */
   ///@{
   /** Copy the nonzero structure into provided space */
   void FillStruct(
      Index* Irn,
      Index* Jcn
   ) const;

   /** Copy the value data into provided space */
   void FillValues(
      Number* Values
   ) const;
   ///@}

protected:
   /**@name Methods overloaded from matrix */
   ///@{
   virtual void MultVectorImpl(
      Number        alpha,
      const Vector& x,
      Number        beta,
      Vector&       y
   ) const;

   virtual bool HasValidNumbersImpl() const;

   virtual void ComputeRowAMaxImpl(
      Vector& rows_norms,
      bool    init
   ) const;

   virtual void PrintImpl(
      const Journalist&  jnlst,
      EJournalLevel      level,
      EJournalCategory   category,
      const std::string& name,
      Index              indent,
      const std::string& prefix
   ) const;
   ///@}

private:
   /**@name Default Compiler Generated Methods
    * (Hidden to avoid implicit creation/calling).
    * These methods are not implemented and
    * we do not want the compiler to implement
    * them for us, so we declare them private
    * and do not define them. This ensures that
    * they will not be implicitly created/called.
    */
   ///@{
   /** Default Constructor */
   SymTMatrix();

   /** Copy Constructor */
   SymTMatrix(
      const SymTMatrix&
   );

   /** Default Assignment Operator */
   void operator=(
      const SymTMatrix&
   );
   ///@}

   /** Copy of the owner_space ptr as a SymTMatrixSpace instead
    *  of a MatrixSpace
    */
   const SymTMatrixSpace* owner_space_;

   /** Values of nonzeros */
   Number* values_;

   /** Flag for Initialization */
   bool initialized_;
};

/** This is the matrix space for a SymTMatrix with fixed sparsity
 *  structure.
 *
 *  The sparsity structure is stored here in the matrix space.
 */
class IPOPTLIB_EXPORT SymTMatrixSpace: public SymMatrixSpace
{
public:
   /** @name Constructors / Destructors */
   ///@{
   /** Constructor, given the number of rows and columns (both as
    *  dim), as well as the number of nonzeros and the position of
    *  the nonzero elements.  Note that the counting of the nonzeros
    *  starts a 1, i.e., iRows[i]==1 and jCols[i]==1 refers to the
    *  first element in the first row.  This is in accordance with
    *  the HSL data structure.  Off-diagonal elements are stored only
    *  once.
    */
   SymTMatrixSpace(
      Index        dim,
      Index        nonZeros,
      const Index* iRows,
      const Index* jCols
   );

   /** Destructor */
   ~SymTMatrixSpace();
   ///@}

   virtual SymMatrix* MakeNewSymMatrix() const
   {
      return MakeNewSymTMatrix();
   }

   /** Method for creating a new matrix of this specific type. */
   SymTMatrix* MakeNewSymTMatrix() const
   {
      return new SymTMatrix(this);
   }

   /**@name Methods describing Matrix structure */
   ///@{
   /** Number of non-zeros in the sparse matrix */
   Index Nonzeros() const
   {
      return nonZeros_;
   }

   /** Row index of each non-zero element */
   const Index* Irows() const
   {
      return iRows_;
   }

   /** Column index of each non-zero element */
   const Index* Jcols() const
   {
      return jCols_;
   }
   ///@}

private:
   /**@name Methods called by SymTMatrix for memory management */
   ///@{
   /** Allocate internal storage for the SymTMatrix values */
   Number* AllocateInternalStorage() const;

   /** Deallocate internal storage for the SymTMatrix values */
   void FreeInternalStorage(
      Number* values
   ) const;
   ///@}

   const Index nonZeros_;
   Index* iRows_;
   Index* jCols_;

   friend class SymTMatrix;
};

/* Inline Methods */
inline Index SymTMatrix::Nonzeros() const
{
   return owner_space_->Nonzeros();
}

inline const Index* SymTMatrix::Irows() const
{
   return owner_space_->Irows();
}

inline const Index* SymTMatrix::Jcols() const
{
   return owner_space_->Jcols();
}

} // namespace Ipopt
#endif
