# ifndef CPPAD_CORE_VEC_AD_HPP
# define CPPAD_CORE_VEC_AD_HPP
/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-18 Bradley M. Bell

CppAD is distributed under the terms of the
             Eclipse Public License Version 2.0.

This Source Code may also be made available under the following
Secondary License when the conditions for such availability set forth
in the Eclipse Public License, Version 2.0 are satisfied:
      GNU General Public License, Version 2.0 or later.
---------------------------------------------------------------------------- */
/*
$begin VecAD$$
$spell
    cppad.hpp
    CondExpGt
    grep
    Ld
    vp
    Lu
    wc
    op
    Ldp
    Ldv
    Taylor
    VecAD
    const
    Cpp
$$


$section AD Vectors that Record Index Operations$$


$head Syntax$$
$codei%VecAD<%Base%> %v%(%n%)%$$
$pre
$$
$icode%v%.size()%$$
$pre
$$
$icode%b% = %v%[%i%]%$$
$pre
$$
$icode%r% = %v%[%x%]%$$

$head Purpose$$
If either $icode v$$ or $icode x$$ is a
$cref/variable/glossary/Variable/$$,
the indexing operation
$codei%
    %r% = %v%[%x%]
%$$
is recorded in the corresponding
AD of $icode Base$$
$cref/operation sequence/glossary/Operation/Sequence/$$ and
transferred to the corresponding $cref ADFun$$ object $icode f$$.
Such an index can change each time
zero order $cref/f.Forward/Forward/$$ is used; i.e.,
$icode f$$ is evaluated with new value for the
$cref/independent variables/glossary/Tape/Independent Variable/$$.
Note that the value of $icode y$$ depends on the value of $icode x$$
in a discrete fashion and CppAD computes its partial derivative with
respect to $icode x$$ as zero.

$head Alternatives$$
If only the values in the vector,
and not the indices,
depend on the independent variables,
the class $icode%Vector%< AD<%Base%> >%$$ is much more efficient for
storing AD values where $icode Vector$$ is any
$cref SimpleVector$$ template class,
If only the indices,
and not the values in the vector,
depend on the independent variables,
The $cref Discrete$$ functions are a much more efficient
way to represent these vectors.

$head VecAD<Base>::reference$$
The result $icode r$$ has type
$codei%
    VecAD<%Base%>::reference
%$$
which is very much like the $codei%AD<%Base%>%$$ type
with some notable exceptions:

$subhead Exceptions$$
$list number$$
The object $icode r$$ cannot be used with the
$cref Value$$ function to compute the corresponding $icode Base$$ value.
If $icode v$$ and $icode i$$ are not $cref/variables/glossary/Variable/$$
$codei%
    %b% = v[%i%]
%$$
can be used to compute the corresponding $icode Base$$ value.

$lnext
The object $icode r$$ cannot be used
with the $cref/compound assignments operators/Arithmetic/$$
$code +=$$,
$code -=$$,
$code *=$$, or
$code /=$$.
For example, the following syntax is not valid:
$codei%
    %v%[%x%] += %z%;
%$$
no matter what the types of $icode z$$.

$lnext
Assignment to $icode r$$ returns a $code void$$.
For example, the following syntax is not valid:
$codei%
    %z% = %v%[%x%] = %u%;
%$$
no matter what the types of $icode z$$, and $icode u$$.

$lnext
The $cref CondExp$$ functions do not accept
$codei%VecAD<%Base%>::reference%$$ arguments.
For example, the following syntax is not valid:
$codei%
    CondExpGt(%v%[%x%], %z%, %u%, %v%)
%$$
no matter what the types of $icode z$$, $icode u$$, and $icode v$$.

$lnext
The $cref/Parameter/con_dyn_var/Parameter/$$
and $cref/Variable/con_dyn_var/Variable/$$ functions cannot be used with
$codei%VecAD<%Base%>::reference%$$ arguments like $icode r$$,
use the entire $codei%VecAD<%Base%>%$$ vector instead; i.e. $icode v$$.

$lnext
The vectors passed to $cref Independent$$ must have elements
of type $codei%AD<%Base%>%$$; i.e., $cref VecAD$$ vectors
cannot be passed to $code Independent$$.

$lnext
If one uses this type in a
AD of $icode Base$$
$cref/operation sequence/glossary/Operation/Sequence/$$,
$cref/sparsity pattern/glossary/Sparsity Pattern/$$ calculations
($cref sparsity_pattern$$)
are less efficient because the dependence of different
elements of the vector cannot be separated.

$lend

$head Constructor$$

$subhead v$$
The syntax
$codei%
    VecAD<%Base%> %v%(%n%)
%$$
creates an $code VecAD$$ object $icode v$$ with
$icode n$$ elements.
The initial value of the elements of $icode v$$ is unspecified.

$head n$$
The argument $icode n$$ has prototype
$codei%
    size_t %n%
%$$

$head size$$
The syntax
$codei%
    %v%.size()
%$$
returns the number of elements in the vector $icode v$$;
i.e., the value of $icode n$$ when it was constructed.

$head size_t Indexing$$
We refer to the syntax
$codei%
    %b% = %v%[%i%]
%$$
as $code size_t$$ indexing of a $code VecAD$$ object.
This indexing is only valid if the vector $icode v$$ is a
$cref/parameter/con_dyn_var/Parameter/$$; i.e.,
it does not depend on the independent variables.

$subhead i$$
The operand $icode i$$ has prototype
$codei%
    size_t %i%
%$$
It must be greater than or equal zero
and less than $icode n$$; i.e., less than
the number of elements in $icode v$$.

$subhead b$$
The result $icode b$$ has prototype
$codei%
    %Base% %b%
%$$
and is a reference to the $th i$$ element in the vector $icode v$$.
It can be used to change the element value;
for example,
$codei%
    %v%[%i%] = %c%
%$$
is valid where $icode c$$ is a $icode Base$$ object.
The reference $icode b$$ is no longer valid once the
destructor for $icode v$$ is called; for example,
when $icode v$$ falls out of scope.

$head AD Indexing$$
We refer to the syntax
$codei%
    %r% = %v%[%x%]
%$$
as AD indexing of a $code VecAD$$ object.

$subhead x$$
The argument $icode x$$ has prototype
$codei%
    const AD<%Base%> &%x%
%$$
The value of $icode x$$ must be greater than or equal zero
and less than $icode n$$; i.e., less than
the number of elements in $icode v$$.

$subhead r$$
The result $icode r$$ has prototype
$codei%
    VecAD<%Base%>::reference %r%
%$$
The object $icode r$$ has an AD type and its
operations are recorded as part of the same
AD of $icode Base$$
$cref/operation sequence/glossary/Operation/Sequence/$$ as
for $codei%AD<%Base%>%$$ objects.
It acts as a reference to the
element with index $latex {\rm floor} (x)$$ in the vector $icode v$$
($latex {\rm floor} (x)$$ is
the greatest integer less than or equal $icode x$$).
Because it is a reference, it can be used to change the element value;
for example,
$codei%
    %v%[%x%] = %z%
%$$
is valid where $icode z$$ is an
$codei%VecAD<%Base%>::reference%$$ object.
As a reference, $icode r$$ is no longer valid once the
destructor for $icode v$$ is called; for example,
when $icode v$$ falls out of scope.

$head Dynamic Parameters$$
The index $icode x$$,
and the value $icode z$$ assigned to
$icode%v%[%x%]%$$ or $icode%v%[%i%]%$$,
cannot be $cref/dynamic parameters/glossary/Parameter/Dynamic/$$.

$head Example$$
$children%
    example/general/vec_ad.cpp
%$$
The file
$cref vec_ad.cpp$$
contains an example and test using $code VecAD$$ vectors.


$head Speed and Memory$$
The $cref VecAD$$ vector type is inefficient because every
time an element of a vector is accessed, a new CppAD
$cref/variable/glossary/Variable/$$ is created on the tape
using either the $code Ldp$$ or $code Ldv$$ operation
(unless all of the elements of the vector are
$cref/parameters/glossary/Parameter/$$).
The effect of this can be seen by executing the following steps:

$list number$$
In the file $code cppad/local/forward1sweep.h$$,
change the definition of $code CPPAD_FORWARD1SWEEP_TRACE$$ to
$codep
    # define CPPAD_FORWARD1SWEEP_TRACE 1
$$
$lnext
In the $code Example$$ directory, execute the command
$codep
    ./test_one.sh lu_vec_ad_ok.cpp lu_vec_ad.cpp -DNDEBUG > lu_vec_ad_ok.log
$$
This will write a trace of all the forward tape operations,
for the test case $cref lu_vec_ad_ok.cpp$$,
to the file $code lu_vec_ad_ok.log$$.
$lnext
In the $code Example$$ directory execute the commands
$codep
    grep "op="           lu_vec_ad_ok.log | wc -l
    grep "op=Ld[vp]"     lu_vec_ad_ok.log | wc -l
    grep "op=St[vp][vp]" lu_vec_ad_ok.log | wc -l
$$
The first command counts the number of operators in the tracing,
the second counts the number of VecAD load operations,
and the third counts the number of VecAD store operations.
(For CppAD version 05-11-20 these counts were 956, 348, and 118
respectively.)
$lend

$end
------------------------------------------------------------------------
*/
# include <cppad/local/pod_vector.hpp>

namespace CppAD { //  BEGIN_CPPAD_NAMESPACE
/*!
\file vec_ad.hpp
Defines the VecAD<Base> class.
*/

/*!
\def CPPAD_VEC_AD_COMPUTED_ASSIGNMENT(op, name)
Prints an error message if the correspinding compound assignment is used.

THis macro is used to print an error message if any of the
compound assignments are used with the VecAD_reference class.
The argument op is one of the following:
+= , -= , *= , /=.
The argument name, is a string literal with the name of the
compound assignment op.
*/
# define CPPAD_VEC_AD_COMPUTED_ASSIGNMENT(op, name)                     \
VecAD_reference& operator op (const VecAD_reference<Base> &right)       \
{   CPPAD_ASSERT_KNOWN(                                                \
        false,                                                        \
        "Cannot use a ADVec element on left side of" name             \
    );                                                                 \
    return *this;                                                      \
}                                                                       \
VecAD_reference& operator op (const AD<Base> &right)                    \
{   CPPAD_ASSERT_KNOWN(                                                \
        false,                                                        \
        "Cannot use a ADVec element on left side of" name             \
    );                                                                 \
    return *this;                                                      \
}                                                                       \
VecAD_reference& operator op (const Base &right)                        \
{   CPPAD_ASSERT_KNOWN(                                                \
        false,                                                        \
        "Cannot use a ADVec element on left side of" name             \
    );                                                                 \
    return *this;                                                      \
}

/*!
Class used to hold a reference to an element of a VecAD object.

\tparam Base
Elements of this class act like an AD<Base> (in a restricted sense),
in addition they track (on the tape) the index they correspond to.
*/
template <class Base>
class VecAD_reference {
    friend bool  Parameter<Base> (const VecAD<Base> &vec);
    friend bool  Variable<Base>  (const VecAD<Base> &vec);
    friend class VecAD<Base>;
    friend class local::ADTape<Base>;

private:
    /// pointer to vecad vector that this is a element of
    VecAD<Base> *vec_;
    /// index in vecad vector that this element corresponds to
    AD<Base>     ind_;         // index for this element
public:
    /*!
    consructor

    \param vec
    value of vec_

    \param ind
    value of ind_
    */
    VecAD_reference(VecAD<Base> *vec, const AD<Base>& ind)
        : vec_( vec ) , ind_(ind)
    {   CPPAD_ASSERT_KNOWN( ! Dynamic(ind),
            "index for element of this VecAD object is a dynamic paramerer"
        );
    }

    // assignment operators
    void operator = (const VecAD_reference<Base> &right);
    void operator = (const AD<Base> &right);
    void operator = (const Base     &right);
    void operator = (int             right);

    // compound assignments
    CPPAD_VEC_AD_COMPUTED_ASSIGNMENT( += , " += " )
    CPPAD_VEC_AD_COMPUTED_ASSIGNMENT( -= , " -= " )
    CPPAD_VEC_AD_COMPUTED_ASSIGNMENT( *= , " *= " )
    CPPAD_VEC_AD_COMPUTED_ASSIGNMENT( /= , " /= " )


    /// Conversion from VecAD_reference to AD<Base>.
    /// puts the correspond vecad load instruction in the tape.
    AD<Base> ADBase(void) const
    {   // start with default construtor (hence dynamic_ is false).
        AD<Base> result;

        size_t i = static_cast<size_t>( Integer(ind_) );
        CPPAD_ASSERT_UNKNOWN( i < vec_->length_ );

        // AD<Base> value corresponding to this element
        result.value_ = vec_->data_[i];

        // this address will be recorded in tape and must be
        // zero for parameters
        CPPAD_ASSERT_UNKNOWN( Parameter(result) );
        result.taddr_ = 0;

        // index corresponding to this element
        if( Variable(*vec_) )
        {
            local::ADTape<Base>* tape = AD<Base>::tape_ptr(vec_->tape_id_);
            CPPAD_ASSERT_UNKNOWN( tape != CPPAD_NULL );
            CPPAD_ASSERT_UNKNOWN( vec_->offset_ > 0  );

            size_t load_op_index = tape->Rec_.num_load_op_rec();
            if( IdenticalCon(ind_) )
            {   CPPAD_ASSERT_UNKNOWN( local::NumRes(local::LdpOp) == 1 );
                CPPAD_ASSERT_UNKNOWN( local::NumArg(local::LdpOp) == 3 );

                // put operand addresses in tape
                tape->Rec_.PutArg(
                    (addr_t) vec_->offset_, (addr_t) i, (addr_t) load_op_index
                );
                // put operator in the tape, ind_ is a parameter
                result.taddr_ = tape->Rec_.PutLoadOp(local::LdpOp);

                // change result to variable for this load
                result.tape_id_ = tape->id_;
                result.ad_type_ = variable_enum;
            }
            else
            {   CPPAD_ASSERT_UNKNOWN( local::NumRes(local::LdvOp) == 1 );
                CPPAD_ASSERT_UNKNOWN( local::NumArg(local::LdvOp) == 3 );
                addr_t ind_taddr;
                if( Parameter(ind_) )
                {   // kludge that should not be needed
                    // if ind_ instead of i is used for index
                    // in the tape
                    ind_taddr  = tape->RecordParOp(
                        ind_.value_
                    );
                }
                else
                    ind_taddr = ind_.taddr_;
                CPPAD_ASSERT_UNKNOWN( ind_taddr > 0 );

                // put operand addresses in tape
                tape->Rec_.PutArg(
                    (addr_t) vec_->offset_,
                    (addr_t) ind_taddr,
                    (addr_t) load_op_index
                );
                // put operator in the tape, ind_ is a variable
                result.taddr_ = tape->Rec_.PutLoadOp(local::LdvOp);

                // change result to variable for this load
                result.tape_id_ = tape->id_;
                result.ad_type_ = variable_enum;
            }
        }
        return result;
    }
};

/*!
Vector of AD objects that tracks indexing operations on the tape.
*/
template <class Base>
class VecAD {
    friend bool  Parameter<Base> (const VecAD<Base> &vec);
    friend bool  Variable<Base>  (const VecAD<Base> &vec);
    friend class local::ADTape<Base>;
    friend class VecAD_reference<Base>;

    friend std::ostream& operator << <Base>
        (std::ostream &os, const VecAD<Base> &vec_);
private:
    /// size of this VecAD vector
    const  size_t   length_;

    /// elements of this vector
    local::pod_vector_maybe<Base> data_;

    /// offset in cummulate vector corresponding to this object
    size_t offset_;

    /// tape id corresponding to the offset
    tape_id_t tape_id_;
public:
    /// declare the user's view of this type here
    typedef VecAD_reference<Base> reference;

    /// default constructor
    /// initialize tape_id_ same as for default constructor; see default.hpp
    VecAD(void)
    : length_(0)
    , offset_(0)
    , tape_id_(0)
    {   CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); }

    /// sizing constructor
    /// initialize tape_id_ same as for parameters; see ad_copy.hpp
    VecAD(size_t n)
    : length_(n)
    , offset_(0)
    , tape_id_(0)
    {   if( length_ > 0 )
        {   size_t i;
            Base zero(0);
            data_.extend(length_);

            // Initialize data to zero so all have same value.
            // This uses less memory and avoids a valgrind error
            // during TapeRec<Base>::PutPar
            for(i = 0; i < length_; i++)
                data_[i] = zero;
        }
        CPPAD_ASSERT_UNKNOWN( Parameter(*this) );
    }

    /// destructor
    ~VecAD(void)
    { }

    /// number of elements in the vector
    size_t size(void)
    {   return length_; }

    /// element access (not taped)
    ///
    /// \param i
    /// element index
    Base &operator[](size_t i)
    {
        CPPAD_ASSERT_KNOWN(
            Parameter(*this),
            "VecAD: cannot use size_t indexing because this"
            " VecAD vector is a variable."
        );
        CPPAD_ASSERT_KNOWN(
            i < length_,
            "VecAD: element index is >= vector length"
        );

        return data_[i];
    }

    /*! delayed taped elemement access

    \param x
    element index

    \par
    This operation may convert this vector from a parameter to a variable
    */
    VecAD_reference<Base> operator[](const AD<Base> &x)
    {
        CPPAD_ASSERT_KNOWN(
            0 <= Integer(x),
            "VecAD: element index is less than zero"
        );
        CPPAD_ASSERT_KNOWN(
            static_cast<size_t>( Integer(x) ) < length_,
            "VecAD: element index is >= vector length"
        );

        // if no need to track indexing operation, return now
        if( Parameter(*this) & Parameter(x) )
            return VecAD_reference<Base>(this, x);

        CPPAD_ASSERT_KNOWN(
            Parameter(*this) | Parameter(x) | (tape_id_ == x.tape_id_),
            "VecAD: vector and index are variables for"
            " different tapes."
        );

        if( Parameter(*this) )
        {   // must place a copy of vector in tape
            offset_ =
            AD<Base>::tape_ptr(x.tape_id_)->AddVec(length_, data_);

            // Advance pointer by one so starts at first component of this
            // vector; i.e., skip length at begining (so is always > 0)
            offset_++;

            // tape id corresponding to this offest
            tape_id_ = x.tape_id_;
        }

        return VecAD_reference<Base>(this, x);
    }

};


/*!
Taped setting of element to a value.

\param y
value that element is set to.
*/
template <class Base>
void VecAD_reference<Base>::operator=(const AD<Base> &y)
{
    CPPAD_ASSERT_KNOWN( ! Dynamic(y),
        "value assigned to element of VecAD object is a dynamic paramerer"
    );

    if( Parameter(y) )
    {   // fold into the Base type assignment
        *this = y.value_;
        return;
    }
    CPPAD_ASSERT_UNKNOWN( y.taddr_ > 0 );

    CPPAD_ASSERT_KNOWN(
        Parameter(*vec_) | (vec_->tape_id_ == y.tape_id_),
        "VecAD assignment: vector and new element value are variables"
        "\nfor different tapes."
    );

    local::ADTape<Base>* tape = AD<Base>::tape_ptr(y.tape_id_);
    CPPAD_ASSERT_UNKNOWN( tape != CPPAD_NULL );
    if( Parameter(*vec_) )
    {   // must place a copy of vector in tape
        vec_->offset_ = tape->AddVec(vec_->length_, vec_->data_);

        // advance offset to be start of vector plus one
        (vec_->offset_)++;

        // tape id corresponding to this offest
        vec_->tape_id_ = y.tape_id_;
    }
    CPPAD_ASSERT_UNKNOWN( Variable(*vec_) );


    // index in vector for this element
    size_t i = static_cast<size_t>( Integer(ind_) );
    CPPAD_ASSERT_UNKNOWN( i < vec_->length_ );

    // assign value for this element (as an AD<Base> object)
    vec_->data_[i] = y.value_;

    // record the setting of this array element
    CPPAD_ASSERT_UNKNOWN( vec_->offset_ > 0 );
    if( Parameter(ind_) )
    {   CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StpvOp) == 3 );
        CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StpvOp) == 0 );

        // put operand addresses in tape
        tape->Rec_.PutArg((addr_t) vec_->offset_, (addr_t) i, y.taddr_);

        // put operator in the tape, ind_ is parameter, y is variable
        tape->Rec_.PutOp(local::StpvOp);
    }
    else
    {   CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StvvOp) == 3 );
        CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StvvOp) == 0 );
        CPPAD_ASSERT_UNKNOWN( ind_.taddr_ > 0 );

        // put operand addresses in tape
        tape->Rec_.PutArg((addr_t) vec_->offset_, ind_.taddr_, y.taddr_);

        // put operator in the tape, ind_ is variable, y is variable
        tape->Rec_.PutOp(local::StvvOp);
    }
}


/*!
Taped setting of element to a value.

\param y
value that element is set to.
*/
template <class Base>
void VecAD_reference<Base>::operator=(const Base &y)
{
    size_t i = static_cast<size_t>( Integer(ind_) );
    CPPAD_ASSERT_UNKNOWN( i < vec_->length_ );

    // assign value for this element
    vec_->data_[i] = y;

    // check if this ADVec object is a parameter
    if( Parameter(*vec_) )
        return;

    local::ADTape<Base>* tape = AD<Base>::tape_ptr(vec_->tape_id_);
    CPPAD_ASSERT_UNKNOWN( tape != CPPAD_NULL );

    // put value of the parameter y in the tape
    addr_t p = tape->Rec_.put_con_par(y);

    // record the setting of this array element
    CPPAD_ASSERT_UNKNOWN( vec_->offset_ > 0 );
    if( Parameter(ind_) )
    {   CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StppOp) == 3 );
        CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StppOp) == 0 );

        // put operand addresses in tape
        tape->Rec_.PutArg((addr_t) vec_->offset_, (addr_t) i, p);

        // put operator in the tape, ind_ is parameter, y is parameter
        tape->Rec_.PutOp(local::StppOp);
    }
    else
    {   CPPAD_ASSERT_UNKNOWN( local::NumArg(local::StvpOp) == 3 );
        CPPAD_ASSERT_UNKNOWN( local::NumRes(local::StvpOp) == 0 );
        CPPAD_ASSERT_UNKNOWN( ind_.taddr_ > 0 );

        // put operand addresses in tape
        tape->Rec_.PutArg((addr_t) vec_->offset_, ind_.taddr_, p);

        // put operator in the tape, ind_ is variable, y is parameter
        tape->Rec_.PutOp(local::StvpOp);
    }
}

/*!
Taped setting of element to a value.

\param y
value that element is set to.

\par
this case gets folded into case where value is AD<Base>.
*/
template <class Base>
void VecAD_reference<Base>::operator=
(const VecAD_reference<Base> &y)
{   *this = y.ADBase(); }

/*!
Taped setting of element to a value.

\param y
value that element is set to.

\par
this case gets folded into case where value is Base.
*/
template <class Base>
void VecAD_reference<Base>::operator=(int y)
{   *this = Base(y); }


} // END_CPPAD_NAMESPACE

// preprocessor symbols that are local to this file
# undef CPPAD_VEC_AD_COMPUTED_ASSIGNMENT

# endif
