/*
 * dqueue.c --
 *
 *	Routines for double ended queues.  See 'dqueue.h'.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/dqueue.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/dqueue.h"
#include "utils/malloc.h"

/*
 * ----------------------------------------------------------------------------
 *
 * DQInit --
 *
 *	Initialize a new queue to have a certain capacity.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
DQInit(
    DQueue *q,
    int capacity)
{
    if (capacity < 1) capacity = 1;
    q->dq_data = (ClientData *) mallocMagic((unsigned)((capacity+1) * sizeof (ClientData)));
    q->dq_size = 0;
    q->dq_maxSize = capacity;
    q->dq_front = 0;	/* next slot in front is loc 0 */
    q->dq_rear = 1;	/* next slot in rear is loc 1 */
}


/*
 * ----------------------------------------------------------------------------
 *
 * DQFree --
 *
 *	Free up a queue.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
DQFree(
    DQueue *q)
{
    freeMagic((char *) q->dq_data);
}

/*
 * ----------------------------------------------------------------------------
 *
 * DQPushFront & DQPushRear --
 *
 *	Push a new element onto one end of the DQueue.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Puts an element in the queue.
 *
 * ----------------------------------------------------------------------------
 */

void
DQPushFront(
    DQueue *q,
    ClientData elem)
{
    if (q->dq_size == q->dq_maxSize)  DQChangeSize(q, 2 * q->dq_maxSize);
    q->dq_data[q->dq_front] = elem;
    q->dq_front--;
    if (q->dq_front < 0) q->dq_front = q->dq_maxSize;
    q->dq_size++;
}

void
DQPushRear(
    DQueue *q,
    ClientData elem)
{
    if (q->dq_size == q->dq_maxSize)  DQChangeSize(q, 2 * q->dq_maxSize);
    q->dq_data[q->dq_rear] = elem;
    q->dq_rear++;
    if (q->dq_rear > q->dq_maxSize) q->dq_rear = 0;
    q->dq_size++;
}

/*
 * ----------------------------------------------------------------------------
 *
 * DQPopFront & DQPopRear --
 *
 *	Pop an element from one end of the queue.
 *
 * Results:
 *	The element, or NULL if there is none.
 *
 * Side Effects:
 *	Removes the element from the queue.
 *
 * ----------------------------------------------------------------------------
 */

ClientData
DQPopFront(
    DQueue *q)
{
    if (q->dq_size == 0) return (ClientData) NULL;
    q->dq_size--;
    q->dq_front++;
    if (q->dq_front > q->dq_maxSize) q->dq_front = 0;
    return q->dq_data[q->dq_front];
}

ClientData
DQPopRear(
    DQueue *q)
{
    if (q->dq_size == 0) return (ClientData) NULL;
    q->dq_size--;
    q->dq_rear--;
    if (q->dq_rear < 0) q->dq_rear = q->dq_maxSize;
    return q->dq_data[q->dq_rear];
}


/*
 * ----------------------------------------------------------------------------
 *
 * DQChangeSize --
 *
 *	Change the size of a DQueue -- either increase or decrease.
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	The DQueue changes size.
 *
 * ----------------------------------------------------------------------------
 */

void
DQChangeSize(
    DQueue *q,
    int newSize)
{
    DQueue newq;

    if (newSize < q->dq_size) newSize = q->dq_size;
    DQInit(&newq, newSize);
    DQCopy(&newq, q);
    freeMagic((char *) q->dq_data);
    q->dq_data = newq.dq_data;
    q->dq_maxSize = newq.dq_maxSize;
    q->dq_front = newq.dq_front;
    q->dq_rear = newq.dq_rear;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DQCopy --
 *
 *	Copy one DQueue into another.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Elements (ClientData pointers) are copied.
 *
 * ----------------------------------------------------------------------------
 */

void
DQCopy(
    DQueue *dst,	/* The destination queue */
    DQueue *src)	/* The source queue */
{
    int i;
    dst->dq_size = 0;
    i = src->dq_front;
    while (dst->dq_size != src->dq_size) {
	i = i + 1;
	if (i > src->dq_maxSize) i = 0;
	DQPushRear(dst, src->dq_data[i]);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * Main --
 *
 *	Test out this module.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Stuff on the screen.
 *
 * ----------------------------------------------------------------------------
 */

/****
void
main()
{
    int i;
    DQueue q;
    DQInit(&q, 0);
    for (i = 0; i < 10; i++) DQPushFront(&q, (ClientData) i);
    while (!DQIsEmpty(&q)) printf("got %d\n", (int) DQPopRear(&q));
    DQFree(&q);
    printf("-------\n");
    DQInit(&q, 0);
    for (i = 0; i < 10; i++) DQPushFront(&q, (ClientData) i);
    for (i = 0; i < 10000; i++) DQPushRear(&q, DQPopFront(&q));
    for (i = 0; i < 10000; i++) DQPushFront(&q, DQPopRear(&q));
    while (!DQIsEmpty(&q)) printf("got %d\n", (int) DQPopFront(&q));
}
****/
