
// $Id: Set.h,v 1.23 2005/07/12 19:15:37 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * 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(SET_OL_GUARD)
#define SET_OL_GUARD

#import <ObjectiveLib/Iterator.h>
#import <ObjectiveLib/Pair.h>
#import <ObjectiveLib/Functional.h>

/**
 * @internal The class that provides the red-black tree data structure.
 */
@class OLTree;

/**
 * @class OLSet Set.h ObjectiveLib/Set.h
 *
 * A set of unique objects. A set is a sorted associative container of objects. Each
 * object is unique; if an object is inserted into the set and another object that is
 * equal is already in the set, then the insertion will fail. Sets are, due to their
 * underlying data structure, sorted by definition, and the sorting order is determined
 * by a comparison function that is passed during intialization. If no function is
 * given to the set, then OLLess is used, thus ordering the set in ascending order.
 *
 * @sa OLAssociativeIterator
 *
 * @ingroup Containers
 */
@interface OLSet : NSObject <OLCopying, OLInserter, NSCoding>
{
@protected
    /**
     * The underlying data structure
     */
    OLTree* tree;
}

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. An instance of OLLess is
 * used to compare items for the purposes of sorting.
 *
 * @return a new set
 */
+ (id) set;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is initialized
 * with the contents of the range <tt>[first, last)</tt>. An instance of OLLess
 * is used to compare items for the purposes of sorting.
 *
 * @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 set
 */
+ (id) setFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is empty and the
 * comparison function @a comp is used to sort keys.
 *
 * @param comp the comparison function used to sort keys
 * @return a new set
 */
+ (id) setWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is initialized
 * with the contents of @a right.
 *
 * @param right the set to copy
 * @return a new set
 */
+ (id) setWithOLSet: (OLSet*)right;

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

/**
 * Initialize the set. The set uses the default memory allocator, the comparison
 * function OLLess for sorting and inserts all elements referred to in the
 * range <tt>[first, last)</tt>. It doesn't matter whether the elements are
 * already sorted using OLLess because the set will sort them, anyway.
 *
 * @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 set
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Initialize the set. The set uses the default memory allocator, the given comparison
 * function for sorting, @a comp, and inserts all elements referred to in the
 * range <tt>[first, last)</tt>. It doesn't matter whether the elements are
 * already sorted using @a comp because the set will sort them, anyway.
 *
 * @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 comp the function to use to sort the set
 * @return a reference to this set
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last compare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp;

/**
 * Initialize the set. The set uses given the memory allocator, @a alloc,
 * the given comparison function, @a comp, for sorting and inserts all
 * elements referred to in the range <tt>[first, last)</tt>.
 * It doesn't matter whether the elements are already sorted using @a comp
 * because the set will sort them, anyway.
 *
 * @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 comp the function to use to sort the set
 * @param alloc the memory allocator to use
 * @return a reference to this set
 */
- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last compare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp allocator: (OLAllocator*)alloc;

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

/**
 * Initialize the set. The set uses the default memory allocator and the given
 * comparison function, @a comp, for sorting. The set is initially empty.
 *
 * @param comp the comparison function used to maintain the sorting order
 * @return a reference to this set
 */
- (id) initWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp;

/**
 * Initialize the set. The set uses the given memory allocator, @a alloc,  and the given
 * comparison function, @a comp, for sorting. The set is initially empty.
 *
 * @param comp the comparison function used to maintain the sorting order
 * @param alloc the memory allocator to use
 * @return a reference to this set
 */
- (id) initWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp allocator: (OLAllocator*)alloc;

/**
 * Initialize the set. The comparison function, memory allocator and all the elements
 * are copied from @a set into this set.
 *
 * @param set the set to copy
 * @return a reference to this set
 */
- (id) initWithOLSet: (OLSet*)set;

/**
 * Finalize the set and deallocate any allocated memory.
 */
- (void) dealloc;
/* @} */

/**
 * Return an iterator pointing to the beginning of the sequence. If the platform and
 * the allocator both support autorelease, then the iterator will be scheduled for
 * autorelease before being returned.
 *
 * @return an iterator pointing to the first element
 */
- (OLAssociativeIterator*) begin;

/**
 * Remove all elements.
 */
- (void) clear;
- (id) copyWithAllocator: (OLAllocator*)alloc;
- (id) copyWithZone: (NSZone*)zone;

/**
 * Return the number of elements equal to @a key.
 *
 * @param key the key to search for in the set
 * @return the number of instances of @a key
 */
- (unsigned) count: (id)key;

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

/**
 * Encode the set. The set is saved to an archive using @a encoder. The set
 * 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;

/**
 * Return an iterator pointing to one position beyond the end of the sequence. If the platform and
 * the allocator both support autorelease, then the iterator will be scheduled for
 * autorelease before being returned.
 *
 * @return an iterator pointing to one position beyond the last element
 */
- (OLAssociativeIterator*) end;

/**
 * Return a range whose elements are equal to @a key. The returned pair contains two instances
 * of OLAssociativeIterator delimiting the range of elements. If the platform and allocator
 * both support autorelease, then the pair returned will be scheduled for
 * autorelease before being returned.
 *
 * @param key the value for which to search
 * @return a pair of OLAssociativeIterator instances that define the range of elements
 * in the set equal to @a key
 */
- (OLPair*) equalRange: (id)key;

/**
 * Remove the element designated by @a where.
 *
 * @pre @a where must point to an element in this set.
 *
 * @param where an iterator designating the element to remove
 */
- (void) erase: (OLAssociativeIterator*)where;

/**
 * Remove a range of elements. All elements in the range <tt>[first, last)</tt> will
 * be removed from the set.
 *
 * @pre @a first and @a last must refer to elements in this set.
 *
 * @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
 */
- (void) eraseFrom: (OLAssociativeIterator*)first to: (OLAssociativeIterator*)last;

/**
 * Erase all instances of @a key. The set is searched for @a key, all instances are removed
 * from the set and the number removed is returned.
 *
 * @param key the key to remove from the set
 * @return the number of elements removed
 */
- (unsigned) eraseKey: (id)key;

/**
 * Find an element. The element @a key is searched for in the set and an iterator to its
 * location is returned. If the element does not exist in the set, then the returned
 * iterator will be equal to the iterator returned by #end. If the platform and allocator
 * both support autorelease, then the iterator returned will be scheduled for
 * autorelease before being returned.
 *
 * @param key the key for which to search
 * @return an iterator pointing to the location of @a key, or an iterator equal to that
 * returned by #end if @a key does not exist
 */
- (OLAssociativeIterator*) find: (id)key;

/**
 * Return the memory allocator being used.
 *
 * @return the memory allocator that this set uses
 */
- (OLAllocator*) allocator;

/**
 * Insert an object into the set. An attempt is made to insert @a object into the set,
 * and an instance of OLPair is returned indicating the state of the insertion. The first
 * element in the returned pair is an instance of OLAssociativeIterator indicating the
 * position of @a object in the set, and the second element of the returned pair is
 * an object that responds to the message @c boolValue. The message @c boolValue will
 * return YES if the insertion was successful, or NO if not. If the platform and
 * the allocator both support autorelease, then the returned pair will be scheduled for
 * autorelease before being returned.
 *
 * @param object the element to insert
 * @return an instance of OLPair indicating the position of @a object in the set and
 * whether the insertion succeeded or failed
 */
- (id) insert: (id)object;

/**
 * Insert an object into the set. The @a object is inserted and the position indicated by
 * @a where is used as a hint about where in the set the @a object should be placed. There
 * is no guarantee that the @a object will ultimately placed anywhere close to @a where.
 * Note that the insertion may fail if the @a object is already in the set. An iterator is
 * returned that points to @a object. If the platform and the allocator both support
 * autorelease, then the iterator will be scheduled for autorelease before being returned.
 *
 * @param where a hint as to where in the set the @a object should be located
 * @param object the object to insert
 * @return the iterator indicating where object is now located in the set
 */
- (OLAssociativeIterator*) insertAt: (OLAssociativeIterator*)where value: (id)object;

/**
 * Insert a range of objects into the set. An attempt is made to insert all objects
 * in the range <tt>[first, last)</tt>, however there is no guarantee that any of
 * the elements in the range will actually be inserted if they already exist in the
 * set.
 *
 * @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) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Test whether another set is equal to this one. Two sets are considered equal if they
 * contain the same number of objects and the objects are in the same order and each
 * object is equal to the corresponding object in the other set. The two sets do not have
 * to use the same memory allocator to be considered equal.
 *
 * @param object the object to test
 * @return YES if @a object is equal to this set
 */
- (BOOL) isEqual: (id)object;

/**
 * Return the function used to compare keys in the set.
 *
 * @return the @ref Functors "function object" that is used to compare keys
 */
- (OLCodableFunctor<OLBoolBinaryFunction>*) keyComp;

/**
 * Find the lower bound of a given @a key. The lower bound is the first position in
 * the set at which @a key can be inserted without disturbing the sorting order. An
 * iterator pointing to the lower bound is returned. If the platform and
 * the allocator both support autorelease, then the iterator will be scheduled for
 * autorelease before being returned.
 *
 * @param key the key for which to find the lower bound
 * @return an iterator indicating the lower bound
 */
- (OLAssociativeIterator*) lowerBound: (id)key;

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

/**
 * 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. If the platform and the allocator both support autorelease,
 * then the iterator will be scheduled for autorelease before being returned.
 *
 * @return a reverse iterator for the end of the sequence
 */
- (OLReverseBidiIterator*) rbegin;

/**
 * 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. If the platform and the allocator both support autorelease,
 * then the iterator will be scheduled for autorelease before being returned.
 *
 * @return a reverse iterator for the beginning of the sequence
 */
- (OLReverseBidiIterator*) rend;

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

/**
 * Swap this set with another one. All elements in @a right will be placed into
 * this set, and all elements in this set will be placed in @a right. Unless the
 * allocators of the two sets are different, the implementation of this message
 * results in a very fast swap.
 *
 * @param right the set with which to swap this one
 */
- (void) swap: (OLSet*)right;

/**
 * Find the upper bound of a given @a key. The upper bound is the last position in
 * the set at which @a key can be inserted without disturbing the sorting order. An
 * iterator pointing to the upper bound is returned. If the platform and
 * the allocator both support autorelease, then the iterator will be scheduled for
 * autorelease before being returned.
 *
 * @param key the key for which to find the upper bound
 * @return an iterator indicating the upper bound
 */
- (OLAssociativeIterator*) upperBound: (id)key;

/**
 * Return the function used to compare values. This returns the same function as
 * the message #keyComp.
 *
 * @return the @ref Functors "function object" for comparing values
 */
- (OLCodableFunctor<OLBoolBinaryFunction>*) valueComp;

@end

/**
 * @class OLMultiSet Set.h ObjectiveLib/Set.h
 *
 * A set that allows multiple instances of objects. Multiset is identical to
 * set expect that as many instances of a given object may be inserted as desired.
 *
 * @sa OLSet, OLAssociativeIterator
 *
 * @ingroup Containers
 */
@interface OLMultiSet : OLSet
{
}

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. An instance of OLLess
 * is used to compare items for sorting.
 *
 * @return a new set
 */
+ (id) multiSet;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is initialized
 * with the contents of the range <tt>[first, last)</tt>. An instance of OLLess
 * is used to compare items for sorting.
 *
 * @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 set
 */
+ (id) multiSetFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is empty and the
 * comparison function @a comp is used to sort keys.
 *
 * @param comp the comparison function used to sort keys
 * @return a new set
 */
+ (id) multiSetWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp;

/**
 * Create and return a new set. The set is allocated using the default
 * allocator. If both the platform and the allocator support autorelease, then the
 * set will be autoreleased before being returned. The set is initialized
 * with the contents of @a right.
 *
 * @note The argument @a right may be an instance of OLSet or OLMultiSet.
 *
 * @param right the set to copy
 * @return a new set
 */
+ (id) multiSetWithOLSet: (OLSet*)right;

/**
 * Insert an object into the set. The object is inserted and an instance of
 * OLAssociativeIterator is returned indicating the position of @a object
 * in the set. If the platform and the allocator both support autorelease,
 * then the iterator will be scheduled for autorelease before being returned.
 *
 * @param object the element to insert
 * @return an instance of OLAssociativeIterator pointing to the newly inserted object
 */
- (id) insert: (id)object;
- (OLAssociativeIterator*) insertAt: (OLAssociativeIterator*)where value: (id)object;
- (void) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last;
- (BOOL) isEqual: (id)object;

@end

#endif
