// Copyright (C) 2004, 2009 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 __IPDENSEVECTOR_HPP__
#define __IPDENSEVECTOR_HPP__

#include "IpUtils.hpp"
#include "IpVector.hpp"
#include <map>

namespace Ipopt
{

/* forward declarations */
class DenseVectorSpace;

/** @name Exceptions */
///@{
DECLARE_STD_EXCEPTION(METADATA_ERROR);
///@}

/** Dense Vector Implementation.
 *
 *  This is the default Vector class in Ipopt.
 *  It stores vectors in contiguous Number arrays, unless
 *  the vector has the same value in all entires.  In the latter
 *  case, we call the vector "homogeneous", and we store only the
 *  values that is repeated in all elements.  If you want to obtain
 *  the values of vector, use the IsHomogeneous() method to find out
 *  what status the vector is in, and then use either Values() const
 *  or Scalar() const methods to get the values.  To set the values
 *  of a homogeneous method, use the Set method.  To set the values
 *  of a non-homogeneous vector, use the SetValues method, or use
 *  the non-const Values method to get an array that you can
 *  overwrite.  In the latter case, storage is ensured.
 */
class IPOPTLIB_EXPORT DenseVector: public Vector
{
public:
   /**@name Constructors / Destructors */
   ///@{
   /** Default Constructor
    */
   DenseVector(
      const DenseVectorSpace* owner_space
   );

   /** Destructor
    */
   virtual ~DenseVector();
   ///@}

   /** @name Additional public methods not in Vector base class. */
   ///@{
   /** Create a new DenseVector from same VectorSpace */
   SmartPtr<DenseVector> MakeNewDenseVector() const;

   /** Set elements in the vector to the Number array x. */
   void SetValues(
      const Number* x
   );

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

   /** Obtain pointer to the internal Number array with vector
    *  elements without the intention to change the vector data.
    *
    *  @attention This does not produce a copy, and lifetime is not
    *  guaranteed!
    *
    *  @attention If this vector is currently homogeneous
    *  (i.e. IsHomogeneous returns true), then you cannot
    *  call this method. Instead, you need to use the Scalar()
    *  method.
    */
   inline const Number* Values() const;

   /** The same as the const version of Values, but we ensure that we
    *  always return a valid array, even if IsHomogeneous returns
    *  true.
    */
   const Number* ExpandedValues() const;

   /** This is the same as Values, but we add it here so that
    *  ExpandedValues can also be used for the non-const case.
    */
   inline Number* ExpandedValues()
   {
      return Values();
   }

   /** Indicates if the vector is homogeneous (i.e., all entries have
    *  the value Scalar().
    */
   bool IsHomogeneous() const
   {
      return homogeneous_;
   }

   /** Scalar value of all entries in a homogeneous vector. */
   Number Scalar() const
   {
      DBG_ASSERT(homogeneous_);
      return scalar_;
   }
   ///@}

   /** @name Modifying subranges of the vector. */
   ///@{
   /** Copy the data in x into the subrange of this vector starting
    *  at position Pos in this vector.
    *
    *  Position count starts at 0.
    */
   void CopyToPos(
      Index         Pos,
      const Vector& x
   );

   /** Copy a subrange of x, starting at Pos, into the full data of
    *  this vector.
    *
    *  Position count starts at 0.
    */
   void CopyFromPos(
      Index         Pos,
      const Vector& x
   );
   ///@}

protected:
   /** @name Overloaded methods from Vector base class */
   ///@{
   virtual void CopyImpl(
      const Vector& x
   );

   virtual void ScalImpl(
      Number alpha
   );

   virtual void AxpyImpl(
      Number        alpha,
      const Vector& x
   );

   virtual Number DotImpl(
      const Vector& x
   ) const;

   virtual Number Nrm2Impl() const;

   virtual Number AsumImpl() const;

   virtual Number AmaxImpl() const;

   virtual void SetImpl(
      Number value
   );

   virtual void ElementWiseDivideImpl(
      const Vector& x
   );

   virtual void ElementWiseMultiplyImpl(
      const Vector& x
   );

   virtual void ElementWiseSelectImpl(
      const Vector& x
   );

   virtual void ElementWiseMaxImpl(
      const Vector& x
   );

   virtual void ElementWiseMinImpl(
      const Vector& x
   );

   virtual void ElementWiseReciprocalImpl();

   virtual void ElementWiseAbsImpl();

   virtual void ElementWiseSqrtImpl();

   virtual void ElementWiseSgnImpl();

   virtual void AddScalarImpl(
      Number scalar
   );

   virtual Number MaxImpl() const;

   virtual Number MinImpl() const;

   virtual Number SumImpl() const;

   virtual Number SumLogsImpl() const;
   ///@}

   /** @name Implemented specialized functions */
   ///@{
   /** Add two vectors (a * v1 + b * v2).
    *
    * Result is stored in this vector.
    */
   void AddTwoVectorsImpl(
      Number        a,
      const Vector& v1,
      Number        b,
      const Vector& v2,
      Number        c
   );

   /** Fraction to the boundary parameter. */
   Number FracToBoundImpl(
      const Vector& delta,
      Number        tau
   ) const;

   /** Add the quotient of two vectors, y = a * z/s + c * y. */
   void AddVectorQuotientImpl(
      Number        a,
      const Vector& z,
      const Vector& s,
      Number        c
   );
   ///@}

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

   /* Print the entire vector with padding, and start counting with an offset. */
   void PrintImplOffset(
      const Journalist&  jnlst,
      EJournalLevel      level,
      EJournalCategory   category,
      const std::string& name,
      Index              indent,
      const std::string& prefix,
      Index              offset
   ) const;
   ///@}

   friend class ParVector;

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 */
   DenseVector();

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

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

   /** Copy of the owner_space ptr as a DenseVectorSpace instead of a VectorSpace. */
   const DenseVectorSpace* owner_space_;

   /** Dense Number array of vector values. */
   Number* values_;

   /** Dense Number array pointer that is used for ExpandedValues */
   mutable Number* expanded_values_;

   /** Get the internal values array, making sure that memory has been allocated. */
   inline Number* values_allocated();

   /** Flag for Initialization.
    *
    * This flag is false, if the data has not yet been initialized.
    */
   bool initialized_;

   /** Flag indicating whether the vector is currently homogeneous
    *  (that is, all elements have the same value).
    *
    *  This flag is used to determine whether the elements of the vector
    *  are stored in values_ or in scalar_.
    */
   bool homogeneous_;

   /** Homogeneous value of all elements if the vector is currently homogeneous. */
   Number scalar_;

   /** Auxiliary method for setting explicitly all elements in values_ to the
    * current scalar value.
    */
   void set_values_from_scalar();
};

/** @name typedefs for the map variables that define meta data for the DenseVectorSpace */
///@{
typedef std::map<std::string, std::vector<std::string> > StringMetaDataMapType;
typedef std::map<std::string, std::vector<Index> > IntegerMetaDataMapType;
typedef std::map<std::string, std::vector<Number> > NumericMetaDataMapType;
///@}

/** This vectors space is the vector space for DenseVector. */
class IPOPTLIB_EXPORT DenseVectorSpace: public VectorSpace
{
public:
   /** @name Constructors/Destructors. */
   ///@{
   /** Constructor, requires dimension of all vector for this VectorSpace */
   DenseVectorSpace(
      Index dim
   )
      : VectorSpace(dim)
   { }

   /** Destructor */
   ~DenseVectorSpace()
   { }
   ///@}

   /** Method for creating a new vector of this specific type. */
   inline DenseVector* MakeNewDenseVector() const
   {
      return new DenseVector(this);
   }

   virtual Vector* MakeNew() const
   {
      return MakeNewDenseVector();
   }

   /**@name Methods called by DenseVector for memory management.
    *
    * This could allow to have sophisticated memory management in the
    * VectorSpace.
    */
   ///@{
   /** Allocate internal storage for the DenseVector */
   inline Number* AllocateInternalStorage() const;

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

   /**@name Methods for dealing with meta data on the vector
    */
   ///@{
   /** Check if string meta exists for tag */
   inline
   bool HasStringMetaData(
      const std::string& tag
   ) const;

   /** Check if Integer meta exists for tag */
   inline
   bool HasIntegerMetaData(
      const std::string& tag
   ) const;

   /** Check if Numeric meta exists for tag */
   inline
   bool HasNumericMetaData(
      const std::string& tag
   ) const;

   /** Get meta data of type std::string by tag */
   inline const std::vector<std::string>& GetStringMetaData(
      const std::string& tag
   ) const;

   /** Get meta data of type Index by tag */
   inline const std::vector<Index>& GetIntegerMetaData(
      const std::string& tag
   ) const;

   /** Get meta data of type Number by tag */
   inline const std::vector<Number>& GetNumericMetaData(
      const std::string& tag
   ) const;

   /** Set meta data of type std::string by tag */
   inline void SetStringMetaData(
      const std::string&              tag,
      const std::vector<std::string>& meta_data
   );

   /** Set meta data of type Index by tag */
   inline void SetIntegerMetaData(
      const std::string&        tag,
      const std::vector<Index>& meta_data
   );

   /** Set meta data of type Number by tag */
   inline void SetNumericMetaData(
      const std::string&         tag,
      const std::vector<Number>& meta_data
   );

   /** Get map of meta data of type Number */
   inline const StringMetaDataMapType& GetStringMetaData() const;

   /** Get map of meta data of type Number */
   inline const IntegerMetaDataMapType& GetIntegerMetaData() const;

   /** Get map of meta data of type Number */
   inline const NumericMetaDataMapType& GetNumericMetaData() const;
   ///@}

private:
   // variables to store vector meta data
   StringMetaDataMapType string_meta_data_;
   IntegerMetaDataMapType integer_meta_data_;
   NumericMetaDataMapType numeric_meta_data_;
};

// inline functions
inline Number* DenseVector::Values()
{
   // Here we assume that every time someone requests this direct raw
   // pointer, the data is going to change and the Tag for this
   // vector has to be updated.

   if( initialized_ && homogeneous_ )
   {
      // If currently the vector is a homogeneous vector, set all elements
      // explicitly to this value
      set_values_from_scalar();
   }
   ObjectChanged();
   initialized_ = true;
   homogeneous_ = false;
   values_allocated();
   DBG_ASSERT(Dim() == 0 || values_ != NULL);
   return values_;
}

inline const Number* DenseVector::Values() const
{
   DBG_ASSERT(initialized_ && (Dim() == 0 || values_));
   return values_;
}

inline Number* DenseVector::values_allocated()
{
   if( values_ == NULL )
   {
      values_ = owner_space_->AllocateInternalStorage();
   }
   return values_;
}

inline Number* DenseVectorSpace::AllocateInternalStorage() const
{
   if( Dim() > 0 )
   {
      return new Number[Dim()];
   }
   else
   {
      return NULL;
   }
}

inline
void DenseVectorSpace::FreeInternalStorage(
   Number* values
) const
{
   delete[] values;
}

inline SmartPtr<DenseVector> DenseVector::MakeNewDenseVector() const
{
   return owner_space_->MakeNewDenseVector();
}

inline
bool DenseVectorSpace::HasStringMetaData(
   const std::string& tag
) const
{
   StringMetaDataMapType::const_iterator iter;
   iter = string_meta_data_.find(tag);

   if( iter != string_meta_data_.end() )
   {
      return true;
   }

   return false;
}

inline
bool DenseVectorSpace::HasIntegerMetaData(
   const std::string& tag
) const
{
   IntegerMetaDataMapType::const_iterator iter;
   iter = integer_meta_data_.find(tag);

   if( iter != integer_meta_data_.end() )
   {
      return true;
   }

   return false;
}

inline
bool DenseVectorSpace::HasNumericMetaData(
   const std::string& tag
) const
{
   NumericMetaDataMapType::const_iterator iter;
   iter = numeric_meta_data_.find(tag);

   if( iter != numeric_meta_data_.end() )
   {
      return true;
   }

   return false;
}

inline const std::vector<std::string>& DenseVectorSpace::GetStringMetaData(
   const std::string& tag
) const
{
   DBG_ASSERT(HasStringMetaData(tag));
   StringMetaDataMapType::const_iterator iter;
   iter = string_meta_data_.find(tag);
   return iter->second;
}

inline const std::vector<Index>& DenseVectorSpace::GetIntegerMetaData(
   const std::string& tag
) const
{
   DBG_ASSERT(HasIntegerMetaData(tag));
   IntegerMetaDataMapType::const_iterator iter;
   iter = integer_meta_data_.find(tag);
   return iter->second;
}

inline const std::vector<Number>& DenseVectorSpace::GetNumericMetaData(
   const std::string& tag
) const
{
   DBG_ASSERT(HasNumericMetaData(tag));
   NumericMetaDataMapType::const_iterator iter;
   iter = numeric_meta_data_.find(tag);
   return iter->second;
}

inline void DenseVectorSpace::SetStringMetaData(
   const std::string&              tag,
   const std::vector<std::string>& meta_data
)
{
   string_meta_data_[tag] = meta_data;
}

inline void DenseVectorSpace::SetIntegerMetaData(
   const std::string&        tag,
   const std::vector<Index>& meta_data
)
{
   integer_meta_data_[tag] = meta_data;
}

inline void DenseVectorSpace::SetNumericMetaData(
   const std::string&         tag,
   const std::vector<Number>& meta_data
)
{
   numeric_meta_data_[tag] = meta_data;
}

inline const StringMetaDataMapType& DenseVectorSpace::GetStringMetaData() const
{
   return string_meta_data_;
}

inline const IntegerMetaDataMapType& DenseVectorSpace::GetIntegerMetaData() const
{
   return integer_meta_data_;
}

inline const NumericMetaDataMapType& DenseVectorSpace::GetNumericMetaData() const
{
   return numeric_meta_data_;
}

} // namespace Ipopt
#endif
