/*
 * 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[] = "$Header: /ufs/repository/magic/utils/dqueue.c,v 1.2 2001/01/12 22:13:29 jsolomon Exp $";
#endif  not lint

#include <stdio.h>
#include "misc/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(q, capacity)
    DQueue *q;
    int capacity;
{
    if (capacity < 1) capacity = 1;
    MALLOC(ClientData *, q->dq_data, (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(q)
    DQueue *q;
{
    FREE((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(q, elem)
    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(q, elem)
    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(q)
    DQueue *q;
{
    if (q->dq_size == 0) return 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(q)
    DQueue *q;
{
    if (q->dq_size == 0) return 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(q, newSize)
    DQueue *q;
    int newSize;
{
    DQueue newq;

    if (newSize < q->dq_size) newSize = q->dq_size;
    DQInit(&newq, newSize);
    DQCopy(&newq, q);
    FREE((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(dst, src)
    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));
}
****/
