#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"

double nal_dnode_root(int *xx, int llx, int nobs, double k) {

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

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

  if (marginal.nobs == 0) {

    /* no locally-complete observations: prevent a network with this local
     * distribution from being selected in structure learning. */
    res = R_NegInf;

  }/*THEN*/
  else {

    /* compute the node-average log-likelihood from the marginal frequencies. */
    res = dlik(marginal) / marginal.nobs;
    /* add the penalty term, scaled by the original sample size. */
    res -= k / nobs * (marginal.llx - 1);

  }/*ELSE*/

  Free1DTAB(marginal);

  return res;

}/*NAL_DNODE_ROOT*/

double nal_dnode_parents(int *xx, int llx, int *yy, int lly, int nobs,
    double k) {

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);

  if (joint.nobs == 0) {

    /* no locally-complete observations: prevent a network with this local
     * distribution from being selected in structure learning. */
    res = R_NegInf;

  }/*THEN*/
  else {

    /* compute the node-average log-likelihood from the marginal frequencies. */
    res = cdlik(joint) / joint.nobs;
    /* add the penalty term, scaled by the original sample size. */
    res -= k / nobs * ((joint.llx - 1) * joint.lly);

  }/*ELSE*/

  Free2DTAB(joint);

  return res;

}/*NAL_DNODE_PARENTS*/

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

double loglik = 0;
char *t = (char *)CHAR(STRING_ELT(target, 0));
int i = 0, npar = 0, node = 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 = nal_dnode_root(dt.dcol[node], dt.nlvl[node], dt.m.nobs, k);

  }/*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);

    loglik = nal_dnode_parents(dt.dcol[node], dt.nlvl[node], config, nconfig,
               dt.m.nobs, k);

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

  }/*ELSE*/

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

  return loglik;

}/*NAL_DNODE*/
