
#ifndef GENERAL_H
#define GENERAL_H

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

void merror(int errorlevel, char* str, ...);
/* Prints error message. str and the following parameters should be formatted
   the same way as for printf. 
   If errorlevel = 0, the SIGINT signal is sent to terminate the program. */

inline void* tmalloc(size_t NBYTES);
inline void* tcalloc(size_t N, size_t S);
/* These functions call malloc and calloc, respectively. In addition, they 
   terminate the program via merror(...) when memory cannot be allocated. */

/**************************************************************************/
/* Memory management for structures that allocate small amounts of memory
   at a time */

struct _alist {
  void* mem; /* pointer to allocated memory */
  struct _alist *next;
};


struct alloc {
  size_t N; /* the min size of the blocks of memory will be N + 1 */
  void* mem; /* pointer to current block of allocated memory */
  size_t i; /* indicates number of available bytes in mem */
  struct _alist *data, *pos; /* pos points to the current alist element */
};


void initalloc(struct alloc* x, size_t N);
/* Must be called before using the allocation object! 
   N determines the number of bytes of the memory blocks that are allocated. 
   N does not limit the maximum size but the minimum size of the allocated
   blocks. 
*/

inline void* smalloc(struct alloc* x, size_t nbytes);
/* Use it in the place of malloc. The function is optimized for speed, not for
   memory usage. Memory is best used if nbytes is small. The fraction of unused
   allocated memory is in the worst case (max(nbytes)-1)/(x->N + 1), where
   max(nbytes) is the maximum value given by the program to nbytes. */


inline void* szalloc(struct alloc* x, size_t nbytes);
/* Same as smalloc except it initializes to zero the allocated memory */


void sfree(struct alloc *x);
/* Deallocates the memory blocks addressed by the object. */

/**************************************************************************/
/* Memory management for queues */

struct _qlist {
  void* mem; /* pointer to allocated memory */
  struct _qlist *next; 
};


/* The structures is organized in blocks of memory called here pages. Each
   page can store N bytes. A page stores elements of size sz bytes. */
struct qalloc {
  long N, sz; /* N: bytes per page, sz: bytes per element */
  long front, rear; /* front: position of last element; 
			 rear: position of first element; */
  long nitems; /* number of items in the queue */
  struct _qlist *frpage, *rrpage; /*the pages of front and rear, respectively*/
  struct _qlist *last; /* pointer to the last element of the chain of pages */

  /* Note that rrpage always points to the first page of the chain. Once all
     elements on a page have been removed, the empty page is placed at the end
     of the chain and rrpage points to the new first page of the chain.
    
     Each time a new element is added to the queue, front -= sz, until front
     becomes negative. Then, frpage is set to the next page. If there is no
     next page, a new page is allocated. 
  */
};

void initqalloc(struct qalloc* x, size_t n, size_t sz);
/* Must be called before using the allocation object! 
   n: number of elements of (max) size sz per block of memory.
*/

inline void* place_in_line(struct qalloc* x);
/* Places a new element in line and returns a pointer to it. */

inline void* next_in_line(struct qalloc* x);
/* Removes from queue the element returned in the previous call (the 
   first element in line) and returns the next element in line. The return 
   value is in the form of a pointer to the element. The pointer is null if 
   no element remains in line. */

void qfree(struct qalloc *x);
/* Deallocates the memory blocks addressed by the object. */


/**************************************************************************/
/* Memory management for stacks */

struct _stkpage {
  void* mem; /* pointer to allocated memory */
  struct _stkpage *next;
  struct _stkpage *prev; 
};



/* The structures is organized in blocks of memory called here pages. Each
   page can store N bytes. A page stores elements of size sz bytes. */
struct stk_alloc {
  long N, sz; /* N: number of bytes per page, sz: number of bytes per item */
  long front; /* front: position of last element */
  long nitems; /* number of items in the stack */
  struct _stkpage *page; /* the page of the element on top of the stack */
  struct _stkpage *firstpage; /* the first element of the page list */

  /* Note that rrpage always points to the first page of the chain. Once all
     elements on a page have been removed, the empty page is placed at the end
     of the chain and rrpage points to the new first page of the chain.
    
     Each time a new element is added to the queue, front -= sz, until front
     becomes negative. Then, frpage is set to the next page. If there is no
     next page, a new page is allocated. 
  */
};

void init_stack(struct stk_alloc* x, size_t n, size_t sz);
/* Must be called before using the allocation object! 
   n: number of items per block, sz: number of bytes per item.
*/

void freestack(struct stk_alloc *x);

inline void* stk_push(struct stk_alloc* x);
/* Places a new item in the stack and returns a pointer to it. 
   Example: To push the integer i, write *((int*) stk_push(x)) = i; 
   (This assumes the stack was initialized to have elements of size >= 
   sizeof(int).)
*/

inline void* stk_pop(struct stk_alloc* x);
/* Takes the top item from the stack and returns a pointer to it. 
   Example: To take the integer i from the top of the stack (without checking
   whether the stack is empty), write
   i = *((int*) stk_pop(x));
*/

#define is_stk_empty(x) ((x)->nitems == 0)

#endif
