//  STACK.C
//  Stack functions
//  © 2021 Peter J. Meyer

#include "iss.h"

static short int *stack_ptr;
static short int *end_of_stack;
static short int *max_stack_ptr;
static unsigned int stack_limit_reached;
static unsigned int stack_limit_reached_overflow;
static unsigned long total_stack_pushes;

extern int max_num_items_in_queue;
extern unsigned int queue_limit_reached;
extern unsigned int queue_limit_reached_overflow;
extern unsigned long total_queue_pushes;

//  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); 
/* stack =  ((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);
    }

//  Initialize stack variables.

end_of_stack = stack + stack_size;      //  These are pointers to short ints.
max_stack_ptr = stack;
stack_limit_reached = 0;
stack_limit_reached_overflow = false;
total_stack_pushes = 0;

//  Initialize queue variables also.

max_num_items_in_queue = 0;
queue_limit_reached = 0;
queue_limit_reached_overflow = false;
total_queue_pushes = 0;
}

/*------------------*/
void clear_stack(void)
{
if ( !stack_size )
    {
    printf("\nStack not allocated!\n");
    exit(1);
    }

stack_ptr = stack;
}

/*-----------------*/
int stack_empty(void)
{
return ( stack_ptr == stack );
}

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

/*----------------------------------------------*/
int push_onto_stack(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 );
}

//  The following two functions are called in WOLFF.C.
//  Same as the push/pop functions above, with an extra parameter.

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

if ( stack_ptr + dimensionality + 1 > 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++) = r;
*(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_with_r(short int *rptr, short int *iptr, short int *jptr, ...)
{
short int *kptr, *lptr;

if ( stack_ptr < stack + dimensionality + 1 ) 
    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);
*rptr = *(--stack_ptr);

return ( true );
}

//  This assumes that we are writing to a file already.
/*---------------------------*/
void write_stack_data(FILE *of)
{
int max_num_short_ints_on_stack = max_stack_ptr - stack;
int stack_size_in_bytes = stack_size*sizeof(short int);
int short_ints_per_stack_push;

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

short_ints_per_stack_push = ( dynamics_is_swendsen_wang ? 
    SHORT_INTS_PER_SWENDSEN_WANG_STACK_PUSH : SHORT_INTS_PER_WOLFF_STACK_PUSH );

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

fprintf(of,"\n%.2f%% of the stack was used (stack size = ",
    ((double)max_num_short_ints_on_stack*100)/stack_size);      
if ( stack_size_in_bytes < 10000 )
    fprintf(of,"%d bytes).",stack_size_in_bytes);
else
    {
    ultoa_commas(stack_size_in_bytes/1024,temp);
    fprintf(of,"%s KB).",temp);
    }         

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

fprintf(of,"\n");
}