//
// $Id: Queue.h,v 1.22 2007/03/06 20:42:20 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004-2007
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#if !defined(QUEUE_OL_GUARD)
#define QUEUE_OL_GUARD

#include <ObjectiveLib/Functional.h>
#include <ObjectiveLib/Iterator.h>

@class OLDeque;
@class OLVector;

/**
 * @class OLQueue Queue.h ObjectiveLib/Queue.h
 *
 * A first-in-first-out collection. Elements may be added to the back of the queue and
 * removed from the front. The element at the #back of the queue is the one added
 * most recently, while the element at the #front is the one added least recently.
 * The queue class just provides a restricted view of the underlying container
 * OLDeque for occasions when only the simple functionality of a FIFO collection
 * is required. It is not a true @ref Containers "container" because it does not
 * provide @ref Iterators "iterators", so it cannot be used with generic
 * @ref Algorithms "algorithms".
 *
 * @sa OLDeque, OLStack
 *
 * @ingroup Containers
 */
@interface OLQueue :
#if defined(OL_NO_OPENSTEP)
    Object <OLStreamable>
#else
    NSObject <OLStreamable, NSCopying, NSCoding>
#endif
{
@protected
    /**
     * The container that provides the underlying data structure
     */
    OLDeque* deque;
}

/**
 * Create and return a new queue.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return a new queue
 */
+ (id) queue;

/**
 * Create and return a new queue. The queue is initialized with
 * the contents of @a right.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param right the queue to copy into the new one
 * @return a new queue
 */
+ (id) queueWithQueue: (OLQueue*)right;

/**
 * @name Initializers and Deallocators
 */
/* @{ */
/**
 * Initialize the queue. The queue is initially empty.
 *
 * @return a reference to this queue
 */
- (id) init;

#if !defined(OL_NO_OPENSTEP)
/**
 * Initialize the queue. This initializer creates a new queue
 * from an archive and returns it.
 *
 * @post The queue returned will be identical to the queue
 * saved to the archive using the #encodeWithCoder: message.
 *
 * @param decoder the coder which will decode the archived queue
 * @return a reference to this queue
 */
- (id) initWithCoder: (NSCoder*)decoder;
#endif

- (id) initWithObjectInStream: (OLObjectInStream*)stream;

/**
 * Initialize the queue. All objects are copied from
 * @a queue into this queue.
 *
 * @param queue the queue that should be copied
 * @return a reference to this queue
 */
- (id) initWithQueue: (OLQueue*)queue;

/**
 * Finalize the queue and deallocate any allocated memory.
 */
#if defined(OL_NO_OPENSTEP)
- (id) free;
#else
- (void) dealloc;
#endif
/* @} */

/**
 * Return the last item. The most recently added element is returned.
 *
 * @note The behavior of this message is undefined if the queue is empty.
 *
 * @return the most recently added item
 */
- (id) back;

/**
 * Compare this queue to another object. If the other object is of type OLQueue,
 * the underlying deque of this object will compared to the underlying deque
 * of @a other using the OLDeque#compare: method.
 *
 * @param other the object with which to compare this one
 * @return a value greater than, equal to, or less than zero accoringly as this object
 * is greater than, equal to, or less than @a other
 */
- (int) compare: (id)other;

#if defined(OL_NO_OPENSTEP)
/**
 * Make a copy of this queue.
 *
 * @return the copy
 */
- (id) copy;
#else
/**
 * Make a copy of this queue allocating memory from the given zone.
 *
 * @param zone the zone from which to allocate memory
 * @return the copy
 */
- (id) copyWithZone: (NSZone*)zone;
#endif

/**
 * Test whether the queue is empty.
 *
 * @return YES if the queue is empty, NO otherwise
 */
- (BOOL) empty;

#if !defined(OL_NO_OPENSTEP)
/**
 * Encode the queue. The queue is saved to an archive using @a encoder. The queue
 * will be retrieved from the archive using the initializer #initWithCoder:.
 *
 * @param encoder the coder which will save the bit queue to the archive
 */
- (void) encodeWithCoder: (NSCoder*)encoder;
#endif

/**
 * Return the least recently added item. The element at the front of the queue is
 * the one that will be removed by the message #pop.
 *
 * @note The behavior of this message is undefined if the queue is empty.
 *
 * @return the least recently added item
 */
- (id) front;

/**
 * Return whether this queue is equal to another one. Two queues are considered equal
 * if they both contain the same number of objects that all return YES to the message
 * @c isEqual: and they are in the same order.
 *
 * @param object the object to test
 * @return YES if @a object is equal to this queue
 */
- (BOOL) isEqual: (id)object;

/**
 * Remove the item at the front of the queue. The element returned by the message
 * #front is removed from the queue.
 *
 * @pre The queue cannot be empty.
 */
- (void) pop;

/**
 * Add an item to the back of the queue. The element @a object will be added to the
 * back of the queue and can be accessed with the message #back.
 *
 * @param object the element to add
 */
- (void) push: (id)object;

/**
 * Return the number of elements in the queue.
 *
 * @return the number of elements
 */
- (unsigned) size;

- (void) writeSelfToStream: (OLObjectOutStream*)stream;

@end

/**
 * @class OLPriorityQueue Queue.h ObjectiveLib/Queue.h
 *
 * A queue that orders its elements by priority. This is similar to a
 * @ref OLQueue "first-in-first-out queue" in that elements are pushed to
 * the back of the queue and retrieved from the head of the queue. The difference
 * is that rather than ordering elements according to the order in which they
 * were placed in the queue, the priority queue orders elements according
 * to a @ref Functors "function object" that determines each elements priority.
 * Thus, the priority queue's #top message retrieves the element with the
 * highest priority regardless of when that element was placed in the queue.
 * As with OLQueue, OLPriorityQueue is not a true @ref Containers "container"
 * because it does not provide @ref Iterators "iterators", and therefore cannot
 * be used with generic @ref Algorithms "algorithms".
 *
 * The @ref Functors "function object" that orders the elements considers
 * the highest priority element to be the one that fails the comparison when
 * compared to all other elements in the queue. Therefore, to create a queue
 * where the largest elements are considered the ones of highest priority,
 * the function OLLess should be used.
 *
 * @sa OLQueue
 *
 * @ingroup Containers
 */
@interface OLPriorityQueue :
#if defined(OL_NO_OPENSTEP)
    Object <OLStreamable>
#else
    NSObject <OLStreamable, NSCopying, NSCoding>
#endif
{
@protected
    /**
     * The container that provides that underlying data structure
     */
    OLVector*                                   vector;

    /**
     * The @ref Functors "function object" used to order the queue
     */
    OLStreamableFunctor<OLBoolBinaryFunction>*  predicate;
}

/**
 * Create and return a new queue. An instance of OLLess is used
 * to order the items by priority.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return a new queue
 */
+ (id) priorityQueue;

/**
 * Create and return a new queue. The queue is initialized with
 * the contents of the range <tt>[first, last)</tt>. An instance of OLLess is used
 * to order the items by priority.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param first the first in the range of elements to insert
 * @param last one beyond the last in the range of elements to insert
 * @return a new queue
 */
+ (id) priorityQueueFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Create and return a new queue. The queue is initialized with
 * the contents of the range <tt>[first, last)</tt>, and @a pred is used to order
 * the items by priority.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param first the first in the range of elements to insert
 * @param last one beyond the last in the range of elements to insert
 * @param pred the @ref Functors "function object" used to order elements
 * @return a new queue
 */
+ (id) priorityQueueFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (OLStreamableFunctor<OLBoolBinaryFunction>*)pred;

/**
 * Create and return a new queue. The @ref Functors "function object"
 * @a pred is used to order the items by priority.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param pred the @ref Functors "function object" used to order elements
 * @return a new queue
 */
+ (id) priorityQueueWithPredicate: (OLStreamableFunctor<OLBoolBinaryFunction>*)pred;

/**
 * Create and return a new queue. The queue is initialized with
 * the contents of the priority queue @a right.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param right the priority queue to copy into the new one
 * @return a new queue
 */
+ (id) priorityQueueWithPriorityQueue: (OLPriorityQueue*)right;

/**
 * @name Initializers and Deallocators
 */
/* @{ */
/**
 * Initialize the queue. The queue is initially empty and the comparison function OLLess.
 *
 * @return a reference to this queue
 */
- (id) init;

/**
 * Initialize the queue. The queue uses the comparison
 * function OLLess. All elements in the range <tt>[first, last)</tt> are inserted
 * into the queue.
 *
 * @param first the first in the range of elements to insert
 * @param last one beyond the last in the range of elements to insert
 * @return a reference to this queue
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Initialize the queue. The queue uses the comparison
 * function @a pred. All elements in the range <tt>[first, last)</tt> are inserted
 * into the queue.
 *
 * @note The predicate @a pred will order the queue in reverse order. Thus, the
 * highest priority element is the one that fails to satisfy the predicate when
 * compared to all other objects. Therefore, to order a queue in ascending order,
 * with the largest element considered the highest priority, use the predicate
 * OLLess.
 *
 * @param first the first in the range of elements to insert
 * @param last one beyond the last in the range of elements to insert
 * @param pred the @ref Functors "function object" used to order elements
 * @return a reference to this queue
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (OLStreamableFunctor<OLBoolBinaryFunction>*)pred;

#if !defined(OL_NO_OPENSTEP)
/**
 * Initialize the queue. This initializer creates a new queue
 * from an archive and returns it.
 *
 * @post The queue returned will be identical to the queue
 * saved to the archive using the #encodeWithCoder: message.
 *
 * @param decoder the coder which will decode the archived queue
 * @return a reference to this queue
 */
- (id) initWithCoder: (NSCoder*)decoder;
#endif

- (id) initWithObjectInStream: (OLObjectInStream*)stream;

/**
 * Initialize the queue. The queue is empty and uses 
 * the @ref Functors "function object" @a pred to order elements.
 *
 * @param pred the @ref Functors "function object" used to order elements
 * @return a reference to this queue
 */
- (id) initWithPredicate: (OLStreamableFunctor<OLBoolBinaryFunction>*)pred;

/**
 * Initialize the queue. The comparison function and all elements are
 * copied from @a right into this queue.
 *
 * @param right the queue to copy
 * @return a reference to this queue
 */
- (id) initWithPriorityQueue: (OLPriorityQueue*)right;

/**
 * Finalize the queue and deallocate any allocated memory.
 */
#if defined(OL_NO_OPENSTEP)
- (id) free;
#else
- (void) dealloc;
#endif
/* @} */

/**
 * Compare this queue to another object. If the other object is of type OLPriorityQueue,
 * the underlying vector of this object will compared to the underlying vector
 * of @a other using the OLVector#compare: method.
 *
 * @param other the object with which to compare this one
 * @return a value greater than, equal to, or less than zero accoringly as this object
 * is greater than, equal to, or less than @a other
 */
- (int) compare: (id)other;

#if defined(OL_NO_OPENSTEP)
/**
 * Make a copy of this queue.
 *
 * @return the copy
 */
- (id) copy;
#else
/**
 * Make a copy of this queue allocating memory from the given zone.
 *
 * @param zone the zone from which to allocate memory
 * @return the copy
 */
- (id) copyWithZone: (NSZone*)zone;
#endif

/**
 * Test whether the queue is empty.
 *
 * @return YES if the queue is empty, NO otherwise
 */
- (BOOL) empty;

#if !defined(OL_NO_OPENSTEP)
/**
 * Encode the queue. The queue is saved to an archive using @a encoder. The queue
 * will be retrieved from the archive using the initializer #initWithCoder:.
 *
 * @param encoder the coder which will save the bit queue to the archive
 */
- (void) encodeWithCoder: (NSCoder*)encoder;
#endif

/**
 * Return whether this queue is equal to another one. Two queues are considered equal
 * if they both contain the same number of objects that all return YES to the message
 * @c isEqual: and they are in the same order.
 *
 * @param object the object to test
 * @return YES if @a object is equal to this queue
 */
- (BOOL) isEqual: (id)object;

/**
 * Remove the highest priority item. The element returned by the message
 * #top is removed from the queue.
 *
 * @pre The queue cannot be empty.
 */
- (void) pop;

/**
 * Add an item to the queue. The element @a object will be added to the
 * queue at whatever position is determined by #predicate.
 *
 * @param object the element to add
 */
- (void) push: (id)object;

/**
 * Return the number of elements in the queue.
 *
 * @return the number of elements
 */
- (unsigned) size;

/**
 * Return the highest priority element. This is the same element that will be
 * removed by the message #pop.
 *
 * @return the highest priority element
 */
- (id) top;

- (void) writeSelfToStream: (OLObjectOutStream*)stream;

@end

#endif
