#include "../include/rcore.h"
#include "../core/allocations.h"
#include "../core/data.table.h"
#include "../include/globals.h"
#include "../math/linear.algebra.h"
#include "../minimal/common.h"
#include "../minimal/data.frame.h"
#include "scores.h"

double glik_incomplete(double *xx, int nobs, double k) {

int i = 0, nc = 0;
double res  = 0, xm = 0, sd = 0;

  c_ols(NULL, xx, nobs, 0, NULL, NULL, &xm, &sd, &nc, TRUE);

  /* compute the log-likelihood. */
  if ((sd < MACHINE_TOL) || (nc == 0)) {

    /* singular distributions and those with no locally-complete observations
     * are scored to prevent their selection in structure learning. */
    res = R_NegInf;

  }/*THEN*/
  else {

    for (i = 0; i < nobs; i++)
      if (!ISNAN(xx[i]))
        res += dnorm(xx[i], xm, (double)sd, TRUE);

    /* average over the locally-complete data... */
    res /= nc;
    /* and add the penalty term, scaled by the original sample size. */
    res -= k / nobs * 2;

  }/*ELSE*/

  return res;

}/*GLIK_INCOMPLETE*/

double cglik_incomplete(double *xx, int nobs, double **dd, int ncol, double k) {

int i = 0, nc = 0;
double res = 0, *fitted = NULL, sd = 0;

  /* allocate the fitted values. */
  fitted = Calloc1D(nobs, sizeof(double));

  c_ols(dd, xx, nobs, ncol, fitted, NULL, NULL, &sd, &nc, TRUE);

  /* compute the log-likelihood. */
  if ((sd < MACHINE_TOL) || (nc == 0)) {

    /* singular distributions and those with no locally-complete observations
     * are scored to prevent their selection in structure learning. */
    res = R_NegInf;

  }/*THEN*/
  else {

    for (i = 0; i < nobs; i++)
      if (!ISNAN(fitted[i]) && !ISNAN(xx[i]))
        res += dnorm(xx[i], fitted[i], (double)sd, TRUE);

    /* average over the locally-complete data... */
    res /= nc;
    /* and add the penalty term, scaled by the original sample size. */
    res -= k / nobs * (ncol + 2);

  }/*ELSE*/

  Free1D(fitted);

  return res;

}/*CGLIK_INCOMPLETE*/

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

double loglik = 0, **dd = NULL;
char *t = (char *)CHAR(STRING_ELT(target, 0));
int i = 0, npar = 0, node = 0, it = 0;

  /* 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 (continuous) column in the tabular data. */
  node = dt.map[tabular_column(dt, t)];

  /* compute the log-likelihood. */
  if (npar == 0) {

    loglik = glik_incomplete(dt.ccol[node], dt.m.nobs, k);

  }/*THEN*/
  else {

    /* gather the parents' columns. */
    dd = Calloc1D(npar, sizeof(double *));
    for (i = 0; i < npar; i++)
      dd[i] = dt.ccol[dt.map[tabular_column(dt, sam.labels[sam.colidx[sam.rowptr[it] + i]])]];

    loglik = cglik_incomplete(dt.ccol[node], dt.m.nobs, dd, npar, k);

    Free1D(dd);

  }/*ELSE*/

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

  return loglik;

}/*NAL_GNODE*/
