#include "../include/rcore.h"
#include "../core/allocations.h"
#include "../core/contingency.tables.h"
#include "../core/data.table.h"
#include "../core/sets.h"
#include "../math/sparse.amat.h"
#include "../minimal/common.h"
#include "../minimal/data.frame.h"

double dlik(counts1d marginal) {

double res = 0;

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

  return res;

}/*DLIK*/

double loglik_dnode_root(int *x, int llx, int nobs, double *nparams) {

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

  /* initialize the contingency table. */
  marginal = new_1d_table(llx);
  fill_1d_table(x, &marginal, nobs);

  /* compute the entropy from the marginal frequencies. */
  res = dlik(marginal);

  /* we may want to store the number of parameters. */
  if (nparams)
    *nparams = marginal.llx - 1;

  Free1DTAB(marginal);

  return res;

}/*LOGLIK_DNODE_ROOT*/

double cdlik(counts2d joint) {

double res = 0;

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

  return res;

}/*CDLIK*/

double loglik_dnode_parents(int *x, int llx, int *y, int lly, int nobs,
    double *nparams) {

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

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

  /* compute the conditional entropy from the joint and marginal frequencies. */
  res = cdlik(joint);

  /* we may want to store the number of parameters. */
  if (nparams)
    *nparams = (joint.llx - 1) * joint.lly;

  Free2DTAB(joint);

  return res;

}/*LOGLIK_DNODE_PARENTS*/

double loglik_dnode(SEXP target, sparse_amat sam, tabular dt, double *nparams,
    int *nparents, bool debugging) {

double loglik = 0;
char *t = (char *)CHAR(STRING_ELT(target, 0));
int node = 0, npar = 0, i = 0, nconfig = 0, it = 0;
int **par_cols = NULL, *par_lvls = NULL, *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) {

    loglik = loglik_dnode_root(dt.dcol[node], dt.nlvl[node], dt.m.nobs, nparams);

  }/*THEN*/
  else {

    /* gather the parent columns and their numbers of levels. */
    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*/

    /* generate the (1-based, all-levels) configurations of the parents. */
    config = Calloc1D(dt.m.nobs, sizeof(int));
    c_fast_config(par_cols, dt.m.nobs, npar, par_lvls, config, &nconfig, 1);

    /* compute the log-likelihood. */
    loglik = loglik_dnode_parents(dt.dcol[node], dt.nlvl[node], config, nconfig,
               dt.m.nobs, nparams);

    Free1D(par_cols);
    Free1D(par_lvls);
    Free1D(config);

  }/*ELSE*/

  if (nparents)
    *nparents = npar;

  if (debugging)
    Rprintf("  > log-likelihood is %lf.\n", loglik);

  return loglik;

}/*LOGLIK_DNODE*/
