text: # ifndef CPPAD_LOCAL_OPTIMIZE_GET_OPT_OP_INFO_HPP
  # define CPPAD_LOCAL_OPTIMIZE_GET_OPT_OP_INFO_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.
  -------------------------------------------------------------------------- */
  /*!
  \file opt_op_info.hpp
  Create operator information tables
  */
  
  # include <cppad/local/optimize/opt_op_info.hpp>
  # include <cppad/local/optimize/match_op.hpp>
  # include <cppad/local/optimize/cexp_info.hpp>
  # include <cppad/local/optimize/usage.hpp>
  
  // BEGIN_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
  namespace CppAD { namespace local { namespace optimize {
  
  /// Is this an addition or subtraction operator
  inline bool add_or_subtract(OpCode op)
  {	bool result;
  	switch(op)
  	{
  		case AddpvOp:
  		case AddvvOp:
  		case SubpvOp:
  		case SubvpOp:
  		case SubvvOp:
  		result = true;
  		break;
  
  		default:
  		result = false;
  		break;
  	}
  	return result;
  }
  
  
  /*!
  Increarse argument usage and propagate cexp_set from result to argument.
  
  \param play
  is the player for the old operation sequence.
  
  \param sum_result
  is result an addition or subtraction operator (passed for speed so
  do not need to call add_or_subtract for result).
  
  \param i_result
  is the operator index for the result operator.
  
  \param i_arg
  is the operator index for the argument to the result operator.
  
  \param opt_op_info
  structure that holds the information for each of the operators.
  The output value of opt_op_info[i_arg].usage is increased; to be specific,
  If sum_result is true and the input value of opt_op_info[i_arg].usage
  is no_usage, its output value is csum_usage.
  Otherwise, the output value of opt_op_info[i_arg].usage is yes_usage.
  
  \param cexp_set
  This is a vector of sets with one set for each operator. We denote
  the i-th set by set[i].
  
  \li
  In the special case where cexp_set.n_set() is zero,
  cexp_set is not changed.
  
  \li
  If cexp_set.n_set() != 0 and opt_op_info[i_arg].usage == no_usage,
  the input value of set[i_arg] must be empty.
  In this case the output value if set[i_arg] is equal to set[i_result]
  (which may also be empty).
  
  \li
  If cexp_set.n_set() != 0 and opt_op_info[i_arg].usage != no_usage,
  the output value of set[i_arg] is the intersection of
  its input value and set[i_result].
  */
  template <class Base>
  inline void usage_cexp_result2arg(
  	const player<Base>*         play           ,
  	bool                        sum_result     ,
  	size_t                      i_result       ,
  	size_t                      i_arg          ,
  	vector<struct_opt_op_info>& opt_op_info    ,
  	sparse_list&                cexp_set       )
  {
  	// cexp_set
  	if( cexp_set.n_set() > 0 )
  	{	if( opt_op_info[i_arg].usage == no_usage )
  		{	// set[i_arg] = set[i_result]
  			cexp_set.assignment(i_arg, i_result, cexp_set);
  		}
  		else
  		{	// set[i_arg] = set[i_arg] intersect set[i_result]
  			cexp_set.binary_intersection(i_arg, i_arg, i_result, cexp_set);
  		}
  	}
  	// usage
  	bool csum = sum_result && opt_op_info[i_arg].usage == no_usage;
  	if( csum )
  	{	OpCode op_a = play->GetOp(i_arg);
  		csum = add_or_subtract( op_a );
  	}
  	if( csum )
  		opt_op_info[i_arg].usage = csum_usage;
  	else
  		opt_op_info[i_arg].usage = yes_usage;
  	//
  	return;
  }
  
  /*!
  Get variable to operator map and operator basic operator information
  
  \tparam Base
  base type for the operator; i.e., this operation was recorded
  using AD< \a Base > and computations by this routine are done using type
  \a Base.
  
  \param conditional_skip
  If conditional_skip this is true, the conditional expression information
  cexp_info will be calculated.
  This may be time intensive and may not have much benefit in the optimized
  recording.
  
  \param compare_op
  if this is true, arguments are considered used if they appear in compare
  operators. This is a side effect because compare operators have boolean
  results (and the result is not in the tape; i.e. NumRes(op) is zero
  for these operators. (This is an example of a side effect.)
  
  \param print_for_op
  if this is true, arguments are considered used if they appear in
  print forward operators; i.e., PriOp.
  This is also a side effect; i.e. NumRes(PriOp) is zero.
  
  \param play
  This is the old operation sequence.
  
  \param dep_taddr
  is a vector of variable indices for the dependent variables.
  
  \param cexp_info
  The input size of this vector must be zero.
  If conditional_skip is false, cexp_info is not changed.
  Otherwise,
  upon return cexp_info has size equal to the number of conditional expressions
  in the operation sequence; i.e., the number of CExpOp operators.
  The value cexp_info[j] is the information corresponding to the j-th
  conditional expression in the operation sequence.
  This vector is in the same order as the operation sequence; i.e.
  if j1 > j2, cexp_info[j1].i_op > cexp_info[j2].i_op.
  Note that skip_op_true and skip_op_false could be part of this structure,
  but then we would allocate and deallocate two vectors for each conditonal
  expression in the operation sequence.
  
  \param skip_op_true
  This vector of sets is empty on input.
  Upon return, the j-th set is the operators that are not used when
  comparison result for cexp_info[j] is true.
  Note that UsrapOp, UsravOp, UsrrpOp, and UsrrvOp, are not in this
  set and should be skipped when the corresponding UserOp are skipped.
  
  \param skip_op_false
  This vector of sets is empty on input.
  Upon return, the j-th set is the operators that are not used when
  comparison result for cexp_info[j] is false.
  Note that UsrapOp, UsravOp, UsrrpOp, and UsrrvOp, are not in this
  set and should be skipped when the corresponding UserOp are skipped.
  
  \param vecad_used
  The input size of this vector must be zero.
  Upon retun it has size equal to the number of VecAD vectors
  in the operations sequences; i.e., play->num_vecad_vec_rec().
  The VecAD vectors are indexed in the order that thier indices apprear
  in the one large play->GetVecInd that holds all the VecAD vectors.
  
  \param opt_op_info
  The input size of this vector must be zero.
  Upon return it has size equal to the number of operators
  in the operation sequence; i.e., num_op = play->nun_var_rec().
  The value opt_op_info[i]
  have been set to the values corresponding to the i-th operator
  in the operation sequence.
  */
  
  template <class Base>
  void get_opt_op_info(
  	bool                          conditional_skip    ,
  	bool                          compare_op          ,
  	bool                          print_for_op        ,
  	const player<Base>*           play                ,
  	const vector<size_t>&         dep_taddr           ,
  	vector<struct_cexp_info>&     cexp_info           ,
  	sparse_list&                  skip_op_true        ,
  	sparse_list&                  skip_op_false       ,
  	vector<bool>&                 vecad_used          ,
  	vector<struct_opt_op_info>&   opt_op_info         )
  {
  	CPPAD_ASSERT_UNKNOWN( cexp_info.size() == 0 );
  	CPPAD_ASSERT_UNKNOWN( vecad_used.size() == 0 );
  	CPPAD_ASSERT_UNKNOWN( opt_op_info.size() == 0 );
  
  	// number of operators in the tape
  	const size_t num_op = play->num_op_rec();
  	opt_op_info.resize( num_op );
  	//
  	// initialize mapping from variable index to operator index
  	CPPAD_ASSERT_UNKNOWN(
  		std::numeric_limits<addr_t>::max() >= num_op
  	);
  	//
  	// information set by forward_user
  	size_t user_old=0, user_m=0, user_n=0, user_i=0, user_j=0;
  	enum_user_state user_state;
  	//
  	// ----------------------------------------------------------------------
  	// Forward pass to determine num_cexp_op
  	// ----------------------------------------------------------------------
  	OpCode        op;     // operator
  	const addr_t* arg;    // arguments
  	size_t        i_op;   // operator index
  	size_t        i_var;  // variable index of first result
  	i_op = 0;
  	play->get_op_info(i_op, op, arg, i_var);
  	CPPAD_ASSERT_UNKNOWN( op              == BeginOp );
  	CPPAD_ASSERT_UNKNOWN( NumRes(BeginOp) == 1 );
  	CPPAD_ASSERT_UNKNOWN( i_op            == 0 );
  	CPPAD_ASSERT_UNKNOWN( i_var           == 0 );
  	//
  	size_t num_cexp_op = 0;
  	user_state = start_user;
  	while(op != EndOp)
  	{	// next operator
  		play->get_op_info(++i_op, op, arg, i_var);
  		//
  		if( op == CExpOp )
  		{	// count the number of conditional expressions.
  			++num_cexp_op;
  		}
  	}
  	// vector that maps conditional expression index to operator index
  	vector<size_t> cexp2op( num_cexp_op );
  	// ----------------------------------------------------------------------
  	// Reverse pass to compute usage and cexp_set for each operator
  	// ----------------------------------------------------------------------
  	// work space used by user defined atomic functions
  	typedef std::set<size_t> size_set;
  	vector<Base>     user_x;       // parameters in x as integers
  	vector<size_t>   user_ix;      // variables indices for argument vector
  	vector<size_set> user_r_set;   // set sparsity pattern for result
  	vector<size_set> user_s_set;   // set sparisty pattern for argument
  	vector<bool>     user_r_bool;  // bool sparsity pattern for result
  	vector<bool>     user_s_bool;  // bool sparisty pattern for argument
  	vectorBool       user_r_pack;  // pack sparsity pattern for result
  	vectorBool       user_s_pack;  // pack sparisty pattern for argument
  	//
  	atomic_base<Base>* user_atom = CPPAD_NULL; // current user atomic function
  	bool               user_pack = false;      // sparsity pattern type is pack
  	bool               user_bool = false;      // sparsity pattern type is bool
  	bool               user_set  = false;      // sparsity pattern type is set
  	// -----------------------------------------------------------------------
  	// vecad information
  	size_t num_vecad      = play->num_vecad_vec_rec();
  	size_t num_vecad_ind  = play->num_vec_ind_rec();
  	//
  	vecad_used.resize(num_vecad);
  	for(size_t i = 0; i < num_vecad; i++)
  		vecad_used[i] = false;
  	//
  	vector<size_t> arg2vecad(num_vecad_ind);
  	for(size_t i = 0; i < num_vecad_ind; i++)
  		arg2vecad[i] = num_vecad; // invalid value
  	size_t arg_0 = 1; // value of arg[0] for theh first vecad
  	for(size_t i = 0; i < num_vecad; i++)
  	{
  		// mapping from arg[0] value to index for this vecad object.
  		arg2vecad[arg_0] = i;
  		//
  		// length of this vecad object
  		size_t length = play->GetVecInd(arg_0 - 1);
  		//
  		// set to proper index in GetVecInd for next VecAD arg[0] value
  		arg_0        += length + 1;
  	}
  	CPPAD_ASSERT_UNKNOWN( arg_0 == num_vecad_ind + 1 );
  	// -----------------------------------------------------------------------
  	// parameter information (used by atomic function calls)
  	size_t num_par = play->num_par_rec();
  	const Base* parameter = CPPAD_NULL;
  	if( num_par > 0 )
  		parameter = play->GetPar();
  	// -----------------------------------------------------------------------
  	// Set of conditional expressions comparisons that usage of each
  	/// operator depends on. The operator can be skipped if any of the
  	// comparisons results in the set holds. A set for operator i_op is
  	// not defined and left empty when opt_op_info[i_op].usage = no_usage.
  	/// It is also left empty for the result of any VecAD operations.
  	sparse_list cexp_set;
  	//
  	// number of sets
  	size_t num_set = 0;
  	if( conditional_skip && num_cexp_op > 0)
  		num_set = num_op;
  	//
  	// conditional expression index   = element / 2
  	// conditional expression compare = bool ( element % 2)
  	size_t end_set = 2 * num_cexp_op;
  	//
  	if( num_set > 0 )
  		cexp_set.resize(num_set, end_set);
  	// -----------------------------------------------------------------------
  	//
  	// initialize operator usage
  	for(size_t i = 0; i < num_op; i++)
  		opt_op_info[i].usage = no_usage;
  	for(size_t i = 0; i < dep_taddr.size(); i++)
  	{	i_op                = play->var2op(dep_taddr[i]);
  		opt_op_info[i_op].usage = yes_usage;    // dependent variables
  	}
  	//
  	// Initialize reverse pass
  	size_t last_user_i_op = 0;
  	size_t cexp_index     = num_cexp_op;
  	user_state            = end_user;
  	i_op = num_op;
  	while(i_op != 0 )
  	{	--i_op;
  		//
  		// this operator information
  		play->get_op_info(i_op, op, arg, i_var);
  		//
  		// Is the result of this operation used.
  		// (This only makes sense when NumRes(op) > 0.)
  		enum_usage use_result = opt_op_info[i_op].usage;
  		//
  		bool sum_op = false;
  		switch( op )
  		{
  			// =============================================================
  			// normal operators
  			// =============================================================
  
  			// Only one variable with index arg[0]
  			case SubvpOp:
  			sum_op = true;
  			//
  			case AbsOp:
  			case AcosOp:
  			case AcoshOp:
  			case AsinOp:
  			case AsinhOp:
  			case AtanOp:
  			case AtanhOp:
  			case CosOp:
  			case CoshOp:
  			case DivvpOp:
  			case ErfOp:
  			case ExpOp:
  			case Expm1Op:
  			case LogOp:
  			case Log1pOp:
  			case PowvpOp:
  			case SignOp:
  			case SinOp:
  			case SinhOp:
  			case SqrtOp:
  			case TanOp:
  			case TanhOp:
  			case ZmulvpOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	size_t j_op = play->var2op(arg[0]);
  				usage_cexp_result2arg(
  					play, sum_op, i_op, j_op, opt_op_info, cexp_set
  				);
  			}
  			break; // --------------------------------------------
  
  			// Only one variable with index arg[1]
  			case AddpvOp:
  			case SubpvOp:
  			sum_op = true;
  			//
  			case DisOp:
  			case DivpvOp:
  			case MulpvOp:
  			case PowpvOp:
  			case ZmulpvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	size_t j_op = play->var2op(arg[1]);
  				usage_cexp_result2arg(
  					play, sum_op, i_op, j_op, opt_op_info, cexp_set
  				);
  			}
  			break; // --------------------------------------------
  
  			// arg[0] and arg[1] are the only variables
  			case AddvvOp:
  			case SubvvOp:
  			sum_op = true;
  			//
  			case DivvvOp:
  			case MulvvOp:
  			case PowvvOp:
  			case ZmulvvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	for(size_t i = 0; i < 2; i++)
  				{	size_t j_op = play->var2op(arg[i]);
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  			}
  			break; // --------------------------------------------
  
  			// Conditional expression operators
  			// arg[2], arg[3], arg[4], arg[5] are parameters or variables
  			case CExpOp:
  			--cexp_index;
  			cexp2op[ cexp_index ] = i_op;
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	CPPAD_ASSERT_UNKNOWN( NumArg(CExpOp) == 6 );
  				// propgate from result to left argument
  				if( arg[1] & 1 )
  				{	size_t j_op = play->var2op(arg[2]);
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  				// propgate from result to right argument
  				if( arg[1] & 2 )
  				{	size_t j_op = play->var2op(arg[3]);
  					usage_cexp_result2arg(
  							play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  				// are if_true and if_false cases the same variable
  				bool same_variable = bool(arg[1] & 4) && bool(arg[1] & 8);
  				same_variable     &= arg[4] == arg[5];
  				//
  				// if_true
  				if( arg[1] & 4 )
  				{	size_t j_op = play->var2op(arg[4]);
  					bool can_skip = conditional_skip & (! same_variable);
  					can_skip     &= opt_op_info[j_op].usage == no_usage;
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  					if( can_skip )
  					{	// j_op corresponds to the value used when the
  						// comparison result is true. It can be skipped when
  						// the comparison is false (0).
  						size_t element = 2 * cexp_index + 0;
  						cexp_set.add_element(j_op, element);
  						//
  						opt_op_info[j_op].usage = yes_usage;
  					}
  				}
  				//
  				// if_false
  				if( arg[1] & 8 )
  				{	size_t j_op = play->var2op(arg[5]);
  					bool can_skip = conditional_skip & (! same_variable);
  					can_skip     &= opt_op_info[j_op].usage == no_usage;
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  					if( can_skip )
  					{	// j_op corresponds to the value used when the
  						// comparison result is false. It can be skipped when
  						// the comparison is true (0).
  						size_t element = 2 * cexp_index + 1;
  						cexp_set.add_element(j_op, element);
  						//
  						opt_op_info[j_op].usage = yes_usage;
  					}
  				}
  			}
  			break;  // --------------------------------------------
  
  			// Operations that are never used
  			// (new CSkip options are generated if conditional_skip is true)
  			case CSkipOp:
  			case ParOp:
  			break;
  
  			// Operators that are always used
  			case InvOp:
  			case BeginOp:
  			case EndOp:
  			opt_op_info[i_op].usage = yes_usage;
  			break;  // -----------------------------------------------
  
  			// The print forward operator
  			case PriOp:
  			CPPAD_ASSERT_NARG_NRES(op, 5, 0);
  			if( print_for_op )
  			{	opt_op_info[i_op].usage = yes_usage;
  				if( arg[0] & 1 )
  				{	// arg[1] is a variable
  					size_t j_op = play->var2op(arg[1]);
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  				if( arg[0] & 2 )
  				{	// arg[3] is a variable
  					size_t j_op = play->var2op(arg[3]);
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  			}
  			break; // -----------------------------------------------------
  
  			// =============================================================
  			// Comparison operators
  			// =============================================================
  
  			// Compare operators where arg[1] is only variable
  			case LepvOp:
  			case LtpvOp:
  			case EqpvOp:
  			case NepvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
  			if( compare_op )
  			{	opt_op_info[i_op].usage = yes_usage;
  				//
  				size_t j_op = play->var2op(arg[1]);
  				usage_cexp_result2arg(
  					play, sum_op, i_op, j_op, opt_op_info, cexp_set
  				);
  			}
  			break; // ----------------------------------------------
  
  			// Compare operators where arg[0] is only variable
  			case LevpOp:
  			case LtvpOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
  			if( compare_op )
  			{	opt_op_info[i_op].usage = yes_usage;
  				//
  				size_t j_op = play->var2op(arg[0]);
  				usage_cexp_result2arg(
  					play, sum_op, i_op, j_op, opt_op_info, cexp_set
  				);
  			}
  			break; // ----------------------------------------------
  
  			// Compare operators where arg[0] and arg[1] are variables
  			case LevvOp:
  			case LtvvOp:
  			case EqvvOp:
  			case NevvOp:
  			if( compare_op )
  			{CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );}
  			if( compare_op )
  			{	opt_op_info[i_op].usage = yes_usage;
  				//
  				for(size_t i = 0; i < 2; i++)
  				{	size_t j_op = play->var2op(arg[i]);
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  			}
  			break; // ----------------------------------------------
  
  			// =============================================================
  			// VecAD operators
  			// =============================================================
  
  			// load operator using a parameter index
  			case LdpOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	size_t i_vec = arg2vecad[ arg[0] ];
  				vecad_used[i_vec] = true;
  			}
  			break; // --------------------------------------------
  
  			// load operator using a variable index
  			case LdvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) > 0 );
  			if( use_result != no_usage )
  			{	size_t i_vec = arg2vecad[ arg[0] ];
  				vecad_used[i_vec] = true;
  				//
  				size_t j_op = play->var2op(arg[1]);
  				opt_op_info[j_op].usage = yes_usage;
  			}
  			break; // --------------------------------------------
  
  			// Store a variable using a parameter index
  			case StpvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
  			if( vecad_used[ arg2vecad[ arg[0] ] ] )
  			{	opt_op_info[i_op].usage = yes_usage;
  				//
  				size_t j_op = play->var2op(arg[2]);
  				opt_op_info[j_op].usage = yes_usage;
  			}
  			break; // --------------------------------------------
  
  			// Store a variable using a variable index
  			case StvvOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) == 0 );
  			if( vecad_used[ arg2vecad[ arg[0] ] ] )
  			{	opt_op_info[i_op].usage = yes_usage;
  				//
  				size_t j_op = play->var2op(arg[1]);
  				opt_op_info[j_op].usage = yes_usage;
  				size_t k_op = play->var2op(arg[2]);
  				opt_op_info[k_op].usage = yes_usage;
  			}
  			break; // -----------------------------------------------------
  
  			// =============================================================
  			// cumulative summation operator
  			// ============================================================
  			case CSumOp:
  			CPPAD_ASSERT_UNKNOWN( NumRes(op) == 1 );
  			{
  				size_t num_add = size_t( arg[0] );
  				size_t num_sub = size_t( arg[1] );
  				for(size_t i = 0; i < num_add + num_sub; i++)
  				{	size_t j_op = play->var2op( arg[3 + i] );
  					usage_cexp_result2arg(
  						play, sum_op, i_op, j_op, opt_op_info, cexp_set
  					);
  				}
  			}
  			// =============================================================
  			// user defined atomic operators
  			// ============================================================
  
  			case UserOp:
  			// start or end atomic operation sequence
  			if( user_state == end_user )
  			{	// revese_user using opt_op_info instead of play
  				size_t user_index = arg[0];
  				user_old          = arg[1];
  				user_n            = arg[2];
  				user_m            = arg[3];
  				user_j            = user_n;
  				user_i            = user_m;
  				user_state        = ret_user;
  				user_atom  = atomic_base<Base>::class_object(user_index);
  				// -------------------------------------------------------
  				last_user_i_op = i_op;
  				CPPAD_ASSERT_UNKNOWN( i_op > user_n + user_m + 1 );
  				CPPAD_ASSERT_UNKNOWN(
  					opt_op_info[last_user_i_op].usage == no_usage
  				);
  # ifndef NDEBUG
  				if( cexp_set.n_set() > 0 )
  				{	sparse_list_const_iterator itr(cexp_set, last_user_i_op);
  					CPPAD_ASSERT_UNKNOWN( *itr == cexp_set.end() );
  				}
  # endif
  				//
  				user_x.resize(  user_n );
  				user_ix.resize( user_n );
  				//
  				user_pack  = user_atom->sparsity() ==
  							atomic_base<Base>::pack_sparsity_enum;
  				user_bool  = user_atom->sparsity() ==
  							atomic_base<Base>::bool_sparsity_enum;
  				user_set   = user_atom->sparsity() ==
  							atomic_base<Base>::set_sparsity_enum;
  				CPPAD_ASSERT_UNKNOWN( user_pack || user_bool || user_set );
  				//
  				// Note that q is one for this call the sparsity calculation
  				if( user_pack )
  				{	user_r_pack.resize( user_m );
  					user_s_pack.resize( user_n );
  					for(size_t i = 0; i < user_m; i++)
  						user_r_pack[ i ] = false;
  				}
  				if( user_bool )
  				{	user_r_bool.resize( user_m );
  					user_s_bool.resize( user_n );
  					for(size_t i = 0; i < user_m; i++)
  						user_r_bool[ i ] = false;
  				}
  				if( user_set )
  				{	user_s_set.resize(user_n);
  					user_r_set.resize(user_m);
  					for(size_t i = 0; i < user_m; i++)
  						user_r_set[i].clear();
  				}
  			}
  			else
  			{	// reverse_user using opt_op_info instead of play
  				CPPAD_ASSERT_UNKNOWN( user_state == start_user );
  				CPPAD_ASSERT_UNKNOWN( user_n == size_t(arg[2]) );
  				CPPAD_ASSERT_UNKNOWN( user_m == size_t(arg[3]) );
  				CPPAD_ASSERT_UNKNOWN( user_j == 0 );
  				CPPAD_ASSERT_UNKNOWN( user_i == 0 );
  				user_state = end_user;
  				// -------------------------------------------------------
  				CPPAD_ASSERT_UNKNOWN(
  					i_op + user_n + user_m + 1 == last_user_i_op
  				);
  				// call users function for this operation
  				user_atom->set_old(user_old);
  				bool user_ok  = false;
  				size_t user_q = 1; // as if sum of dependent variables
  				if( user_pack )
  				{	user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_pack, user_s_pack, user_x
  					);
  					if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_pack, user_s_pack
  					);
  				}
  				if( user_bool )
  				{	user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_bool, user_s_bool, user_x
  					);
  					if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_bool, user_s_bool
  					);
  				}
  				if( user_set )
  				{	user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_set, user_s_set, user_x
  					);
  					if( ! user_ok ) user_ok = user_atom->rev_sparse_jac(
  						user_q, user_r_set, user_s_set
  					);
  				}
  				if( ! user_ok )
  				{	std::string s =
  						"Optimizing an ADFun object"
  						" that contains the atomic function\n\t";
  					s += user_atom->afun_name();
  					s += "\nCurrent atomic_sparsity is set to ";
  					//
  					if( user_set )
  						s += "set_sparsity_enum.\n";
  					if( user_bool )
  						s += "bool_sparsity_enum.\n";
  					if( user_pack )
  						s += "pack_sparsity_enum.\n";
  					//
  					s += "This version of rev_sparse_jac returned false";
  					CPPAD_ASSERT_KNOWN(false, s.c_str() );
  				}
  
  				if( opt_op_info[last_user_i_op].usage != no_usage )
  				for(size_t j = 0; j < user_n; j++)
  				if( user_ix[j] > 0 )
  				{	// This user argument is a variable
  					bool use_arg_j = false;
  					if( user_set )
  					{	if( ! user_s_set[j].empty() )
  							use_arg_j = true;
  					}
  					if( user_bool )
  					{	if( user_s_bool[j] )
  							use_arg_j = true;
  					}
  					if( user_pack )
  					{	if( user_s_pack[j] )
  							use_arg_j = true;
  					}
  					if( use_arg_j )
  					{	size_t j_op = play->var2op(user_ix[j]);
  						usage_cexp_result2arg(play,
  							sum_op, last_user_i_op, j_op, opt_op_info, cexp_set
  						);
  					}
  				}
  				// copy set infomation from last to first
  				if( cexp_set.n_set() > 0 )
  					cexp_set.assignment(i_op, last_user_i_op, cexp_set);
  				// copy user information from last to all the user operators
  				// for this call
  				for(size_t j = 0; j < user_n + user_m + 1; ++j)
  					opt_op_info[i_op + j].usage = opt_op_info[last_user_i_op].usage;
  			}
  			break; // -------------------------------------------------------
  
  			case UsrapOp:
  			// parameter argument in an atomic operation sequence
  			CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
  			//
  			// reverse_user using opt_op_info instead of play
  			CPPAD_ASSERT_NARG_NRES(op, 1, 0);
  			CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j < user_n );
  			--user_j;
  			if( user_j == 0 )
  				user_state = start_user;
  			// -------------------------------------------------------------
  			user_ix[user_j] = 0;
  			//
  			// parameter arguments
  			user_x[user_j] = parameter[arg[0]];
  			//
  			break;
  
  			case UsravOp:
  			// variable argument in an atomic operation sequence
  			CPPAD_ASSERT_UNKNOWN( 0 < arg[0] );
  			//
  			// reverse_user using opt_op_info instead of play
  			CPPAD_ASSERT_NARG_NRES(op, 1, 0);
  			CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j <= user_n );
  			--user_j;
  			if( user_j == 0 )
  				user_state = start_user;
  			// -------------------------------------------------------------
  			user_ix[user_j] = arg[0];
  			//
  			// variable arguments as parameters
  			user_x[user_j] = CppAD::numeric_limits<Base>::quiet_NaN();
  			//
  			break;
  
  			case UsrrvOp:
  			// variable result in an atomic operation sequence
  			//
  			// reverse_user using opt_op_info instead of play
  			CPPAD_ASSERT_NARG_NRES(op, 0, 1);
  			CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i <= user_m );
  			--user_i;
  			if( user_i == 0 )
  				user_state = arg_user;
  			// -------------------------------------------------------------
  			if( use_result )
  			{	if( user_set )
  					user_r_set[user_i].insert(0);
  				if( user_bool )
  					user_r_bool[user_i] = true;
  				if( user_pack )
  					user_r_pack[user_i] = true;
  				//
  				usage_cexp_result2arg(
  					play, sum_op, i_op, last_user_i_op, opt_op_info, cexp_set
  				);
  			}
  			break; // --------------------------------------------------------
  
  			case UsrrpOp:
  			CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
  			//
  			// reverse_user using opt_op_info instead of play
  			CPPAD_ASSERT_NARG_NRES(op, 0, 1);
  			CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i < user_m );
  			--user_i;
  			if( user_i == 0 )
  				user_state = arg_user;
  			break;
  			// ============================================================
  
  			// all cases should be handled above
  			default:
  			CPPAD_ASSERT_UNKNOWN(0);
  		}
  	}
  	// ----------------------------------------------------------------------
  	// compute previous in opt_op_info
  	// ----------------------------------------------------------------------
  	sparse_list  hash_table_op;
  	hash_table_op.resize(CPPAD_HASH_TABLE_SIZE, num_op);
  	//
  	user_state = start_user;
  	for(i_op = 0; i_op < num_op; ++i_op)
  	{	opt_op_info[i_op].previous = 0;
  
  		if( opt_op_info[i_op].usage == yes_usage )
  		switch( play->GetOp(i_op) )
  		{
  			case NumberOp:
  			CPPAD_ASSERT_UNKNOWN(false);
  			break;
  
  			case BeginOp:
  			case CExpOp:
  			case CSkipOp:
  			case CSumOp:
  			case EndOp:
  			case InvOp:
  			case LdpOp:
  			case LdvOp:
  			case ParOp:
  			case PriOp:
  			case StppOp:
  			case StpvOp:
  			case StvpOp:
  			case StvvOp:
  			case UserOp:
  			case UsrapOp:
  			case UsravOp:
  			case UsrrpOp:
  			case UsrrvOp:
  			// these operators never match pevious operators
  			break;
  
  			case AbsOp:
  			case AcosOp:
  			case AcoshOp:
  			case AddpvOp:
  			case AddvvOp:
  			case AsinOp:
  			case AsinhOp:
  			case AtanOp:
  			case AtanhOp:
  			case CosOp:
  			case CoshOp:
  			case DisOp:
  			case DivpvOp:
  			case DivvpOp:
  			case DivvvOp:
  			case EqpvOp:
  			case EqvvOp:
  			case ErfOp:
  			case ExpOp:
  			case Expm1Op:
  			case LepvOp:
  			case LevpOp:
  			case LevvOp:
  			case LogOp:
  			case Log1pOp:
  			case LtpvOp:
  			case LtvpOp:
  			case LtvvOp:
  			case MulpvOp:
  			case MulvvOp:
  			case NepvOp:
  			case NevvOp:
  			case PowpvOp:
  			case PowvpOp:
  			case PowvvOp:
  			case SignOp:
  			case SinOp:
  			case SinhOp:
  			case SqrtOp:
  			case SubpvOp:
  			case SubvpOp:
  			case SubvvOp:
  			case TanOp:
  			case TanhOp:
  			case ZmulpvOp:
  			case ZmulvpOp:
  			case ZmulvvOp:
  			// check for a previous match
  			match_op(play, opt_op_info, i_op, hash_table_op );
  			if( opt_op_info[i_op].previous != 0 )
  			{	// like a unary operator that assigns i_op equal to previous.
  				size_t previous = opt_op_info[i_op].previous;
  				bool sum_op = false;
  				CPPAD_ASSERT_UNKNOWN( previous < i_op );
  				usage_cexp_result2arg(
  					play, sum_op, i_op, previous, opt_op_info, cexp_set
  				);
  			}
  			break;
  		}
  	}
  	// ----------------------------------------------------------------------
  	// compute cexp_info
  	// ----------------------------------------------------------------------
  	if( cexp_set.n_set() == 0 )
  		return;
  	//
  	// initialize information for each conditional expression
  	cexp_info.resize(num_cexp_op);
  	skip_op_true.resize(num_cexp_op, num_op);
  	skip_op_false.resize(num_cexp_op, num_op);
  	//
  	for(size_t i = 0; i < num_cexp_op; i++)
  	{	CPPAD_ASSERT_UNKNOWN(
  			opt_op_info[i].previous == 0 || opt_op_info[i].usage == yes_usage
  		);
  		i_op            = cexp2op[i];
  		play->get_op_info(i_op, op, arg, i_var);
  		CPPAD_ASSERT_UNKNOWN( op == CExpOp );
  		//
  		struct_cexp_info info;
  		info.i_op       = i_op;
  		info.cop        = CompareOp( arg[0] );
  		info.flag       = arg[1];
  		info.left       = arg[2];
  		info.right      = arg[3];
  		//
  		// max_left_right
  		size_t index    = 0;
  		if( arg[1] & 1 )
  			index = std::max(index, info.left);
  		if( arg[1] & 2 )
  			index = std::max(index, info.right);
  		CPPAD_ASSERT_UNKNOWN( index > 0 );
  		info.max_left_right = index;
  		//
  		cexp_info[i] = info;
  	};
  	// Determine which operators can be conditionally skipped
  	i_op = 0;
  	while(i_op < num_op)
  	{	size_t j_op = i_op;
  		bool keep = opt_op_info[i_op].usage != no_usage;
  		keep     &= opt_op_info[i_op].usage != csum_usage;
  		keep     &= opt_op_info[i_op].previous == 0;
  		if( keep )
  		{	sparse_list_const_iterator itr(cexp_set, i_op);
  			if( *itr != cexp_set.end() )
  			{	if( play->GetOp(i_op) == UserOp )
  				{	// i_op is the first operations in this user atomic call.
  					// Find the last operation in this call.
  					++j_op;
  					while( play->GetOp(j_op) != UserOp )
  					{	switch( play->GetOp(j_op) )
  						{	case UsrapOp:
  							case UsravOp:
  							case UsrrpOp:
  							case UsrrvOp:
  							break;
  
  							default:
  							CPPAD_ASSERT_UNKNOWN(false);
  						}
  						++j_op;
  					}
  				}
  			}
  			while( *itr != cexp_set.end() )
  			{	size_t element = *itr;
  				size_t index   = element / 2;
  				bool   compare = bool( element % 2 );
  				if( compare == false )
  				{	// cexp_info[index].skip_op_false.push_back(i_op);
  					skip_op_false.add_element(index, i_op);
  					if( j_op != i_op )
  					{	// cexp_info[index].skip_op_false.push_back(j_op);
  						skip_op_false.add_element(index, j_op);
  					}
  				}
  				else
  				{	// cexp_info[index].skip_op_true.push_back(i_op);
  					skip_op_true.add_element(index, i_op);
  					if( j_op != i_op )
  					{	// cexp_info[index].skip_op_true.push_back(j_op);
  						skip_op_true.add_element(index, j_op);
  					}
  				}
  				++itr;
  			}
  		}
  		CPPAD_ASSERT_UNKNOWN( i_op <= j_op );
  		i_op += (1 + j_op) - i_op;
  	}
  	return;
  }
  
  } } } // END_CPPAD_LOCAL_OPTIMIZE_NAMESPACE
  
  # endif
  