/*
     File:         heaputils.c
     Author:       David Cohn
     Created:      22 Aug 2002
     Description:  Utilities using the heap code
*/

#include "heaputils.h"
#include "sheap.h"

/* A (relatively) fast way to get the indices of the n largest
   elements in a laaaarge dyv.
*/

ivec *mk_indices_of_n_largest_in_dyv(dyv *vec, int n)
{
  int dim = dyv_size(vec) ;

  ivec *best_indices = mk_constant_ivec(n, -1) ;
  int *holders = (int*)am_malloc(n*sizeof(int)) ;
  sheap *heap = mk_sheap_given_max_size(n) ;
  
  double least_best_val = -77.7;
  int index ;

  for (index = 0; index < dim; ++index) {
    double val = dyv_ref(vec, index) ;

    /* If the queue isn't full, just add the current entry */

    if (sheap_size(heap) < n) {

      /* Stash the index we're inserting into one of the holders */

      int *place = &holders[sheap_size(heap)] ;
      *place = index ;

      add_to_sheap(heap, place, val) ;
      least_best_val = sheap_minimum_priority(heap) ;
    }

    /* If queue is already full, see if current value is better than
       current least_best_val. If so, stick it in, ditch the
       least_best_val, and find a new one.
    */

    else if (val > least_best_val) {
      /* We're going to pop something off - keep track of its placeholder
	 so we can reuse it.
      */
      int *place = sheap_data_with_minimum_priority(heap) ;
      sheap_remove_minimum_priority(heap) ;

      /* Stash the new index in place, and insert in the heap */
      *place = index ;
      add_to_sheap(heap, place, val) ;

      /* printf("new value %g > %g, popped\n", val,least_best_val) ; */

      least_best_val = sheap_minimum_priority(heap) ;

      /* printf("new least_best_val is %g\n", least_best_val) ; */
    }

    /* printf("loaded %g, lbest %g\n", val, least_best_val) ; */
  }

  for (index = sheap_size(heap) - 1; index >= 0; --index) {
    int *place = (int*) sheap_data_with_minimum_priority(heap) ;
    ivec_set(best_indices, index, *place) ;
    sheap_remove_minimum_priority(heap) ;
  }
  am_free((void*)holders, n*sizeof(int)) ;
  free_sheap(heap) ;

  return best_indices ;
}

/* The code below uses priority queues, but doesn't work, due to my lack of understanding of what pq_insert does. */
ivec *old_mk_indices_of_n_largest_in_dyv(dyv *vec, int n)
{
  int dim = dyv_size(vec) ;

  ivec *best_indices = mk_constant_ivec(n, -1) ;
  pqueue *queue = pq_create(n) ;
  double least_best_val = -77.7;
  int index ;

  for (index = 0; index < dim; ++index) {
    double val = -dyv_ref(vec, index) ;

    /* If the queue isn't full, just add the current entry */

    if (pq_size(queue) < n) {
      pq_insert(queue, index, val) ;
      least_best_val = priority_of_best(queue) ;
      printf("adding\n") ;
    }

    /* If queue is already full, see if current value is better than
       current least_best_val. If so, stick it in, ditch the
       least_best_val, and find a new one.
    */

    else if (val > least_best_val) {
      printf("new value %g > %g, popping\n", val,least_best_val) ;
      pq_pop_best(queue) ;
      pq_insert(queue, index, val) ;
      least_best_val = priority_of_best(queue) ;

      printf("new least_best_val is %g\n", least_best_val) ;
    }

    printf("loaded %g, lbest %g\n", val, least_best_val) ;
  }

  for (index = pq_size(queue) - 1; index >= 0; --index)
    ivec_set(best_indices, index, pq_pop_best(queue)) ;
  
  free_pqueue(queue) ;

  return best_indices ;
}
