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

double glik(double *xx, int num, double *nparams) {

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

  /* compute the mean, */
  for (i = 0; i < num; i++)
    xm += xx[i];
  xm /= num;

  /* compute the standard deviation. */
  if (num == 0)
    sd = NA_REAL;
  else if (num == 1)
    sd = 0;
  else {

    for (i = 0; i < num; i++)
      sd += (xx[i] - xm) * (xx[i] - xm);
    sd = sqrt(sd / (num - 1));

  }/*ELSE*/

  /* compute the log-likelihood (singular models have zero density). */
  if (sd < MACHINE_TOL)
    res = R_NegInf;
  else
    for (i = 0; i < num; i++)
      res += dnorm(xx[i], xm, (double)sd, TRUE);

  /* we may want to store the number of parameters (one mean, one standard
   * deviation). */
  if (nparams)
    *nparams = 2;

  return res;

}/*GLIK*/

double cglik(double *xx, int nrow, double **dd, int ncol, double *nparams) {

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

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

  c_ols(dd, xx, nrow, ncol, fitted, NULL, NULL, &sd, NULL, FALSE);

  /* compute the log-likelihood (singular models have zero density). */
  if (sd < MACHINE_TOL)
    res = R_NegInf;
  else
    for (i = 0; i < nrow; i++)
      res += dnorm(xx[i], fitted[i], (double)sd, TRUE);

  /* we may want to store the number of parameters (one intercept, one
   * regression coefficient per parent, one residuals standard deviation). */
  if (nparams)
    *nparams = ncol + 2;

  Free1D(fitted);

  return res;

}/*CGLIK*/

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

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

  }/*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(dt.ccol[node], dt.m.nobs, dd, npar, nparams);

    Free1D(dd);

  }/*ELSE*/

  if (nparents)
    *nparents = npar;

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

  return loglik;

}/*LOGLIK_GNODE*/
