#include "../include/rcore.h"
#include "../core/sort.h"
#include "../core/uppertriangular.h"
#include "../math/linear.algebra.h"

SEXP arc_hash_matrix(SEXP arcs, SEXP nodes, bool uptri, bool sort);
SEXP arc_hash_dataframe(SEXP arcs, SEXP nodes, bool uptri, bool sort);

/* compute the hash of an arc set using CMC()/UPTRI3() coordinates. */
SEXP arc_hash(SEXP arcs, SEXP nodes, bool uptri, bool sort) {

  switch(TYPEOF(arcs)) {

    case STRSXP:
      return arc_hash_matrix(arcs, nodes, uptri, sort);
      break;

    case VECSXP:
      return arc_hash_dataframe(arcs, nodes, uptri, sort);
      break;

  }/*SWITCH*/

  return R_NilValue;

}/*ARC_HASH*/

/* arc hash backend. */
void c_arc_hash(int narcs, int nnodes, int *from, int *to, int *uptri,
  int *cmc, bool sort) {

int i = 0;

  if (uptri) {

    for (i = 0; i < narcs; i++)
      uptri[i] = UPTRI3(from[i], to[i], nnodes);

    if (sort)
      i_sort(uptri, NULL, narcs);

  }/*THEN*/

  if (cmc) {

    for (i = 0; i < narcs; i++)
      cmc[i] = CMC(from[i], to[i], nnodes);

    if (sort)
      i_sort(cmc, NULL, narcs);

  }/*ELSE*/

}/*C_ARC_HASH*/

/* compute the hash of an arc set from an arc matrix. */
SEXP arc_hash_matrix(SEXP arcs, SEXP nodes, bool uptri, bool sort) {

int narcs = length(arcs)/2, nnodes = length(nodes);
int *temp = NULL, *hash_el = NULL;
SEXP matched, hash;

  /* match the lables of the nodes. */
  PROTECT(matched = match(nodes, arcs, 0));
  temp = INTEGER(matched);

  /* allocate the integer array to hold the hash. */
  PROTECT(hash = allocVector(INTSXP, narcs));
  hash_el = INTEGER(hash);

  if (uptri)
    c_arc_hash(narcs, nnodes, temp, temp + narcs, hash_el, NULL, sort);
  else
    c_arc_hash(narcs, nnodes, temp, temp + narcs, NULL, hash_el, sort);

  UNPROTECT(2);

  return hash;

}/*ARC_HASH_MATRIX*/

/* compute the hash of an arc set from a data frame. */
SEXP arc_hash_dataframe(SEXP arcs, SEXP nodes, bool uptri, bool sort) {

int narcs = length(VECTOR_ELT(arcs, 0)), nnodes = length(nodes);
int *temp1 = NULL, *temp2 = NULL, *hash_el = NULL;
SEXP matched1, matched2, hash;

  /* match the labels in the first column. */
  PROTECT(matched1 = match(nodes, VECTOR_ELT(arcs, 0), 0));
  temp1 = INTEGER(matched1);
  /* match the labels in the second column. */
  PROTECT(matched2 = match(nodes, VECTOR_ELT(arcs, 1), 0));
  temp2 = INTEGER(matched2);

  /* allocate the integer array to hold the hash. */
  PROTECT(hash = allocVector(INTSXP, narcs));
  hash_el = INTEGER(hash);

  if (uptri)
    c_arc_hash(narcs, nnodes, temp1, temp2, hash_el, NULL, sort);
  else
    c_arc_hash(narcs, nnodes, temp1, temp2, NULL, hash_el, sort);

  UNPROTECT(3);

  return hash;

}/*ARC_HASH_DATAFRAME*/

/* test membership in a sorted set of flat CMC() coordinates (used to encode
 * blacklists, whitelists and adjacency matrices sparsely). */
bool coord_set_member(int *coords, int ncoords, int coord) {

int lo = 0, hi = ncoords - 1, mid = 0;

  while (lo <= hi) {

    mid = lo + (hi - lo) / 2;

    if (coords[mid] == coord)
      return true;
    else if (coords[mid] < coord)
      lo = mid + 1;
    else
      hi = mid - 1;

  }/*WHILE*/

  return false;

}/*COORD_SET_MEMBER*/


