//
// $Id: List.h,v 1.34 2007/03/06 20:42:19 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(LIST_OL_GUARD)
#define LIST_OL_GUARD

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

/**
 * @internal A node in a linked list.
 */
@class OLListNode;

/**
 * @class OLListIterator List.h ObjectiveLib/List.h
 *
 * An iterator for traversing linked lists. This is a basic bidirectional
 * iterator that is specialized to understand how lists work.
 *
 * @sa OLList
 *
 * @ingroup Iterators
 */
@interface OLListIterator : OLBidirectionalIterator
{
@private
    /**
     * The node to which this iterator currently points. This member is private
     * because list nodes are for internal use and have no public API.
     */
    OLListNode*     node;
}

- (id) advance;
- (id) assign: (id)object;
#if defined(OL_NO_OPENSTEP)
- (id) copy;
#else
- (id) copyWithZone: (NSZone*)zone;
#endif
- (id) dereference;
- (BOOL) isEqual: (id)object;
- (id) reverse;

@end

/**
 * @class OLList List.h ObjectiveLib/List.h
 *
 * A doubly linked list. A sequence of elements that is a bidirectional linked list, and
 * thus may be traversed both forwards and backwards. An important feature of lists is
 * that insertion and removal do not affect existing iterators unless the iterator points
 * directly to an object that has been removed. Due to the fact that some algorithms from
 * OLAlgorithm require random access iterators and lists only provide bidirectional
 * iterators, some messages are provided in OLList to make up for the unavailability of
 * the functionality in OLAlgorithm. Examples of these messages include #sort, #unique
 * and #merge:.
 *
 * @sa OLListIterator
 *
 * @ingroup Containers
 */
@interface OLList :
#if defined(OL_NO_OPENSTEP)
    Object <OLBackInserter, OLFrontInserter, OLInserter, OLStreamable>
#else
    NSObject <OLBackInserter, OLFrontInserter, OLInserter, OLStreamable, NSCopying, NSCoding>
#endif
{
@protected
    /**
     * The head of the list
     */
    OLListNode*     node;
}

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

/**
 * Create and return a new list. The list is initialized with the contents of
 * the range <tt>[first, last)</tt>.
 *
 * @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 list
 */
+ (id) listFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Create and return a new list. The list 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 list with which to initialize the new one
 * @return a new list
 */
+ (id) listWithList: (OLList*)right;

/**
 * Create and return a new list. The list is initialized with a size of @a size
 * and is filled with @a value.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param size the number of elements to insert
 * @param value the object to be copied
 * @return a new list
 */
+ (id) listWithSize: (unsigned)size filledWith: (id)value;

/**
 * @name Initializers and Deallocators
 */
/* @{ */
/**
 * Initialize the list. The resulting list will be empty.
 *
 * @return a reference to this list
 */
- (id) init;

/**
 * Initialize the list. The list inserts
 * all elements referred to in the range <tt>[first, last)</tt>.
 *
 * @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 list
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

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

/**
 * Initialize the list. All the elements of @a list are copied
 * into the current list.
 *
 * @param list the list to copy
 * @return a reference to this list
 */
- (id) initWithList: (OLList*)list;

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

/**
 * Initialize the list. The initial size of the list will be @a size, and @a value
 * will be copied into the list @a size times.
 *
 * @param size the number of elements to insert
 * @param value the object to be copied
 * @return a reference to this list
 */
- (id) initWithSize: (unsigned)size filledWith: (id)value;

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

/**
 * Assign objects to the list. The current contents of the list are removed,
 * and the list is filled with @a count elements of @a value.
 *
 * @param count the number of elements to assign
 * @param value the object to be copied into the list
 */
- (void) assign: (unsigned)count filledWith: (id)value;

/**
 * Assign a range of objects to the list. The current contents of the list are
 * removed, and all elements in the range <tt>[first, last)</tt> are inserted
 * into the list.
 *
 * @param first the first in the range of objects to assign
 * @param last one beyond the last in the range of objects to assign
 */
- (void) assignFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Return the last object in the list. A reference to the object is returned, though
 * the list still owns the object in question.
 *
 * @note The results of this message are undefined if the list is empty.
 *
 * @return the last object
 */
- (id) back;

/**
 * Return an iterator pointing to the beginning of the sequence.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return an iterator pointing to the first element
 */
- (OLListIterator*) begin;

/**
 * Remove all elements.
 */
- (void) clear;

/**
 * Compare this list to another object. If the other object is of type OLList, each
 * of the contained objects is compared to the corresponding object in @a other by
 * calling the @c 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 list.
 *
 * @return the copy
 */
- (id) copy;
#else
/**
 * Make a copy of this list 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 list is empty.
 *
 * @return YES if the list is empty, NO otherwise
 */
- (BOOL) empty;

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

/**
 * Return an iterator pointing to one position beyond the end of the sequence.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return an iterator pointing to one position beyond the last element
 */
- (OLListIterator*) end;

/**
 * Remove the element designated by @a where.
 *
 * @pre @a where must point to an element in this list.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param where an iterator designating the element to remove
 * @return an iterator indicating the next element in the sequence remaining after @a where
 */
- (OLListIterator*) erase: (OLListIterator*)where;

/**
 * Remove a range of elements. All elements in the range <tt>[first, last)</tt> will
 * be removed from the list.
 *
 * @pre @a first and @a last must refer to elements in this list.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param first the first in the range of elements to remove
 * @param last one position beyond the last in the range of elements to remove
 * @return an iterator indicating the next element in the sequence remaining after
 * removal of the range
 */
- (OLListIterator*) eraseFrom: (OLListIterator*)first to: (OLListIterator*)last;

/**
 * Return the first object in the list. A reference to the object is returned, though
 * the list still owns the object in question.
 *
 * @note The results of this message are undefined if the list is empty.
 *
 * @return the first object
 */
- (id) front;

/**
 * Insert a number of objects into the list. @a num objects of @a value will be inserted
 * just before the position referred to by @a where. Thus, it is valid to use an iterator returned
 * by #end as an argument.
 *
 * @pre @a where must refer to a position in this list
 *
 * @param where the position at which to insert the objects
 * @param num the number of objects to insert
 * @param value the object to be copied into the list
 */
- (void) insertAt: (OLListIterator*)where count: (unsigned)num filledWith: (id)value;

/**
 * Insert a range of objects into the list. All objects in the range <tt>[first, last)</tt>
 * are inserted just before the position referred to by @a where. Thus, it is valid to
 * use an iterator returned by #end as an argument.
 *
 * @param where the position at which to insert the objects
 * @param first the first in the range of objects to insert
 * @param last one position beyond the last in the range of objects to insert
 */
- (void) insertAt: (OLListIterator*)where from: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Insert an object into the list. The @a object will be inserted just before the
 * position referred to by @a where. Thus, it is valid to use an iterator returned
 * by #end as an argument.
 *
 * @pre @a where must refer to a position in this list.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param where the position at which to insert @a object
 * @param object the object to insert
 * @return an iterator pointing to the newly inserted object
 */
- (OLListIterator*) insertAt: (OLListIterator*)where value: (id)object;

/**
 * Return whether this list is equal to another one. Two lists 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 list
 */
- (BOOL) isEqual: (id)object;

/**
 * Return the maxiumum number of objects that can be stored in a list. This limit is
 * theoretical, meaning that there is no guarantee that you can insert this many
 * objects into any given list. The memory conditions of the run-time system play an
 * important role.
 *
 * @return the maximum number of objects for a list
 */
- (unsigned) maxSize;

/**
 * Merge the contents of two lists. Both lists must be sorted in ascending order.
 * That is, they must be sorted using the @ref Functors "function object" OLLess, or
 * with a function object that perfroms the same operation as OLLess. All elements
 * are removed from @a right and inserted in order into this list. This is a stable
 * merge in that if two elements are equal, the one from this list will precede the
 * one from @a right after the merge.
 *
 * @param right the list to merge into this list
 */
- (void) merge: (OLList*)right;

/**
 * Merge the contents of two lists. Both lists must be sorted in the order defined
 * by @a pred. All elements are removed from @a right and inserted in order into
 * this list. This is a stable merge in that if two elements are equal, the one
 * from this list will precede the one from @a right after the merge.
 *
 * @param right the list to merge into this list
 * @param pred the @ref Functors "function object" that defines the sorting order
 */
- (void) merge: (OLList*)right withOrder: (id<OLBoolBinaryFunction>)pred;

/**
 * Remove the last element in the list.
 *
 * @note If there are no elements in the list, then the behavior of this message
 * is undefined.
 */
- (void) popBack;

/**
 * Remove the first element in the list.
 *
 * @note If there are no elements in the list, then the behavior of this message
 * is undefined.
 */
- (void) popFront;

/**
 * Insert @a object at the end of the sequence.
 *
 * @param object the object to insert
 */
- (void) pushBack: (id)object;

/**
 * Insert @a object at the beginning of the sequence.
 *
 * @param object the object to insert
 */
- (void) pushFront: (id)object;

/**
 * Return a reverse iterator pointing to the end of the sequence. Advancing the returned
 * iterator will move backwards through the sequence to the point indicated by the
 * iterator returned by #rend.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return a reverse iterator for the end of the sequence
 */
- (OLReverseBidiIterator*) rbegin;

/**
 * Remove all objects equal to @a object. For each element in the list this message
 * performs an equivilance check using the message @c isEqual:, and if the check
 * succeeds the element is removed from the list.
 *
 * @pre @a object must respond to the message @c isEqual:.
 *
 * @param object the object with which to compare this list's members
 */
- (void) remove: (id)object;

/**
 * Remove all objects for which @a pred returns YES. For each element in the list
 * this message uses the @ref Functors "function object" @a pred to test the element.
 * If the @ref Functors "function object" returns YES, then the element is removed
 * from the list.
 *
 * @param pred the object to perform the check
 */
- (void) removeIf: (id<OLBoolUnaryFunction>)pred;

/**
 * Return a reverse iterator pointing to the beginning of the sequence. This iterator
 * indicates one position beyond the last position that may be referenced by a
 * reverse iterator.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return a reverse iterator for the beginning of the sequence
 */
- (OLReverseBidiIterator*) rend;

/**
 * Resize the list to @a newsize. If @a newsize is smaller than the current #size, then
 * the sequence will be truncated. If @a newsize is larger than the current #size,
 * then @a value will be inserted at the end of the list as many times as necessary
 * to fill the new size.
 *
 * @param count the new size of the list
 * @param value the value with which to fill new elements, if necessary
 */
- (void) resize: (unsigned)count filledWith: (id)value;

/**
 * Reverse the order of the list. The elements are placed in reverse order.
 */
- (void) reverse;

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

/**
 * Sort the list in ascending order. The @ref Functors "function object" OLLess is
 * used to compare elements in the list to establish the order.
 */
- (void) sort;

/**
 * Sort the list. The @ref Functors "function object" @a pred is used to compare
 * elements in the list to establish the order.
 *
 * @param pred the object used to compare elements
 */
- (void) sortWith: (id<OLBoolBinaryFunction>)pred;

/**
 * Splice another list into this one. All elements are removed from @a right and inserted
 * just before the element pointed to by @a where.
 *
 * @pre @a where must point to an element in this list.
 *
 * @param where the position at which to perform the splice
 * @param right the list to splice to this one
 */
- (void) spliceAt: (OLListIterator*)where list: (OLList*)right;

/**
 * Splice an element from another list into this one. The element pointed to by @a first
 * is removed from @a right and inserted into this list just before @a where.
 *
 * @pre @a where must point to an element in this list, and @a first must point to
 * an element in @a right.
 *
 * @param where the position at which to perform the splice
 * @param right the list that contains @a first
 * @param first the element in @a right to splice into this list
 */
- (void) spliceAt: (OLListIterator*)where list: (OLList*)right from: (OLListIterator*)first;

/**
 * Splice a range of elements into this list. The elements in the list @a right in the range
 * <tt>[first, last)</tt> are removed from @a right and inserted into this list just
 * before the element pointed to by @a where.
 *
 * @pre @a where must point to an element in this list, and @a first and @a last must refer
 * to a valid range in @a right.
 *
 * @param where the position at which to perform the splice
 * @param right the list that contains @a first and @a last
 * @param first the first element in the range to splice
 * @param last one position beyond the last element in the range to splice
 */
- (void) spliceAt: (OLListIterator*)where list: (OLList*)right from: (OLListIterator*)first to: (OLListIterator*)last;

/**
 * Swap this list with another one. All elements in @a right will be placed into
 * this list, and all elements in this list will be placed in @a right.
 *
 * @param right the list with which to swap this one
 */
- (void) swap: (OLList*)right;

/**
 * Remove all but one of each consecutive element in the list. The first of
 * equivilent elements is retained, and the others are removed. This message
 * will only be useful if the list is sorted first, as only consecutive elements are compared.
 */
- (void) unique;

/**
 * Remove all but one of each consecutive element for which @a pred returns YES. For
 * any two consecutive elements for which @a pred returns YES only the first is
 * retained in the list. All others are removed.
 *
 * @param pred the function to use to compare list elements
 */
- (void) uniqueWith: (id<OLBoolBinaryFunction>)pred;

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

@end

#endif
