/***************************************
 Queue data type functions.

 Part of the Routino routing software.
 ******************/ /******************
 This file Copyright 2008-2013, 2017 Andrew M. Bishop

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ***************************************/


#include <string.h>
#include <stdlib.h>

#include "results.h"
#include "logging.h"


/*+ A queue of results. +*/
struct _Queue
{
 uint32_t nincrement;           /*+ The amount to increment the queue when full. +*/
 uint32_t nallocated;           /*+ The number of entries allocated. +*/
 uint32_t noccupied;            /*+ The number of entries occupied. +*/

 Result **results;              /*+ The queue of pointers to results. +*/
};


/*++++++++++++++++++++++++++++++++++++++
  Allocate a new queue.

  Queue *NewQueueList Returns the queue.

  uint8_t log2bins The base 2 logarithm of the initial number of bins in the queue.
  ++++++++++++++++++++++++++++++++++++++*/

Queue *NewQueueList(uint8_t log2bins)
{
 Queue *queue;

 queue=(Queue*)malloc(sizeof(Queue));

 queue->nincrement=1<<log2bins;

 queue->nallocated=queue->nincrement;
 queue->noccupied=0;

 queue->results=(Result**)malloc(queue->nallocated*sizeof(Result*));

#ifndef LIBROUTINO
 log_malloc(queue->results,queue->nallocated*sizeof(Result*));
#endif

 return(queue);
}


/*++++++++++++++++++++++++++++++++++++++
  Re-use an existing queue.

  Queue *queue The queue to reset for re-use.
  ++++++++++++++++++++++++++++++++++++++*/

void ResetQueueList(Queue *queue)
{
 queue->noccupied=0;
}


/*++++++++++++++++++++++++++++++++++++++
  Free a queue.

  Queue *queue The queue to be freed.
  ++++++++++++++++++++++++++++++++++++++*/

void FreeQueueList(Queue *queue)
{
#ifndef LIBROUTINO
 log_free(queue->results);
#endif

 free(queue->results);

 free(queue);
}


/*++++++++++++++++++++++++++++++++++++++
  Insert a new item into the queue in the right place.

  The data is stored in a "Binary Heap" http://en.wikipedia.org/wiki/Binary_heap
  and this operation is adding an item to the heap.

  Queue *queue The queue to insert the result into.

  Result *result The result to insert into the queue.

  score_t score The score to use for sorting the node.
  ++++++++++++++++++++++++++++++++++++++*/

void InsertInQueue(Queue *queue,Result *result,score_t score)
{
 uint32_t index;

 if(result->queued==NOT_QUEUED)
   {
    queue->noccupied++;
    index=queue->noccupied;

    if(queue->noccupied==queue->nallocated)
      {
       queue->nallocated=queue->nallocated+queue->nincrement;
       queue->results=(Result**)realloc((void*)queue->results,queue->nallocated*sizeof(Result*));

#ifndef LIBROUTINO
       log_malloc(queue->results,queue->nallocated*sizeof(Result*));
#endif
      }

    queue->results[index]=result;
    queue->results[index]->queued=index;
   }
 else
    index=result->queued;

 queue->results[index]->sortby=score;

 /* Bubble up the new value */

 while(index>1)
   {
    uint32_t newindex;
    Result *temp;

    newindex=index/2;

    if(queue->results[index]->sortby>=queue->results[newindex]->sortby)
       break;

    temp=queue->results[index];
    queue->results[index]=queue->results[newindex];
    queue->results[newindex]=temp;

    queue->results[index]->queued=index;
    queue->results[newindex]->queued=newindex;

    index=newindex;
   }
}


/*++++++++++++++++++++++++++++++++++++++
  Pop an item from the front of the queue.

  The data is stored in a "Binary Heap" http://en.wikipedia.org/wiki/Binary_heap
  and this operation is deleting the root item from the heap.

  Result *PopFromQueue Returns the top item.

  Queue *queue The queue to remove the result from.
  ++++++++++++++++++++++++++++++++++++++*/

Result *PopFromQueue(Queue *queue)
{
 uint32_t index;
 Result *retval;

 if(queue->noccupied==0)
    return(NULL);

 retval=queue->results[1];
 retval->queued=NOT_QUEUED;

 index=1;

 queue->results[index]=queue->results[queue->noccupied];

 queue->noccupied--;

 /* Bubble down the newly promoted value */

 while((2*index)<queue->noccupied)
   {
    uint32_t newindex;
    Result *temp;

    newindex=2*index;

    if(queue->results[newindex]->sortby>queue->results[newindex+1]->sortby)
       newindex=newindex+1;

    if(queue->results[index]->sortby<=queue->results[newindex]->sortby)
       break;

    temp=queue->results[newindex];
    queue->results[newindex]=queue->results[index];
    queue->results[index]=temp;

    queue->results[index]->queued=index;
    queue->results[newindex]->queued=newindex;

    index=newindex;
   }

 if((2*index)==queue->noccupied)
   {
    uint32_t newindex;
    Result *temp;

    newindex=2*index;

    if(queue->results[index]->sortby<=queue->results[newindex]->sortby)
       ; /* break */
    else
      {
       temp=queue->results[newindex];
       queue->results[newindex]=queue->results[index];
       queue->results[index]=temp;

       queue->results[index]->queued=index;
       queue->results[newindex]->queued=newindex;
      }
   }

 return(retval);
}
