/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

/*
  This file contains routines for sorting numbers and determining ranks.

  File:    ranks.c
  Author:  B. Douglas Ward
  Date:    31 March 2000

*/

/*---------------------------------------------------------------------------*/
/*
  Structure to store list of values, sorted in increasing order.
*/
  
typedef struct node
{
  float fval;             /* floating point value */
  int d;                  /* count of number of occurrences of this value */
  struct node * next;     /* link to next node */
} node;


/*---------------------------------------------------------------------------*/
/*
  Print contents of list, starting at smallest value. 
*/

void list_print (node * n, int * count)
{
  int i;

  for (i = 0;  i < n->d;  i++)
    {
      printf (" %6.1f", n->fval);
      *count += 1;
      if (*count % 10 == 0)
	printf ("\n");
    }

  if (n->next != NULL)
    list_print (n->next, count);
}


/*---------------------------------------------------------------------------*/
/*
  Delete the entire list.
*/

void list_delete (node ** n)
{
  if ((*n)->next != NULL)
    list_delete (&((*n)->next));
  free (*n);
  *n = NULL;
}


/*---------------------------------------------------------------------------*/
/*
  Insert one node with value r (before the node *n); reset pointers.
*/

void node_insert (node ** n, float r)
{
  node * ptr;

  ptr = *n;
  *n = (node *) malloc (sizeof(node));
  (*n)->fval = r;
  (*n)->d = 1;
  (*n)->next = ptr;
}


/*---------------------------------------------------------------------------*/
/*
  Add number r to list; if number is already in the list, just increment the
  counter.  Otherwise, insert new node.
*/

void node_addvalue (node ** head, float r)
{
  node ** lastptr;
  node * ptr;


  if (*head == NULL)  node_insert (head, r);
  else
    {
      lastptr = head;
      ptr = *head;

      while ( (ptr->fval < r) && (ptr->next != NULL) )
	{
	  lastptr = &(ptr->next);
	  ptr = ptr->next;
	}
      
      if (ptr->fval > r)
	node_insert (lastptr, r);
      else
	if (ptr->fval == r)
	  ptr->d += 1;
        else
	  node_insert (&(ptr->next), r);
    }
}

/*---------------------------------------------------------------------------*/
/* Creates an entire list at once from an array -- RWCox -- Oct 2007.
*/

void node_allatonce( node **head, int nar , float *ar )
{
  node **lastptr; node *ptr;
  int ii , ibot ; float r ;

  qsort_float( nar , ar ) ;

  *head = NULL ; node_insert(head,ar[0]) ;

  lastptr = head ; ptr = *head ;
  for( ii=1 ; ii < nar ; ii++ ){
    r = ar[ii] ;
    if( r == ptr->fval ){ ptr->d ++ ; }
    else                { node_insert(&(ptr->next),r); ptr = ptr->next; }
  }

  return ;
}


/*---------------------------------------------------------------------------*/
/*
  Get rank corresponding to number r.  If ties exist, return average rank.
*/

float node_get_rank (node * head, float r)
{
  node * ptr;
  float rank;

  ptr = head;
  rank = 0.0;

  while (ptr->fval != r)
    {
      rank += ptr->d;
      ptr = ptr->next;
    }

  rank += (ptr->d + 1) / 2.0;
  return (rank);
}


/*---------------------------------------------------------------------------*/
/*
  Return value corresponding to the specified rank.
*/

float node_get_value (node * head, int rank)
{
  node * ptr;
  int k;

  ptr = head;
  k = 0;

  while (k + ptr->d < rank)
    {
      k += ptr->d;
      ptr = ptr->next;
    }

  return (ptr->fval);
}


/*---------------------------------------------------------------------------*/
/*
  Return value corresponding to the median value.
*/

float node_get_median (node * head, int n)
{
  float median;


  if (n % 2)
    median = node_get_value(head, n/2 + 1);
  else
    median = 0.5 * (node_get_value(head, n/2) + 
		    node_get_value(head, n/2 + 1));

  return (median);
}


/*---------------------------------------------------------------------------*/
/*
  Sort the input data array of floats, and return a new array containing 
  the ranks of the input data. 
*/

float * rank_array
(
  int n,                             /* number of data points */
  float * xarray                     /* array of data to be ranked */
)

{
  int i;                             /* array index */
  node * xhead = NULL;               /* pointer to list of sorted values */
  float * rarray = NULL;             /* array of ranks */


  /*----- Allocate memory for array of ranks -----*/
  rarray = (float *) malloc (sizeof(float) * n);    MTEST (rarray); 


  /*----- Enter and sort original data  -----*/
  for (i = 0;  i < n;  i++)
    node_addvalue (&xhead, xarray[i]); 


  /*----- Get ranks of data -----*/
  for (i = 0;  i < n;  i++)
    rarray[i] = node_get_rank (xhead, xarray[i]);


  /*----- Deallocate memory -----*/
  list_delete (&xhead);


  /*----- Return array of ranks -----*/
  return (rarray);
}


/*---------------------------------------------------------------------------*/
/*
  Sort the input data array of doubles, and return a new array containing 
  the ranks of the input data. 
*/

float * rank_darray
(
  int n,                             /* number of data points */
  double * darray                    /* array of data to be ranked */
)

{
  int i;                             /* array index */
  node * xhead = NULL;               /* pointer to list of sorted values */
  float * rarray = NULL;             /* array of ranks */


  /*----- Allocate memory for array of ranks -----*/
  rarray = (float *) malloc (sizeof(float) * n);    MTEST (rarray); 


  /*----- Enter and sort original data  -----*/
  for (i = 0;  i < n;  i++)
    node_addvalue (&xhead, (float) darray[i]); 


  /*----- Get ranks of data -----*/
  for (i = 0;  i < n;  i++)
    rarray[i] = node_get_rank (xhead, (float) darray[i]);


  /*----- Deallocate memory -----*/
  list_delete (&xhead);


  /*----- Return array of ranks -----*/
  return (rarray);
}


/*---------------------------------------------------------------------------*/

