//  QUEUE.C
//  Queue functions
//  © 2021 Peter J. Meyer

//  A circular queue is used, that is, when the queue pointers 
//  exceed queue memory space they wrap around.

#include "iss.h"

#define queue_size stack_size
//  Queue and stack use same memory.
//  stack_size = number of short ints, not number of bytes.

#define abs_queue_start stack
//  address of queue memory space.
#define abs_queue_end  (stack+stack_size)
//  address of 1st short int beyond queue memory space.

short int *queue_start;
//  address of 1st short int in queue.
short int *queue_end;
//  address of 1st short int beyond last item in queue

//  Thes are referenced by allocate_memory_for_stack().
int max_num_items_in_queue;
unsigned int queue_limit_reached;
unsigned int queue_limit_reached_overflow;
unsigned long total_queue_pushes;

static int num_items_in_queue(void);
static int num_free_places_in_queue(void);
static void queue_increment(short int **queue_ptr);

/*------------------*/
void clear_queue(void)
{
if ( !queue_size )
    {
    printf("\nQueue not allocated!\n");
    exit(1);
    }

queue_start = queue_end = abs_queue_start;
}

/*-----------------*/
int queue_empty(void)
{
return ( queue_end == queue_start );
}


/*-------------------------------------*/
void check_queue_is_empty(char *location)
{
if ( !queue_empty() )
    {
    printf("\nQueue not empty (in %s).\n",location);
    exit(1);       
    } 
}

#if false

/*----------------------------------------------*/
int push_onto_queue(short int i, short int j, ...)
{
//  int k, l;

if ( stack_ptr + dimensionality > end_of_stack ) 
    {
    if ( stack_limit_reached + 1 == 0 )
        stack_limit_reached_overflow = true;
    else
        stack_limit_reached++;
    return ( false );
    }

total_stack_pushes++;

*(stack_ptr++) = i;
*(stack_ptr++) = j;

switch ( dimensionality )
    {
    case 3:
    //  k = *(((int *)(&j))+1);
    *(stack_ptr++) = *(((int *)(&j))+1);
    break;

    case 4:
    //  k = *(((int *)(&j))+1);
    //  l = *(((int *)(&j))+2);
    *(stack_ptr++) = *(((int *)(&j))+1);
    *(stack_ptr++) = *(((int *)(&j))+2);
    break;
    }

if ( stack_ptr > max_stack_ptr )
    max_stack_ptr = stack_ptr;

return ( true );
}

/*-----------------------------------------------------*/
int pop_from_stack(short int *iptr, short int *jptr, ...)
{
short int *kptr, *lptr;

if ( stack_ptr < stack + dimensionality  ) 
    return ( false );

//  Must decrement stack pointer *before* popping stack value.

switch ( dimensionality )
    {        
    case 3:
    kptr = *(((short int **)(&jptr))+1);
    *kptr = *(--stack_ptr);
    break;

    case 4:
    kptr = *(((short int **)(&jptr))+1);
    lptr = *(((short int **)(&jptr))+2);
    *lptr = *(--stack_ptr);
    *kptr = *(--stack_ptr);
    break;
    }

*jptr = *(--stack_ptr);
*iptr = *(--stack_ptr);

return ( true );
}

#endif

/*-------------------------------*/
static int num_items_in_queue(void)
{
if ( queue_start <= queue_end )
    return ( queue_end - queue_start );
else
    //  queue_end < queue_start, so queue wraps around.
    return ( queue_end + queue_size - queue_start );
}

/*-------------------------------------*/
static int num_free_places_in_queue(void)
{
return ( queue_size - num_items_in_queue() );
}

//  This adds items to the end of the queue.
/*------------------------------------------------------------------*/
int push_onto_queue_with_n(short int n, short int i, short int j, ...)
{
int k, l;

if ( num_free_places_in_queue() < dimensionality + 1 ) 
    {
    if ( queue_limit_reached + 1 == 0 )
        queue_limit_reached_overflow = true;
    else
        queue_limit_reached++;
    return ( false );
    }

total_queue_pushes++;

*queue_end = n;
queue_increment(&queue_end);
*queue_end = i;
queue_increment(&queue_end);
*queue_end = j;
queue_increment(&queue_end);

switch ( dimensionality )
    {
    case 3:
    k = *(((int *)(&j))+1);
    *queue_end = k;
    queue_increment(&queue_end);
    break;

    case 4:
    k = *(((int *)(&j))+1);
    l = *(((int *)(&j))+2);
    //  *(stack_ptr++) = *(((int *)(&j))+1);
    //  *(stack_ptr++) = *(((int *)(&j))+2);
    *queue_end = k;
    queue_increment(&queue_end);
    *queue_end = l;
    queue_increment(&queue_end);
    break;
    }

if ( num_items_in_queue() > max_num_items_in_queue )
    max_num_items_in_queue = num_items_in_queue();

return ( true );
}

//  This removes items from the start of the queue.
//  Unlike with a stack, with a queue items are popped 
//  in the order in which they were pushed.
/*-----------------------------------------------------------------------------*/
int pop_from_queue_with_n(short int *nptr, short int *iptr, short int *jptr, ...)
{
short int *kptr, *lptr;

if ( num_items_in_queue() < dimensionality + 1 ) 
    return ( false );

*nptr = *queue_start;
queue_increment(&queue_start);
*iptr = *queue_start;
queue_increment(&queue_start);
*jptr = *queue_start;
queue_increment(&queue_start);

switch ( dimensionality )
    {        
    case 3:
    kptr = *(((short int **)(&jptr))+1);
    *kptr = *queue_start;
    queue_increment(&queue_start);
    break;

    case 4:
    kptr = *(((short int **)(&jptr))+1);
    lptr = *(((short int **)(&jptr))+2);
    *kptr = *queue_start;
    queue_increment(&queue_start);
    *lptr = *queue_start;
    queue_increment(&queue_start);
    break;
    }

return ( true );
}

//  This adds 1 to queue_ptr with wrap-around 
//  if new queue_ptr is out of queue memory.
//  This function assumes that queue_ptr is within queue memory space.
/*---------------------------------------*/
void queue_increment(short int **queue_ptr)
{
if ( ++(*queue_ptr) == abs_queue_end )
    *queue_ptr = abs_queue_start;
}

//  This assumes that we are writing to a file already.
/*---------------------------*/
void write_queue_data(FILE *of)
{
int queue_size_in_bytes = queue_size*sizeof(short int);
int short_ints_per_queue_push;

fprintf(of,"\n\nTotal number of queue pushes = %s",ultoa_commas(total_queue_pushes,temp));

//  short_ints_per_stack_push = ( dynamics_is_swendsen_wang ? 
//      SHORT_INTS_PER_SWENDSEN_WANG_STACK_PUSH : SHORT_INTS_PER_WOLFF_STACK_PUSH );
short_ints_per_queue_push = dimensionality + 1;

fprintf(of,"\nMaximum consecutive stack pushes = %s",
  ultoa_commas(max_num_items_in_queue/short_ints_per_queue_push,temp1));        

fprintf(of,"\n%.2f%% of the queue was used (queue size = ",
    ((double)max_num_items_in_queue*100)/queue_size);      

if ( queue_size_in_bytes < 10000 )
    fprintf(of,"%d bytes).",queue_size_in_bytes);
else
    {
    ultoa_commas(queue_size_in_bytes/1024,temp);
    fprintf(of,"%s KB).",temp);
    }         

if ( queue_limit_reached )
    {
    fprintf(of,"\nQueue limit was reached ");
    if ( queue_limit_reached_overflow )
        fprintf(of,"more than ");
    fprintf(of,"%s time%s.\n",ultoa_commas(queue_limit_reached,temp),
        (queue_limit_reached==1 ? "" : "s" ));
    }

fprintf(of,"\n");
}

#if false

//  Queue and stack use same memory.

//  stack_size is a global variable.
/*--------------------------------*/
void allocate_memory_for_stack(void)
{
int err_flag;

//  stack_size = number of short ints, not number of bytes.
if ( !stack_size )
    {
    printf("\nAttempt to allocate stack of zero size.\n");
    exit(1);
    }
 
stack = (A1(short int))array_alloc(&err_flag, sizeof(short int), 
    NULL, 1, stack_size);
if ( err_flag )
    {
    printf("\nError %d when attempting to allocate an array of %d short ints for stack.\n",
        err_flag,stack_size);
    exit(1);
    }

end_of_stack = stack + stack_size;      //  These are pointers to ints.
max_stack_ptr = stack;

stack_limit_reached = 0;
stack_limit_reached_overflow = false;
total_stack_pushes = 0;
}
#endif