#include "../include/rcore.h"
#include "../core/allocations.h"
#include "../core/contingency.tables.h"
#include "../core/sets.h"
#include "../minimal/common.h"
#include "../minimal/data.frame.h"
#include "scores.h"

static double cdlik_diff(counts2d joint) {

double res = 0;

  for (int j = 0; j < joint.lly; j++) {

    for (int i = 0; i < joint.llx; i++)
      if (joint.n[i][j] != 0)
        res += (double)joint.n[i][j] * log((double)joint.n[i][j]);

    if (joint.nj[j] != 0)
      res -= (double)joint.nj[j] * log((double)joint.nj[j]);

  }/*FOR*/

  return res;

}/*CDLIK_DIFF*/

/* factorized normalized maximum likelihood, root nodes. */
double fnml(int *xx, int llx, int nobs) {

  double res = 0;
  counts1d marginal = { 0 };

  /* initialize the contingency table. */
  marginal = new_1d_table(llx);
  fill_1d_table(xx, &marginal, nobs);
  res = dlik(marginal);
  res -= nml_regret(marginal.nobs, llx);
  Free1DTAB(marginal);

  return res;

}/*FNML*/

/* factorized normalized maximum likelihood, nodes with parents. */
double cfnml(int *xx, int llx, int *yy, int lly, int nobs) {

  double res = 0;
  counts2d joint = { 0 };

  /* initialize the contingency table and the marginal frequencies. */
  joint = new_2d_table(llx, lly, TRUE);
  fill_2d_table(xx, yy, &joint, nobs);

  res = cdlik(joint);
  for (int j = 0; j < joint.lly; j++) {
    if (joint.nj[j] != 0)
      res -= nml_regret(joint.nj[j], llx);
  }
  Free2DTAB(joint);

  return res;

}/*CFNML*/

/* generate the (1-based, all-levels) configurations of the parents. */
static int *nml_config(sparse_amat sam, int it, tabular dt, int npar,
    int *nconfig) {

int i = 0;
int **par_cols = NULL, *par_lvls = NULL, *config = NULL;

  par_cols = Calloc1D(npar, sizeof(int *));
  par_lvls = Calloc1D(npar, sizeof(int));
  for (i = 0; i < npar; i++) {

    int pc = dt.map[tabular_column(dt, sam.labels[sam.colidx[sam.rowptr[it] + i]])];
    par_cols[i] = dt.dcol[pc];
    par_lvls[i] = dt.nlvl[pc];

  }/*FOR*/

  config = Calloc1D(dt.m.nobs, sizeof(int));
  c_fast_config(par_cols, dt.m.nobs, npar, par_lvls, config, nconfig, 1);

  Free1D(par_cols);
  Free1D(par_lvls);

  return config;

}/*NML_CONFIG*/

double fnml_node(SEXP target, sparse_amat sam, tabular dt, bool debugging) {

double nml = 0;
char *t = (char *)CHAR(STRING_ELT(target, 0));
int npar = 0, node = 0, nconfig = 0, it = 0, *config = NULL;

  /* get the parents of the node from the sparse adjacency matrix. */
  it = sparse_amat_node(sam, t);
  npar = sam.rowptr[it + 1] - sam.rowptr[it];

  /* locate the node's (discrete) column in the tabular data. */
  node = dt.map[tabular_column(dt, t)];

  if (npar == 0) {

    nml = fnml(dt.dcol[node], dt.nlvl[node], dt.m.nobs);

  }/*THEN*/
  else {

    config = nml_config(sam, it, dt, npar, &nconfig);
    nml = cfnml(dt.dcol[node], dt.nlvl[node], config, nconfig, dt.m.nobs);
    Free1D(config);

  }/*ELSE*/

  if (debugging)
    Rprintf("  > normalized maximum likelihood is %lf.\n", nml);

  return nml;

}/*FNML_NODE*/


/* quotient normalized maximum likelihood, root nodes. */
double qnml(int *xx, int llx, int nobs) {

  return fnml(xx, llx, nobs);

}/*QNML*/

/* quotient normalized maximum likelihood, nodes with parents. */
double cqnml(int *xx, int llx, int *yy, int lly, int nobs) {

  double res = 0;
  counts2d joint = { 0 };

  /* initialize the contingency table and the marginal frequencies. */
  joint = new_2d_table(llx, lly, TRUE);
  fill_2d_table(xx, yy, &joint, nobs);
  res += cdlik_diff(joint);
  res -= nml_regret(joint.nobs, joint.llx * joint.lly);
  res += nml_regret(joint.nobs, joint.lly);
  Free2DTAB(joint);

  return res;

}/*CQNML*/

double qnml_node(SEXP target, sparse_amat sam, tabular dt, bool debugging) {

double nml = 0;
char *t = (char *)CHAR(STRING_ELT(target, 0));
int npar = 0, node = 0, nconfig = 0, it = 0, *config = NULL;

  /* get the parents of the node from the sparse adjacency matrix. */
  it = sparse_amat_node(sam, t);
  npar = sam.rowptr[it + 1] - sam.rowptr[it];

  /* locate the node's (discrete) column in the tabular data. */
  node = dt.map[tabular_column(dt, t)];

  if (npar == 0) {

    nml = qnml(dt.dcol[node], dt.nlvl[node], dt.m.nobs);

  }/*THEN*/
  else {

    config = nml_config(sam, it, dt, npar, &nconfig);
    nml = cqnml(dt.dcol[node], dt.nlvl[node], config, nconfig, dt.m.nobs);
    Free1D(config);

  }/*ELSE*/

  if (debugging)
    Rprintf("  > normalized maximum likelihood is %lf.\n", nml);

  return nml;

}/*QNML_NODE*/
