//=================================================================================================
/*!
//  \file blaze/math/expressions/SVecTransposer.h
//  \brief Header file for the sparse vector transposer
//
//  Copyright (C) 2012-2020 Klaus Iglberger - All Rights Reserved
//
//  This file is part of the Blaze library. You can redistribute it and/or modify it under
//  the terms of the New (Revised) BSD License. Redistribution and use in source and binary
//  forms, with or without modification, are permitted provided that the following conditions
//  are met:
//
//  1. Redistributions of source code must retain the above copyright notice, this list of
//     conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright notice, this list
//     of conditions and the following disclaimer in the documentation and/or other materials
//     provided with the distribution.
//  3. Neither the names of the Blaze development group nor the names of its contributors
//     may be used to endorse or promote products derived from this software without specific
//     prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
//  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//  DAMAGE.
*/
//=================================================================================================

#ifndef _BLAZE_MATH_EXPRESSIONS_SVECTRANSPOSER_H_
#define _BLAZE_MATH_EXPRESSIONS_SVECTRANSPOSER_H_


//*************************************************************************************************
// Includes
//*************************************************************************************************

#include <blaze/math/Aliases.h>
#include <blaze/math/constraints/Computation.h>
#include <blaze/math/constraints/SparseVector.h>
#include <blaze/math/constraints/TransposeFlag.h>
#include <blaze/math/Exception.h>
#include <blaze/math/expressions/SparseVector.h>
#include <blaze/math/shims/IsDefault.h>
#include <blaze/math/typetraits/MaxSize.h>
#include <blaze/math/typetraits/Size.h>
#include <blaze/system/MacroDisable.h>
#include <blaze/util/algorithms/Max.h>
#include <blaze/util/algorithms/Min.h>
#include <blaze/util/Assert.h>
#include <blaze/util/Types.h>


namespace blaze {

//=================================================================================================
//
//  CLASS SVECTRANSPOSER
//
//=================================================================================================

//*************************************************************************************************
/*!\brief Expression object for the transposition of a sparse vector.
// \ingroup sparse_vector_expression
//
// The SVecTransposer class is a wrapper object for the temporary transposition of a sparse vector.
*/
template< typename VT  // Type of the sparse vector
        , bool TF >    // Transpose flag
class SVecTransposer
   : public SparseVector< SVecTransposer<VT,TF>, TF >
{
 public:
   //**Type definitions****************************************************************************
   using This           = SVecTransposer<VT,TF>;  //!< Type of this SVecTransposer instance.
   using BaseType       = SparseVector<This,TF>;  //!< Base type of this SVecTransposer instance.
   using ResultType     = TransposeType_t<VT>;    //!< Result type for expression template evaluations.
   using TransposeType  = ResultType_t<VT>;       //!< Transpose type for expression template evaluations.
   using ElementType    = ElementType_t<VT>;      //!< Resulting element type.
   using ReturnType     = ReturnType_t<VT>;       //!< Return type for expression template evaluations.
   using CompositeType  = const This&;            //!< Data type for composite expression templates.
   using Reference      = Reference_t<VT>;        //!< Reference to a non-constant matrix value.
   using ConstReference = ConstReference_t<VT>;   //!< Reference to a constant matrix value.
   using Iterator       = Iterator_t<VT>;         //!< Iterator over non-constant elements.
   using ConstIterator  = ConstIterator_t<VT>;    //!< Iterator over constant elements.
   //**********************************************************************************************

   //**Compilation flags***************************************************************************
   //! Compilation flag for SMP assignments.
   /*! The \a smpAssignable compilation flag indicates whether the vector can be used in SMP
       (shared memory parallel) assignments (both on the left-hand and right-hand side of the
       assignment). */
   static constexpr bool smpAssignable = VT::smpAssignable;
   //**********************************************************************************************

   //**Constructor*********************************************************************************
   /*!\brief Constructor for the SVecTransposer class.
   //
   // \param sv The sparse vector operand.
   */
   explicit inline SVecTransposer( VT& sv ) noexcept
      : sv_( sv )  // The sparse vector operand
   {}
   //**********************************************************************************************

   //**Subscript operator**************************************************************************
   /*!\brief Subscript operator for the direct access to the vector elements.
   //
   // \param index Access index. The index has to be in the range \f$[0..N-1]\f$.
   // \return Reference to the accessed value.
   */
   inline ConstReference operator[]( size_t index ) const {
      BLAZE_USER_ASSERT( index < sv_.size(), "Invalid vector access index" );
      return const_cast<const VT&>( sv_ )[index];
   }
   //**********************************************************************************************

   //**At function*********************************************************************************
   /*!\brief Checked access to the vector elements.
   //
   // \param index Access index. The index has to be in the range \f$[0..N-1]\f$.
   // \return The resulting value.
   // \exception std::out_of_range Invalid vector access index.
   */
   inline ConstReference at( size_t index ) const {
      if( index >= sv_.size() ) {
         BLAZE_THROW_OUT_OF_RANGE( "Invalid vector access index" );
      }
      return (*this)[index];
   }
   //**********************************************************************************************

   //**Begin function******************************************************************************
   /*!\brief Returns an iterator to the first non-zero element of the sparse vector.
   //
   // \return Iterator to the first non-zero element of the sparse vector.
   */
   inline Iterator begin() {
      return sv_.begin();
   }
   //**********************************************************************************************

   //**Begin function******************************************************************************
   /*!\brief Returns an iterator to the first non-zero element of the sparse vector.
   //
   // \return Iterator to the first non-zero element of the sparse vector.
   */
   inline ConstIterator begin() const {
      return sv_.cbegin();
   }
   //**********************************************************************************************

   //**Cbegin function*****************************************************************************
   /*!\brief Returns an iterator to the first non-zero element of the sparse vector.
   //
   // \return Iterator to the first non-zero element of the sparse vector.
   */
   inline ConstIterator cbegin() const {
      return sv_.cbegin();
   }
   //**********************************************************************************************

   //**End function********************************************************************************
   /*!\brief Returns an iterator just past the last non-zero element of the sparse vector.
   //
   // \return Iterator just past the last non-zero element of the sparse vector.
   */
   inline Iterator end() {
      return sv_.end();
   }
   //**********************************************************************************************

   //**End function********************************************************************************
   /*!\brief Returns an iterator just past the last non-zero element of the sparse vector.
   //
   // \return Iterator just past the last non-zero element of the sparse vector.
   */
   inline ConstIterator end() const {
      return sv_.cend();
   }
   //**********************************************************************************************

   //**Cend function*******************************************************************************
   /*!\brief Returns an iterator just past the last non-zero element of the sparse vector.
   //
   // \return Iterator just past the last non-zero element of the sparse vector.
   */
   inline ConstIterator cend() const {
      return sv_.cend();
   }
   //**********************************************************************************************

   //**Size function*******************************************************************************
   /*!\brief Returns the current size/dimension of the vector.
   //
   // \return The size of the vector.
   */
   inline size_t size() const noexcept {
      return sv_.size();
   }
   //**********************************************************************************************

   //**Capacity function***************************************************************************
   /*!\brief Returns the maximum capacity of the vector.
   //
   // \return The capacity of the vector.
   */
   inline size_t capacity() const noexcept {
      return sv_.capacity();
   }
   //**********************************************************************************************

   //**NonZeros function***************************************************************************
   /*!\brief Returns the number of non-zero elements in the vector.
   //
   // \return The number of non-zero elements in the vector.
   */
   inline size_t nonZeros() const {
      return sv_.nonZeros();
   }
   //**********************************************************************************************

   //**Reset function******************************************************************************
   /*!\brief Resets the vector elements.
   //
   // \return void
   */
   inline void reset() {
      return sv_.reset();
   }
   //**********************************************************************************************

   //**Insert function*****************************************************************************
   /*!\brief Inserting an element into the sparse vector.
   //
   // \param index The index of the new element. The index has to be in the range \f$[0..N-1]\f$.
   // \param value The value of the element to be inserted.
   // \return Iterator to the inserted element.
   // \exception std::invalid_argument Invalid sparse vector access index.
   //
   // This function inserts a new element into the sparse vector. However, duplicate elements are
   // not allowed. In case the sparse matrix already contains an element with row index \a index,
   // a \a std::invalid_argument exception is thrown.
   */
   inline Iterator insert( size_t index, const ElementType& value ) {
      return sv_.insert( index, value );
   }
   //**********************************************************************************************

   //**Find function*******************************************************************************
   /*!\brief Inserting an element into the sparse vector.
   //
   // \param index The index of the search element. The index has to be in the range \f$[0..N-1]\f$.
   // \return Iterator to the element in case the index is found, end() iterator otherwise.
   //
   // This function can be used to check whether a specific element is contained in the sparse
   // vector. It specifically searches for the element with index \a index. In case the element
   // is found, the function returns an iterator to the element. Otherwise an iterator just past
   // the last non-zero element of the sparse vector (the end() iterator) is returned. Note that
   // the returned sparse vector iterator is subject to invalidation due to inserting operations
   // via the subscript operator or the insert() function!
   */
   inline Iterator find( size_t index ) {
      return sv_.find( index );
   }
   //**********************************************************************************************

   //**Reserve function****************************************************************************
   /*!\brief Setting the minimum capacity of the sparse vector.
   //
   // \param nonzeros The new minimum capacity of the sparse vector.
   // \return void
   //
   // This function increases the capacity of the sparse vector to at least \a nonzeros elements.
   // The current values of the vector elements are preserved.
   */
   inline void reserve( size_t nonzeros ) {
      sv_.reserve( nonzeros );
   }
   //**********************************************************************************************

   //**Append function*****************************************************************************
   /*!\brief Appending an element to the sparse vector.
   //
   // \param index The index of the new element. The index has to be in the range \f$[0..N-1]\f$.
   // \param value The value of the element to be appended.
   // \param check \a true if the new value should be checked for default values, \a false if not.
   // \return void
   //
   // This function provides a very efficient way to fill a sparse vector with elements. It
   // appends a new element to the end of the sparse vector without any additional memory
   // allocation. Therefore it is strictly necessary to keep the following preconditions in
   // mind:
   //
   //  - the index of the new element must be strictly larger than the largest index of non-zero
   //    elements in the sparse vector
   //  - the current number of non-zero elements must be smaller than the capacity of the vector
   //
   // Ignoring these preconditions might result in undefined behavior! The optional \a check
   // parameter specifies whether the new value should be tested for a default value. If the new
   // value is a default value (for instance 0 in case of an integral element type) the value is
   // not appended. Per default the values are not tested.
   //
   // \note Although append() does not allocate new memory, it still invalidates all iterators
   // returned by the end() functions!
   */
   inline void append( size_t index, const ElementType& value, bool check=false ) {
      sv_.append( index, value, check );
   }
   //**********************************************************************************************

   //**IsIntact function***************************************************************************
   /*!\brief Returns whether the invariants of the vector are intact.
   //
   // \return \a true in case the vector's invariants are intact, \a false otherwise.
   */
   inline bool isIntact() const noexcept
   {
      using blaze::isIntact;
      return isIntact( sv_ );
   }
   //**********************************************************************************************

   //**CanAlias function***************************************************************************
   /*!\brief Returns whether the vector can alias with the given address \a alias.
   //
   // \param alias The alias to be checked.
   // \return \a true in case the alias corresponds to this vector, \a false if not.
   */
   template< typename Other >  // Data type of the foreign expression
   inline bool canAlias( const Other* alias ) const noexcept
   {
      return sv_.canAlias( alias );
   }
   //**********************************************************************************************

   //**IsAliased function**************************************************************************
   /*!\brief Returns whether the vector is aliased with the given address \a alias.
   //
   // \param alias The alias to be checked.
   // \return \a true in case the alias corresponds to this vector, \a false if not.
   */
   template< typename Other >  // Data type of the foreign expression
   inline bool isAliased( const Other* alias ) const noexcept
   {
      return sv_.isAliased( alias );
   }
   //**********************************************************************************************

   //**CanSMPAssign function***********************************************************************
   /*!\brief Returns whether the vector can be used in SMP assignments.
   //
   // \return \a true in case the vector can be used in SMP assignments, \a false if not.
   */
   inline bool canSMPAssign() const noexcept
   {
      return sv_.canSMPAssign();
   }
   //**********************************************************************************************

   //**Transpose assignment of vectors*************************************************************
   /*!\brief Implementation of the transpose assignment of a vector.
   //
   // \param rhs The right-hand side vector to be assigned.
   // \return void
   //
   // This function must \b NOT be called explicitly! It is used internally for the performance
   // optimized evaluation of expression templates. Calling this function explicitly might result
   // in erroneous results and/or in compilation errors. Instead of using this function use the
   // assignment operator.
   */
   template< typename VT2 >  // Type of the right-hand side vector
   inline void assign( const Vector<VT2,TF>& rhs )
   {
      BLAZE_CONSTRAINT_MUST_BE_VECTOR_WITH_TRANSPOSE_FLAG( VT2, TF );

      sv_.assign( trans( *rhs ) );
   }
   //**********************************************************************************************

 private:
   //**********************************************************************************************
   /*!\brief Calculating a new vector capacity.
   //
   // \return The new compressed vector capacity.
   //
   // This function calculates a new vector capacity based on the current capacity of the sparse
   // vector. Note that the new capacity is restricted to the interval \f$[7..size]\f$.
   */
   inline size_t extendCapacity() const noexcept
   {
      using blaze::max;
      using blaze::min;

      size_t nonzeros( 2UL*sv_.capacity()+1UL );
      nonzeros = max( nonzeros, 7UL );
      nonzeros = min( nonzeros, sv_.size() );

      BLAZE_INTERNAL_ASSERT( nonzeros > sv_.capacity(), "Invalid capacity value" );

      return nonzeros;
   }
   //**********************************************************************************************

   //**Member variables****************************************************************************
   VT& sv_;  //!< The sparse vector operand.
   //**********************************************************************************************

   //**Compile time checks*************************************************************************
   /*! \cond BLAZE_INTERNAL */
   BLAZE_CONSTRAINT_MUST_BE_SPARSE_VECTOR_TYPE( VT );
   BLAZE_CONSTRAINT_MUST_BE_VECTOR_WITH_TRANSPOSE_FLAG( VT, !TF );
   BLAZE_CONSTRAINT_MUST_NOT_BE_COMPUTATION_TYPE( VT );
   /*! \endcond */
   //**********************************************************************************************
};
//*************************************************************************************************




//=================================================================================================
//
//  GLOBAL OPERATORS
//
//=================================================================================================

//*************************************************************************************************
/*! \cond BLAZE_INTERNAL */
/*!\brief Returns whether the invariants of the given SVecTransposer are intact.
// \ingroup sparse_vector_expression
//
// \param v The sparse vector to be tested.
// \return \a true in caes the given vector's invariants are intact, \a false otherwise.
*/
template< typename VT  // Type of the sparse vector
        , bool TF >    // Transpose flag
inline bool isIntact( const SVecTransposer<VT,TF>& v ) noexcept
{
   return v.isIntact();
}
/*! \endcond */
//*************************************************************************************************




//=================================================================================================
//
//  SIZE SPECIALIZATIONS
//
//=================================================================================================

//*************************************************************************************************
/*! \cond BLAZE_INTERNAL */
template< typename VT, bool TF >
struct Size< SVecTransposer<VT,TF>, 0UL >
   : public Size<VT,0UL>
{};
/*! \endcond */
//*************************************************************************************************




//=================================================================================================
//
//  MAXSIZE SPECIALIZATIONS
//
//=================================================================================================

//*************************************************************************************************
/*! \cond BLAZE_INTERNAL */
template< typename VT, bool TF >
struct MaxSize< SVecTransposer<VT,TF>, 0UL >
   : public MaxSize<VT,0UL>
{};
/*! \endcond */
//*************************************************************************************************

} // namespace blaze

#endif
