// BEGIN underlyingMacro
#define UNDERLYING(type)        type, IP_##type
// END underlyingMacro

// generic case
template<class UnderlyingModel, class inputParameters, class DataStructure, class DerivativeMethod>
class Pricing : UnderlyingModel, DataStructure, DerivativeMethod
{
 public:
  Pricing(inputParameters inParam) : UnderlyingModel(inParam), DataStructure(Q)
    {
    }
};

// specific case for a complete tree derivative
template<class UnderlyingModel, class inputParameters, class UnderlyingMethod, class DerivativeMethod>
class Pricing<UnderlyingModel, inputParameters, Tree<Complete, UnderlyingMethod>, DerivativeMethod > : 
  UnderlyingModel, DerivativeMethod 
{
 private:
  Tree<Complete, Empty> *dTree_;
  Tree<Complete, UnderlyingMethod> *sTree_;
  TSpecificFunctor< Pricing<UnderlyingModel, inputParameters, Tree<Complete, UnderlyingMethod>, 
    DerivativeMethod> > *specificFunctor_;
  double rootDval;


// BEGIN pricingTreeComplete
 public:
  Pricing(inputParameters inParam, double K) : UnderlyingModel(inParam) 
    {
      derivativeInit(N);      

      specificFunctor_ = new TSpecificFunctor< Pricing<UnderlyingModel, inputParameters, 
	Tree<Complete, UnderlyingMethod>, DerivativeMethod> >(this, &DerivativeMethod::visitStock); 

      sTree_ = new Tree<Complete, UnderlyingMethod>(Q, S0, N, M, specificFunctor_);
      dTree_ = new Tree<Complete, Empty>(Q, N);
      
      TreeNode<double> *sNode;
      TreeNode<double> *dNode;

      PostOrderIterator<double> *ptrDPostOrderIter = dTree_->GetPostOrderIterator();
      PostOrderIterator<double>& dPostOrderIter  = *ptrDPostOrderIter;

      PostOrderIterator<double> *ptrSPostOrderIter = sTree_->GetPostOrderIterator();
      PostOrderIterator<double>& sPostOrderIter = *ptrSPostOrderIter;

      stack<double> *childStack = new stack<double>;
      
      while(! sPostOrderIter.isDone() && ! dPostOrderIter.isDone())
	{
	  sNode = *sPostOrderIter;
	  dNode = *dPostOrderIter;

	  if(sNode->children.size() == 0)
	    dNode->value = payOff(K, sNode->value);
	  else
	    dNode->value = derivativeValue(childStack, sNode->depth, P, disc, K, sNode->value);

	  childStack->push(dNode->value);

	  ++sPostOrderIter;
	  ++dPostOrderIter;
	}

      rootDval = dNode->value;
    }
// END pricingTreeComplete

  // stock access methods
  PostOrderIterator<double>& getStockPostOrderIterator(void)
  {
    PostOrderIterator<double> *iter = sTree_->GetPostOrderIterator();
    
    iter->Reset();
    
    return *iter;
  }

  BreadthFirstIterator<double>& getStockBreadthFirstIterator(void)
  {
    BreadthFirstIterator<double> *iter = sTree_->GetBreadthFirstIterator();
    
    iter->Reset();
    
    return *iter;
  }

#ifdef GRAPHICS
  void graphStock(char *fname, unsigned int width, unsigned int height)
  {
    sTree_->dump(fname, width, height);
  }
#endif
  
  // derivative access methods
  PostOrderIterator<double>& getDerivativePostOrderIterator(void)
  {
    PostOrderIterator<double> *iter = dTree_->GetPostOrderIterator();
    
    iter->Reset();
    
    return *iter;
  }

  BreadthFirstIterator<double>& getDerivativeBreadthFirstIterator(void)
  {
    PostOrderIterator<double> *iter = dTree_->GetBreadthFirstIterator();
    
    iter->Reset();
    
    return *iter;
  }

#ifdef GRAPHICS
  void graphDerivative(char *fname, unsigned int width, unsigned int height)
  {
    dTree_->dump(fname, width, height);
  }
#endif
  
  double getDerivativeRootValue(void)
  {
    return rootDval;
  }
  
};

// specific case for a memory saving tree derivative
template<class UnderlyingModel, class inputParameters, class UnderlyingMethod, class DerivativeMethod>
class Pricing<UnderlyingModel, inputParameters, Tree<MemorySaving, UnderlyingMethod>, DerivativeMethod > : 
  UnderlyingModel, Tree<MemorySaving, UnderlyingMethod>, DerivativeMethod
{
private:
  double K;
  double rootDval;
  
// BEGIN pricingTreeMemorySaveInit
public:
  Pricing(inputParameters inParam, double inK) : UnderlyingModel(inParam), 
    Tree<MemorySaving, UnderlyingMethod>()
    {
      K = inK;

      sRoot_ = new TreeNode<double>();
      sRoot_->value = S0;

      stack<double> stock;
      stack<double> derivative;

      derivativeInit(N);

      stock.push(S0);
      visitStock(0, 0, stock.top());

      createTree(&stock, &derivative, 0);
      
      rootDval = derivative.top();
      
      return;
    }
// END pricingTreeMemorySaveInit

// BEGIN pricingTreeMemorySaveCreate
  void createTree(stack<double> *stock, stack<double> *derivative, unsigned int depth)
    {
      if(depth == N)
	{
	  derivative->push(payOff(K, stock->top()));

	  stock->pop();
	  return;
	}

      double parentVal = stock->top();

      for(unsigned int c = 0; c < Q; c++)
	{
	  stock->push(underlyingChildValue(parentVal, c, depth, M));
	  visitStock(depth, c, stock->top());
		  
	  createTree(stock, derivative, depth+1);

	  if(c+1 == Q)
	    {
	      derivative->push(derivativeValue(derivative, depth, P, disc, K, stock->top()));
	      stock->pop();
	    }
	}
    }
// END pricingTreeMemorySaveCreate

  double getDerivativeRootValue(void)
  {
    return rootDval;
  }

};

// specific case for a complete matrix derivative
template<class UnderlyingModel, class inputParameters, class UnderlyingMethod, class DerivativeMethod>
class Pricing<UnderlyingModel, inputParameters, Matrix<Complete, UnderlyingMethod>, DerivativeMethod > : 
  UnderlyingModel, DerivativeMethod 
{
 private:
  Matrix<Complete, Empty> *dJag_;
  Matrix<Complete, UnderlyingMethod> *sJag_;
  double rootDval;

// BEGIN pricingMatrixComplete
 public:
  Pricing(inputParameters inParam, double K) : UnderlyingModel(inParam)
  {
    derivativeInit(N);

    rootDval = 0.0;

    if(! recomb)
      {
	fprintf(stderr, "Error: cannot run a non-recombining underlying on a matrix\n");
	return;
      }
    
    if(pathDependent)
      {
	fprintf(stderr, "Error: cannot run a path dependent derivative on a matrix\n");
	return;
      }
    
    sJag_ = new Matrix<Complete, UnderlyingMethod>(Q, S0, N, M);
    dJag_ = new Matrix<Complete, Empty>(Q, N);

    stack<double> *Stk = new stack<double>();

    for(unsigned int colNum = N; ;colNum--)
      {
	for(unsigned int rowNum = 0; rowNum < sJag_->Height(colNum); rowNum++)
	  {
	    if(colNum == N)
	      dJag_->Set(colNum, rowNum, payOff(K, sJag_->Get(N, rowNum)));
	    else
	      {
		for(unsigned int childNum = 0; childNum < Q; childNum++)
		  Stk->push(dJag_->Get(colNum + 1, rowNum + childNum));
	      
		dJag_->Set(colNum, rowNum, derivativeValue(Stk, colNum, P, disc, K, 
							 sJag_->Get(colNum, rowNum)));

	      }
	  }
	
	if(colNum == 0)
	  break;
      }
    
    rootDval = dJag_->Get(0, 0);
  }
// END pricingMatrixComplete

  // stock access methods
  double getStockValue(unsigned int col, unsigned int row)
  {
    return sJag_->Get(col, row);
  }

#ifdef GRAPHICS
  void graphStock(char *fname, unsigned int width, unsigned int height)
  {
    sJag_->dump(fname, width, height);
  }
#endif

  // derivative access methods
  double getDerivativeValue(unsigned int col, unsigned int row)
  {
    return dJag_->Get(col, row);
  }
  
  double getDerivativeRootValue(void)
  {
    return rootDval;
  }

#ifdef GRAPHICS  
  void graphDerivative(char *fname, unsigned int width, unsigned int height)
  {
    dJag_->dump(fname, width, height);
  }
#endif
  
};

// specific case for a memory saving matrix derivative
template<class UnderlyingModel, class inputParameters, class UnderlyingMethod, class DerivativeMethod>
class Pricing<UnderlyingModel, inputParameters, Matrix<MemorySaving, UnderlyingMethod>, 
  DerivativeMethod > : UnderlyingModel, DerivativeMethod 
{
private:
  Matrix<Complete, UnderlyingMethod> *sJag_;
  double rootDval;

// BEGIN pricingMatrixMemorySave  
public:
  Pricing(inputParameters inParam, double K) : UnderlyingModel(inParam)
  {
    derivativeInit(N);
    
    rootDval = 0.0;

    if(! recomb)
      {
	fprintf(stderr, "Error: cannot run a non-recombining underlying on a matrix\n");
	return;
      }
    
    if(pathDependent)
      {
	fprintf(stderr, "Error: cannot run a path dependent derivative on a matrix\n");
	return;
      }
    
    sJag_ = new Matrix<Complete, UnderlyingMethod>(Q, S0, N, M);

    queue<double> *Que[Q];
    
    for(unsigned int i = 0; i < Q; i++)
      Que[i] = new queue<double>();
    
    stack<double> *Stk = new stack<double>();
    
    double dVal;

    for(unsigned int colNum = N; ;colNum--)
      {
	for(unsigned int rowNum = 0; rowNum < sJag_->Height(colNum); rowNum++)
	  {
	    if(colNum == N)
	      dVal = payOff(K, sJag_->Get(N, rowNum));
	    else
	      {
		for(unsigned int childNum = 0; childNum < Q; childNum++)
		  {
		    Stk->push(Que[childNum]->front());
		    Que[childNum]->pop();
		  }

		dVal = derivativeValue(Stk, colNum, P, disc, K, sJag_->Get(colNum, rowNum));
	      }

	    if(rowNum == 0)
		Que[0]->push(dVal);
	    else if(rowNum == sJag_->Height(colNum)-1)
		Que[Q-1]->push(dVal);
	    else if((rowNum == 1) && Q == 3)
	      {
		Que[0]->push(dVal);
		Que[1]->push(dVal);
	      }
	    else if( (rowNum == sJag_->Height(colNum)-2) && Q == 3)
	      {
		Que[Q-1]->push(dVal);
		Que[1]->push(dVal);
	      }
	    else
	      for(unsigned int childNum = 0; childNum < Q; childNum++)
		  Que[childNum]->push(dVal);
	  }
	
	if(colNum == 0)
	  break;
      }

    rootDval = dVal;
  }
// END pricingMatrixMemorySave  

  double getDerivativeRootValue(void)
  {
    return rootDval;
  }
  
};
