#include <config.h>
#include <sampler/ConjugateBeta.h>
#include <sampler/ConjugateSampler.h>
#include <graph/LogicalNode.h>
#include <graph/StochasticNode.h>
#include <graph/MixtureNode.h>

#include <set>
#include <stdexcept>
#include <vector>
#include <cmath>
#include <algorithm>

#include <Rmath.h>

using std::vector;
using std::set;
using std::sqrt;
using std::invalid_argument;
using std::logic_error;
using std::max;
using std::min;

ConjugateBeta::ConjugateBeta(StochasticNode *snode, Graph const &graph)
  : ConjugateSampler(snode, graph) 
{
  if (!canSample(snode, graph)) {
    throw invalid_argument("Can't construct ConjugateBeta sampler");
  }
}

ConjugateBeta::~ConjugateBeta()
{

}

bool ConjugateBeta::canSample(StochasticNode *snode,
			      Graph const &graph)
{
  switch(getDist(snode)) {
  case BETA:
    break;
  case UNIF:
    // dunif(0,1) is equivalent to dbeta(1,1) 
    if(!(*snode->parameters()[0]->value() == 0 &&
	 *snode->parameters()[1]->value() == 1 &&
	 snode->parameters()[0]->isFixed() &&
	 snode->parameters()[1]->isFixed()))
      return false;
    break;
  default:
    return false;
  }

  vector<StochasticNode const*> stoch_nodes;
  vector<Node*> dtrm_nodes;
  classifyChildren(vector<StochasticNode*>(1,snode), 
		   graph, stoch_nodes, dtrm_nodes);

  /* 
     Create a set of nodes containing snode and its deterministic
     descendants for the checks below.
  */
  set<SArray const *> paramset;
  paramset.insert(&snode->data);
  for (unsigned int j = 0; j < dtrm_nodes.size(); ++j) {
    paramset.insert(&dtrm_nodes[j]->data);
  }

  // Check deterministic descendants
  // Only Mixture nodes are allowed
  for (unsigned int j = 0; j < dtrm_nodes.size(); ++j) {
    if (isMixture(dtrm_nodes[j])) {
      // Check that indices do not depend on snode
      vector<Node *> const &index = asMixture(dtrm_nodes[j])->index();
      for (unsigned int i = 0; i < index.size(); ++i) {
        if (paramset.count(&index[i]->data))
          return false;
      }
    }
    else {
      return false;
    }
  }

  // Check stochastic children
  for (unsigned int i = 0; i < stoch_nodes.size(); ++i) {
    if (stoch_nodes[i]->isBounded()) {
      return false; //Bounded
    }
    switch(getDist(stoch_nodes[i])) {
    case BIN: 
      if (paramset.count(stoch_nodes[i]->parameters()[1])) {
	return false; //n depends on snode
      }      
      break;
    case BERN:
      break;
    default:
      return false;
    }
  }

  return true; //We made it!
}

void ConjugateBeta::update()
{
  vector<StochasticNode const*> const &stoch_children = stochasticChildren();
  StochasticNode const *snode = node();

  double a, b;
  switch (_target_dist) {
  case BETA:
    a = *snode->parameters()[0]->value();
    b = *snode->parameters()[1]->value();
    break;
  case UNIF:
    a = 1;
    b = 1;
  }
  unsigned int Nchild = stoch_children.size();

  /* For mixture models, we count only stochastic children that
     depend on snode */
  double *C = 0;
  bool is_mix = !deterministicChildren().empty();
  if (is_mix) {
    C = new double[Nchild];
    for (unsigned int i = 0; i < Nchild; ++i) {
      C[i] = *stoch_children[i]->parameters()[0]->value();
    }
    // Perturb current value, keeping in the legal range [0,1]
    double x = *snode->data.value();
    x = x > 0.5 ? x - 0.4 : x + 0.4;
    setValue(&x, 1);
    // C[i] == 1 if parameter of child i has changed (so depends on snode)
    // C[i] == 0 otherwise
    for (unsigned int i = 0; i < Nchild; ++i) {
      C[i] = (*stoch_children[i]->parameters()[0]->value() != C[i]);
    }
  }

  for (unsigned int i = 0; i < stoch_children.size(); ++i) {
    if (!(is_mix && C[i] == 0)) {
      double y = *stoch_children[i]->data.value();
      double n;
      switch(_child_dist[i]) {
      case BIN:
	n = *stoch_children[i]->parameters()[1]->value();
	break;
      case BERN:
	n = 1;
	break;
      default:
	throw logic_error("Invalid distribution in Conjugate Beta sampler");
      }
      a += y;
      b += (n - y);
    }
  }

  // Draw the sample
  double xnew = rbeta(a, b);
  if (node()->isBounded()) {
    double lower = 0;
    SArray const *lb = node()->lowerBound();
    if (lb) {
      lower = max(lower, *lb->value());
    }
    double upper = 1;
    SArray const *ub = node()->upperBound();
    if (ub) {
      upper = min(upper, *ub->value());
    }
    /* Try 4 more attempts to get random sample within the bounds */
    for (int i = 0; i < 4; i++) {
      if (xnew >= lower && xnew <= upper) {
	setValue(&xnew, 1);
	return;
      }
      xnew = rbeta(a, b);
    }
    /* Failure! Use inversion */
    double plower = lb ? pbeta(lower, a, b, 1, 0) : 0;
    double pupper = ub ? pbeta(upper, a, b, 1, 0) : 1;
    double p = runif(plower, pupper);
    xnew = qbeta(p, a, b, 1, 0);   
  }
  setValue(&xnew, 1);

  if (is_mix) {
    delete [] C;
  }
}


syntax highlighted by Code2HTML, v. 0.9.1