/* $Id$ */
# ifndef CPPAD_TRACK_NEW_DEL_INCLUDED
# define CPPAD_TRACK_NEW_DEL_INCLUDED

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

CppAD is distributed under multiple licenses. This distribution is under
the terms of the 
                    GNU General Public License Version 3.

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.
-------------------------------------------------------------------------- */
/*
$begin TrackNewDel$$
$spell
	cppad.hpp
	Cpp
	newptr
	Vec
	oldptr
	newlen
	ncopy
	const
$$

$section Routines That Track Use of New and Delete$$
$index new, track$$
$index delete, track$$
$index track, new and delete$$
$index memory, track$$

$head Deprecated 2007-07-23$$
$index deprecated, track memory$$
All these routines have been deprecated.
You should use the $cref thread_alloc$$ memory allocator instead
(which works better in both a single thread and 
properly in multi-threading environment).

$head Syntax$$
$codei%# include <cppad/track_new_del.hpp>
%$$
$icode%newptr% = TrackNewVec(%file%, %line%, %newlen%, %oldptr%)
%$$
$codei%TrackDelVec(%file%, %line%, %oldptr%)
%$$
$icode%newptr% = TrackExtend(%file%, %line%, %newlen%, %ncopy%, %oldptr%)
%$$
$icode%count% = TrackCount(%file%, %line%)%$$


$head Purpose$$
These routines 
aid in the use of $code new[]$$ and  $code delete[]$$
during the execution of a C++ program.

$head Include$$
The file $code cppad/track_new_del.hpp$$ is included by 
$code cppad/cppad.hpp$$
but it can also be included separately with out the rest of the 
CppAD include files.


$head file$$
The argument $icode file$$ has prototype
$codei%
	const char *%file%
%$$
It should be the source code file name 
where the call to $code TrackNew$$ is located.
The best way to accomplish this is the use the preprocessor symbol
$code __FILE__$$ for this argument.

$head line$$
The argument $icode line$$ has prototype
$codei%
	int %line%
%$$
It should be the source code file line number 
where the call to $code TrackNew$$ is located.
The best way to accomplish this is the use the preprocessor symbol
$code __LINE__$$ for this argument.

$head oldptr$$
The argument $icode oldptr$$ has prototype
$codei%
	%Type% *%oldptr%
%$$
This argument is used to identify the type $icode Type$$.

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

$head head newptr$$
The return value $icode newptr$$ has prototype
$codei%
	%Type% *%newptr%
%$$
It points to the newly allocated vector of objects
that were allocated using
$codei%
	new Type[%newlen%]
%$$

$head ncopy$$
The argument $icode ncopy$$ has prototype
$codei%
        size_t %ncopy%
%$$
This specifies the number of elements that are copied from
the old array to the new array.
The value of $icode ncopy$$ 
must be less than or equal $icode newlen$$.

$head TrackNewVec$$
$index TrackNewVec$$
$index NDEBUG$$
If $code NDEBUG$$ is defined, this routine only sets
$codei%
	%newptr% = %Type% new[%newlen%]
%$$
The value of $icode oldptr$$ does not matter 
(except that it is used to identify $icode Type$$).
If $code NDEBUG$$ is not defined, $code TrackNewVec$$ also
tracks the this memory allocation.
In this case, if memory cannot be allocated
$cref ErrorHandler$$ is used to generate a message
stating that there was not sufficient memory.

$subhead Macro$$
$index CPPAD_TRACK_NEW_VEC$$
The preprocessor macro call
$codei%
	CPPAD_TRACK_NEW_VEC(%newlen%, %oldptr%)
%$$
expands to
$codei%
	CppAD::TrackNewVec(__FILE__, __LINE__, %newlen%, %oldptr%)
%$$

$subhead Previously Deprecated$$
$index  CppADTrackNewVec$$
The preprocessor macro $code CppADTrackNewVec$$ is the
same as $code CPPAD_TRACK_NEW_VEC$$ and was previously deprecated.

$head TrackDelVec$$
$index TrackDelVec$$
This routine is used to a vector of objects 
that have been allocated using $code TrackNew$$ or $code TrackExtend$$.
If $code NDEBUG$$ is defined, this routine only frees memory with
$codei%
	delete [] %oldptr%
%$$
If $code NDEBUG$$ is not defined, $code TrackDelete$$ also checks that
$icode oldptr$$ was allocated by $code TrackNew$$ or $code TrackExtend$$
and has not yet been freed.
If this is not the case,
$cref ErrorHandler$$ is used to generate an error message.

$subhead Macro$$
$index CPPAD_TRACK_DEL_VEC$$
The preprocessor macro call
$codei%
	CPPAD_TRACK_DEL_VEC(%oldptr%)
%$$
expands to
$codei%
	CppAD::TrackDelVec(__FILE__, __LINE__, %oldptr%)
%$$

$subhead Previously Deprecated$$
$index  CppADTrackDelVec$$
The preprocessor macro $code CppADTrackDelVec$$ is the
same as $code CPPAD_TRACK_DEL_VEC$$ was previously deprecated.

$head TrackExtend$$
$index TrackExtend$$
This routine is used to 
allocate a new vector (using $code TrackNewVec$$),
copy $icode ncopy$$ elements from the old vector to the new vector.
If $icode ncopy$$ is greater than zero, $icode oldptr$$ 
must have been allocated using $code TrackNewVec$$ or $code TrackExtend$$.
In this case, the vector pointed to by $icode oldptr$$ 
must be have at least $icode ncopy$$ elements
and it will be deleted (using $code TrackDelVec$$).
Note that the dependence of $code TrackExtend$$ on $code NDEBUG$$
is indirectly through the routines $code TrackNewVec$$ and 
$code TrackDelVec$$.

$subhead Macro$$
$index CPPAD_TRACK_EXTEND$$
The preprocessor macro call
$codei%
	CPPAD_TRACK_EXTEND(%newlen%, %ncopy%, %oldptr%)
%$$
expands to
$codei%
	CppAD::TrackExtend(__FILE__, __LINE__, %newlen%, %ncopy%, %oldptr%)
%$$

$subhead Previously Deprecated$$
$index  CppADTrackExtend$$
The preprocessor macro $code CppADTrackExtend$$ is the
same as $code CPPAD_TRACK_EXTEND$$ and was previously deprecated.

$head TrackCount$$
$index TrackCount$$
The return value $icode count$$ has prototype
$codei%
	size_t %count%
%$$
If $code NDEBUG$$ is defined, $icode count$$ will be zero.
Otherwise, it will be
the number of vectors that 
have been allocated
(by $code TrackNewVec$$ or $code TrackExtend$$)
and not yet freed
(by $code TrackDelete$$).

$subhead Macro$$
$index CPPAD_TRACK_COUNT$$
The preprocessor macro call
$codei%
	CPPAD_TRACK_COUNT()
%$$
expands to
$codei%
	CppAD::TrackCount(__FILE__, __LINE__)
%$$

$subhead Previously Deprecated$$
$index  CppADTrackNewVec$$
The preprocessor macro $code CppADTrackCount$$ is the
same as $code CPPAD_TRACK_COUNT$$ and was previously deprecated.

$head Multi-Threading$$
$index multi-threading, TrackCount$$
$index TrackCount, multi-threading$$
$index thread, multi TrackCount$$
These routines cannot be used $cref/in_parallel/ta_in_parallel/$$
execution mode.
Use the $cref thread_alloc$$ routines instead.

$head Example$$
$children%
	test_more/track_new_del.cpp
%$$
The file $cref TrackNewDel.cpp$$
contains an example and test of these functions.
It returns true, if it succeeds, and false otherwise.

$end
------------------------------------------------------------------------------
*/
# include <cppad/local/define.hpp>
# include <cppad/local/cppad_assert.hpp>
# include <cppad/thread_alloc.hpp>
# include <sstream>
# include <string>

# ifndef CPPAD_TRACK_DEBUG
# define CPPAD_TRACK_DEBUG 0
# endif 

// -------------------------------------------------------------------------
# define CPPAD_TRACK_NEW_VEC(newlen, oldptr) \
	CppAD::TrackNewVec(__FILE__, __LINE__, newlen, oldptr)

# define CPPAD_TRACK_DEL_VEC(oldptr) \
	CppAD::TrackDelVec(__FILE__, __LINE__, oldptr)

# define CPPAD_TRACK_EXTEND(newlen, ncopy, oldptr) \
	CppAD::TrackExtend(__FILE__, __LINE__, newlen, ncopy, oldptr)

# define CPPAD_TRACK_COUNT() \
	CppAD::TrackCount(__FILE__, __LINE__)
// -------------------------------------------------------------------------
# define CppADTrackNewVec CPPAD_TRACK_NEW_VEC
# define CppADTrackDelVec CPPAD_TRACK_DEL_VEC
# define CppADTrackExtend CPPAD_TRACK_EXTEND
# define CppADTrackCount  CPPAD_TRACK_COUNT
// -------------------------------------------------------------------------
namespace CppAD { // Begin CppAD namespace

// TrackElement ------------------------------------------------------------
class TrackElement {
	
public:
	std::string   file;   // corresponding file name
	int           line;   // corresponding line number
	void          *ptr;   // value returned by TrackNew
	TrackElement *next;   // next element in linked list

	// default contructor (used to initialize root)
	TrackElement(void)
	: file(""), line(0), ptr(CPPAD_NULL), next(CPPAD_NULL)
	{ }
	
	TrackElement(const char *f, int l, void *p)
	: file(f), line(l), ptr(p), next(CPPAD_NULL)
	{	CPPAD_ASSERT_UNKNOWN( p != CPPAD_NULL);
	}

	// There is only one tracking list and it starts it here
	static TrackElement *Root(void)
	{	CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() );
		static TrackElement root;
		return &root; 
	}

	// Print one tracking element
	static void Print(TrackElement* E)
	{
		CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() );
		Rcout << "E = "         << E;
		Rcout << ", E->next = " << E->next;
		Rcout << ", E->ptr  = " << E->ptr;
		Rcout << ", E->line = " << E->line;
		Rcout << ", E->file = " << E->file;
		Rcout << std::endl;
	}

	// Print the linked list for a thread
	static void Print(void)
	{
		CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() );
		using std::endl;
		TrackElement *E = Root();
		// convert int(size_t) to avoid warning on _MSC_VER systems
		Rcout << "Begin Track List" << endl;
		while( E->next != CPPAD_NULL )
		{	E = E->next;
			Print(E);
		}
		Rcout << "End Track List:" << endl;
		Rcout << endl;
	}
}; 


// TrackError ----------------------------------------------------------------
inline void TrackError(
	const char *routine,
	const char *file,
	int         line,
	const char *msg )
{
	CPPAD_ASSERT_UNKNOWN( ! thread_alloc::in_parallel() );
	std::ostringstream buf;
	buf << routine
	    << ": at line "
	    << line
	    << " in file "
	    << file
	    << std::endl
	    << msg; 
	std::string str = buf.str();
	size_t      n   = str.size();
	size_t i;
	char *message = new char[n + 1];
	for(i = 0; i < n; i++)
		message[i] = str[i];
	message[n] = '\0';
	CPPAD_ASSERT_KNOWN( false , message);
}

// TrackNewVec ---------------------------------------------------------------
# ifdef NDEBUG
template <class Type>
inline Type *TrackNewVec(
	const char *file, int line, size_t len, Type * /* oldptr */ )
{
# if CPPAD_TRACK_DEBUG
	static bool first = true;
	if( first )
	{	Rcout << "NDEBUG is defined for TrackNewVec" << std::endl;
		first = false;
	}
# endif
	return (new Type[len]); 
}

# else

template <class Type>
Type *TrackNewVec(
	const char *file          , 
	int         line          , 
	size_t      len           ,
	Type       * /* oldptr */ )
{
	CPPAD_ASSERT_KNOWN(
		! thread_alloc::in_parallel() ,
		"attempt to use TrackNewVec in parallel execution mode."
	);
	// try to allocate the new memrory
	Type *newptr = CPPAD_NULL;
	try
	{	newptr = new Type[len];
	}
	catch(...)
	{	TrackError("TrackNewVec", file, line, 
			"Cannot allocate sufficient memory"
		);
	}
	// create tracking element
	void *vptr = static_cast<void *>(newptr);
	TrackElement *E = new TrackElement(file, line, vptr);

	// get the root
	TrackElement *root = TrackElement::Root();

	// put this elemenent at the front of linked list
	E->next    = root->next;  
	root->next = E;

# if CPPAD_TRACK_DEBUG
	Rcout << "TrackNewVec: ";
	TrackElement::Print(E);
# endif

	return newptr;
}

# endif

// TrackDelVec --------------------------------------------------------------
# ifdef NDEBUG
template <class Type>
inline void TrackDelVec(const char *file, int line, Type *oldptr)
{
# if CPPAD_TRACK_DEBUG
	static bool first = true;
	if( first )
	{	Rcout << "NDEBUG is defined in TrackDelVec" << std::endl;
		first = false;
	}
# endif
	 delete [] oldptr; 
}

# else

template <class Type>
void TrackDelVec(
	const char *file    ,
	int         line    ,
	Type       *oldptr  )
{
	CPPAD_ASSERT_KNOWN(
		! thread_alloc::in_parallel() ,
		"attempt to use TrackDelVec in parallel execution mode."
	);
	TrackElement        *P;
	TrackElement        *E;

	// search list for pointer
	P          = TrackElement::Root();
	E          = P->next;
	void *vptr = static_cast<void *>(oldptr);
	while(E != CPPAD_NULL && E->ptr != vptr)
	{	P = E;
		E = E->next;
	}

	// check if pointer was not in list
	if( E == CPPAD_NULL || E->ptr != vptr ) TrackError(
		"TrackDelVec", file, line, 
		"Invalid value for the argument oldptr.\n"
		"Possible linking of debug and NDEBUG compliations of CppAD."
	); 

# if CPPAD_TRACK_DEBUG
	Rcout << "TrackDelVec: ";
	TrackElement::Print(E);
# endif

	// remove tracking element from list
	P->next = E->next;

	// delete allocated pointer
	delete [] oldptr;

	// delete tracking element
	delete E;

	return;
}

# endif

// TrackExtend --------------------------------------------------------------
template <class Type>
Type *TrackExtend(
	const char *file    , 
	int         line    , 
	size_t      newlen  , 
	size_t      ncopy   ,
	Type       *oldptr  ) 
{	
	CPPAD_ASSERT_KNOWN(
		! thread_alloc::in_parallel() ,
		"attempt to use TrackExtend in parallel execution mode."
	);

# if CPPAD_TRACK_DEBUG
	Rcout << "TrackExtend: file = " << file;
	Rcout << ", line = " << line;
	Rcout << ", newlen = " << newlen;
	Rcout << ", ncopy = " << ncopy;
	Rcout << ", oldptr = " << oldptr;
	Rcout << std::endl;
# endif
	CPPAD_ASSERT_KNOWN( 
		ncopy <= newlen,
		"TrackExtend: ncopy is greater than newlen."
	);

	// allocate the new memrory
	Type *newptr = TrackNewVec(file, line, newlen, oldptr);

	// copy the data
	size_t i;
	for(i = 0; i < ncopy; i++)
		newptr[i] = oldptr[i];

	// delete the old vector 
	if( ncopy > 0 )
		TrackDelVec(file, line, oldptr);

	return newptr;
}

// TrackCount --------------------------------------------------------------
inline size_t TrackCount(const char *file, int line)
{
	CPPAD_ASSERT_KNOWN(
		! thread_alloc::in_parallel() ,
		"attempt to use TrackCount in parallel execution mode."
	);
	size_t count = 0;
	TrackElement *E = TrackElement::Root();
	while( E->next != CPPAD_NULL ) 
	{	++count;
		E = E->next;
	}
	return count;
}
// ---------------------------------------------------------------------------

} // End CppAD namespace

// preprocessor symbols local to this file
# undef CPPAD_TRACK_DEBUG

# endif
