# ifndef CPPAD_CORE_AD_HPP
# define CPPAD_CORE_AD_HPP

/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell

CppAD is distributed under multiple licenses. This distribution is under
the terms of the
                    Eclipse Public License Version 1.0.

A copy of this license is included in the COPYING file of this distribution.
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
-------------------------------------------------------------------------- */

// simple AD operations that must be defined for AD as well as base class
# include <cppad/core/ordered.hpp>
# include <cppad/core/identical.hpp>

// define the template classes that are used by the AD template class
# include <cppad/local/op_code.hpp>
# include <cppad/local/recorder.hpp>
# include <cppad/local/player.hpp>
# include <cppad/local/ad_tape.hpp>

namespace CppAD { // BEGIN_CPPAD_NAMESPACE

typedef enum {
	tape_manage_new,
	tape_manage_delete,
	tape_manage_clear
} tape_manage_job;

template <class Base>
class AD {
private :
	// -----------------------------------------------------------------------
	// value_ corresponding to this object
	Base value_;

	// Tape identifier corresponding to taddr
	tape_id_t tape_id_;

	// taddr_ in tape for this variable
	addr_t taddr_;
	// -----------------------------------------------------------------------

	// enable use of AD<Base> in parallel mode
	template <class Type>
	friend void parallel_ad(void);

	// template friend functions where template parameter is not bound
	template <class VectorAD>
	friend void Independent(VectorAD &x, size_t abort_op_index);

	// one argument functions
	friend bool Parameter          <Base>
		(const AD<Base>    &u);
	friend bool Parameter          <Base>
		(const VecAD<Base> &u);
	friend bool Variable           <Base>
		(const AD<Base>    &u);
	friend bool Variable           <Base>
		(const VecAD<Base> &u);
	friend int  Integer            <Base>
		(const AD<Base>    &u);
	friend AD   Var2Par            <Base>
		(const AD<Base>    &u);

	// power function
	friend AD pow <Base>
		(const AD<Base> &x, const AD<Base> &y);

	// azmul function
	friend AD azmul <Base>
		(const AD<Base> &x, const AD<Base> &y);

	// order determining functions, see ordered.hpp
	friend bool GreaterThanZero   <Base> (const AD<Base> &x);
	friend bool GreaterThanOrZero <Base> (const AD<Base> &x);
	friend bool LessThanZero      <Base> (const AD<Base> &x);
	friend bool LessThanOrZero    <Base> (const AD<Base> &x);
	friend bool abs_geq           <Base>
		(const AD<Base>& x, const AD<Base>& y);

	// The identical property functions, see identical.hpp
	friend bool IdenticalPar      <Base> (const AD<Base> &x);
	friend bool IdenticalZero     <Base> (const AD<Base> &x);
	friend bool IdenticalOne      <Base> (const AD<Base> &x);
	friend bool IdenticalEqualPar <Base>
		(const AD<Base> &x, const AD<Base> &y);

	// EqualOpSeq function
	friend bool EqualOpSeq <Base>
		(const AD<Base> &u, const AD<Base> &v);

	// NearEqual function
	friend bool NearEqual <Base> (
	const AD<Base> &x, const AD<Base> &y, const Base &r, const Base &a);

	friend bool NearEqual <Base> (
	const Base &x, const AD<Base> &y, const Base &r, const Base &a);

	friend bool NearEqual <Base> (
	const AD<Base> &x, const Base &y, const Base &r, const Base &a);

	// CondExp function
	friend AD<Base> CondExpOp  <Base> (
		enum CompareOp  cop       ,
		const AD<Base> &left      ,
		const AD<Base> &right     ,
		const AD<Base> &trueCase  ,
		const AD<Base> &falseCase
	);

	// classes
	friend class local::ADTape<Base>;
	friend class ADFun<Base>;
	friend class atomic_base<Base>;
	friend class discrete<Base>;
	friend class VecAD<Base>;
	friend class VecAD_reference<Base>;

	// arithematic binary operators
	friend AD<Base> operator + <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend AD<Base> operator - <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend AD<Base> operator * <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend AD<Base> operator / <Base>
		(const AD<Base> &left, const AD<Base> &right);

	// comparison operators
	friend bool operator < <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend bool operator <= <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend bool operator > <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend bool operator >= <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend bool operator == <Base>
		(const AD<Base> &left, const AD<Base> &right);
	friend bool operator != <Base>
		(const AD<Base> &left, const AD<Base> &right);

	// input operator
	friend std::istream& operator >> <Base>
		(std::istream &is, AD<Base> &x);

	// output operations
	friend std::ostream& operator << <Base>
		(std::ostream &os, const AD<Base> &x);
	friend void PrintFor <Base> (
		const AD<Base>&    flag   ,
		const char*        before ,
		const AD<Base>&    var    ,
		const char*        after
	);
public:
	// type of value
	typedef Base value_type;

	// implicit default constructor
	inline AD(void);

	// destructor
	~AD(void) { }

	// use default implicit copy constructor
	// inline AD(const AD &x);

# ifdef CPPAD_FOR_TMB
	// TMB would rather have implicit construction from double,
	// CppAD uses default constructor and assignment to double instead.
	inline AD(const double &d);
# else
	// implicit construction from base type
	inline AD(const Base &b);
# endif

	// implicit contructor from VecAD<Base>::reference
	inline AD(const VecAD_reference<Base> &x);

	// explicit construction from some other type (depricated)
	template <class T> inline explicit AD(const T &t);

	// conversion from AD to Base type
	friend Base Value <Base> (const AD<Base> &x);

	// use default assignment operator
	// inline AD& operator=(const AD &x);

	// assingment from base type
	inline AD& operator=(const Base &b);

	// assignment from VecAD<Base>::reference
	inline AD& operator=(const VecAD_reference<Base> &x);

	// assignment from some other type
	template <class T> inline AD& operator=(const T &right);

	// compound assignment operators
	inline AD& operator += (const AD &right);
	inline AD& operator -= (const AD &right);
	inline AD& operator *= (const AD &right);
	inline AD& operator /= (const AD &right);

	// unary operators
	inline AD operator +(void) const;
	inline AD operator -(void) const;

	// interface so these functions need not be friends
	inline AD abs_me(void) const;
	inline AD acos_me(void) const;
	inline AD asin_me(void) const;
	inline AD atan_me(void) const;
	inline AD cos_me(void) const;
	inline AD cosh_me(void) const;
	inline AD exp_me(void) const;
	inline AD fabs_me(void) const;
	inline AD log_me(void) const;
	inline AD sin_me(void) const;
	inline AD sign_me(void) const;
	inline AD sinh_me(void) const;
	inline AD sqrt_me(void) const;
	inline AD tan_me(void) const;
	inline AD tanh_me(void) const;
# if CPPAD_USE_CPLUSPLUS_2011
	inline AD erf_me(void) const;
	inline AD asinh_me(void) const;
	inline AD acosh_me(void) const;
	inline AD atanh_me(void) const;
	inline AD expm1_me(void) const;
	inline AD log1p_me(void) const;
# endif

	// ----------------------------------------------------------
	// static public member functions

	// abort current AD<Base> recording
	static void        abort_recording(void);

	// set the maximum number of OpenMP threads (deprecated)
	static void        omp_max_thread(size_t number);

	// These functions declared public so can be accessed by user through
	// a macro interface and are not intended for direct use.
	// The macro interface is documented in bool_fun.hpp.
	// Developer documentation for these fucntions is in  bool_fun.hpp
	static inline bool UnaryBool(
		bool FunName(const Base &x),
		const AD<Base> &x
	);
	static inline bool BinaryBool(
		bool FunName(const Base &x, const Base &y),
		const AD<Base> &x , const AD<Base> &y
	);

private:
	//
	// Make this variable a parameter
	//
	void make_parameter(void)
	{	CPPAD_ASSERT_UNKNOWN( Variable(*this) );  // currently a var
		tape_id_ = 0;
	}
	//
	// Make this parameter a new variable
	//
	void make_variable(tape_id_t id,  addr_t taddr)
	{	CPPAD_ASSERT_UNKNOWN( Parameter(*this) ); // currently a par
		CPPAD_ASSERT_UNKNOWN( taddr > 0 );        // sure valid taddr

		taddr_   = taddr;
		tape_id_ = id;
	}
	// ---------------------------------------------------------------
	// tape linking functions
	//
	// not static
	inline local::ADTape<Base>* tape_this(void) const;
	//
	// static
	inline static tape_id_t**    tape_id_handle(size_t thread);
	inline static tape_id_t*     tape_id_ptr(size_t thread);
	inline static local::ADTape<Base>** tape_handle(size_t thread);
	static local::ADTape<Base>*         tape_manage(tape_manage_job job);
	inline static local::ADTape<Base>*  tape_ptr(void);
	inline static local::ADTape<Base>*  tape_ptr(tape_id_t tape_id);
};
// ---------------------------------------------------------------------------

} // END_CPPAD_NAMESPACE

// tape linking private functions
# include <cppad/core/tape_link.hpp>

// operations that expect the AD template class to be defined


# endif
