//
// $Id: Set.m,v 1.19 2005/05/18 06:46:54 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.
 */

#import "Set.h"
#import "Tree.h"
#import "ContainerCoder.h"
#import "Constants.h"
#import <Foundation/NSCoder.h>

@implementation OLSet

+ (id) set
{
    OL_BEGIN_AUTO_CTOR(OLSet)
        init
    OL_END_AUTO_CTOR;
}

+ (id) setFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OL_BEGIN_AUTO_CTOR(OLSet)
        initFrom: first to: last
    OL_END_AUTO_CTOR;
}

+ (id) setWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp
{
    OL_BEGIN_AUTO_CTOR(OLSet)
        initWithCompare: comp
    OL_END_AUTO_CTOR;
}

+ (id) setWithOLSet: (OLSet*)right
{
    OL_BEGIN_AUTO_CTOR(OLSet)
        initWithOLSet: right
    OL_END_AUTO_CTOR;
}

- (id) init
{
    [super init];
    tree = [[[OLAllocator defaultAllocator] allocateObject: [OLTree class]] init];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [self init];
    [self insertFrom: first to: last];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last compare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp
{
    [self initWithCompare: comp];
    [self insertFrom: first to: last];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last compare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp allocator: (OLAllocator*)alloc
{
    [self initWithCompare: comp allocator: alloc];
    [self insertFrom: first to: last];
    return self;
}

- (id) initWithCoder: (NSCoder*)decoder
{
    OLAllocator* alloc = [OLContainerCoder decodeAllocatorWithCoder: decoder];
    OLCodableFunctor<OLBoolBinaryFunction>* comp;
    id* array;
    OLArrayIterator* first;
    OLArrayIterator* last;
    unsigned count;

    if ([decoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [decoder allowsKeyedCoding])
    {
        comp = [decoder decodeObjectForKey: COMPARATOR_KEY];
    }
    else
    {
        comp = [decoder decodeObject];
    }
    array = [OLContainerCoder decodeWithCoder: decoder allocator: alloc returnedCount: &count];
    first = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array];
    last = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array + count];
    [self initFrom: first to: last compare: comp allocator: alloc];
    [alloc deallocateBytes: array];
    [alloc deallocateObject: first];
    [alloc deallocateObject: last];
    return self;
}

- (id) initWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp
{
    [super init];
    tree = [(OLTree*)[[OLAllocator defaultAllocator] allocateObject: [OLTree class]]
        initWithCompare: comp];
    return self;
}

- (id) initWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp allocator: (OLAllocator*)alloc
{
    [super init];
    tree = [(OLTree*)[alloc allocateObject: [OLTree class]]
        initWithCompare: comp allocator: alloc];
    return self;
}

- (id) initWithOLSet: (OLSet*)set
{
    [super init];
    tree = [[[set allocator] allocateObject: [OLTree class]] initWithTree: set->tree];
    return self;
}

- (void) dealloc
{
    [[tree allocator] deallocateObject: tree];
    [super dealloc];
}

- (OLAssociativeIterator*) begin
{
    return [tree begin];
}

- (void) clear
{
    [tree clear];
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    return [[alloc allocateObject: [OLSet class]] initWithOLSet: self];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLSet allocWithZone: zone] initWithOLSet: self];
}

- (unsigned) count: (id)key
{
    return [tree count: key];
}

- (BOOL) empty
{
    return [tree empty];
}

- (void) encodeWithCoder: (NSCoder*)encoder
{
    OLAssociativeIterator* first = [tree beginImpl];

    [OLContainerCoder encodeAllocator: [tree allocator] withCoder: encoder];
    if ([encoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [encoder allowsKeyedCoding])
    {
        [encoder encodeObject: [tree keyComp] forKey: COMPARATOR_KEY];
    }
    else
    {
        [encoder encodeObject: [tree keyComp]];
    }
    [OLContainerCoder encodeFrom: first count: [tree size] withCoder: encoder
        allocator: [tree allocator]];
    [[tree allocator] deallocateObject: first];
}

- (OLAssociativeIterator*) end
{
    return [tree end];
}

- (OLPair*) equalRange: (id)key
{
    return [tree equalRange: key];
}

- (void) erase: (OLAssociativeIterator*)where
{
    [tree erase: where];
}

- (void) eraseFrom: (OLAssociativeIterator*)first to: (OLAssociativeIterator*)last
{
    [tree eraseFrom: first to: last];
}

- (unsigned) eraseKey: (id)key
{
    return [tree eraseKey: key];
}

- (OLAssociativeIterator*) find: (id)key
{
    return [tree find: key];
}

- (OLAllocator*) allocator
{
    return [tree allocator];
}

- (id) insert: (id)object
{
    return [tree insertUnique: object];
}

- (OLAssociativeIterator*) insertAt: (OLAssociativeIterator*)where value: (id)object
{
    return [tree insertUniqueAt: where value: object];
}

- (void) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [tree insertUniqueFrom: first to: last];
}

- (BOOL) isEqual: (id)object
{
    return [object isKindOfClass: [OLSet class]] &&
           [tree isEqualUnique: ((OLSet*)object)->tree];
}

- (OLCodableFunctor<OLBoolBinaryFunction>*) keyComp
{
    return [tree keyComp];
}

- (OLAssociativeIterator*) lowerBound: (id)key
{
    return [tree lowerBound: key];
}

- (unsigned) maxSize
{
    return [tree maxSize];
}

- (OLReverseBidiIterator*) rbegin
{
    return [tree rbegin];
}

- (OLReverseBidiIterator*) rend
{
    return [tree rend];
}

- (unsigned) size
{
    return [tree size];
}

- (void) swap: (OLSet*)right
{
    [tree swap: right->tree];
}

- (OLAssociativeIterator*) upperBound: (id)key
{
    return [tree upperBound: key];
}

- (OLCodableFunctor<OLBoolBinaryFunction>*) valueComp
{
    return [tree keyComp];
}

@end

@implementation OLMultiSet

+ (id) multiSet
{
    OL_BEGIN_AUTO_CTOR(OLMultiSet)
        init
    OL_END_AUTO_CTOR;
}

+ (id) multiSetFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OL_BEGIN_AUTO_CTOR(OLMultiSet)
        initFrom: first to: last
    OL_END_AUTO_CTOR;
}

+ (id) multiSetWithCompare: (OLCodableFunctor<OLBoolBinaryFunction>*)comp
{
    OL_BEGIN_AUTO_CTOR(OLMultiSet)
        initWithCompare: comp
    OL_END_AUTO_CTOR;
}

+ (id) multiSetWithOLSet: (OLSet*)right
{
    OL_BEGIN_AUTO_CTOR(OLMultiSet)
        initWithOLSet: right
    OL_END_AUTO_CTOR;
}

- (id) insert: (id)object
{
    return [tree insertEqual: object];
}

- (OLAssociativeIterator*) insertAt: (OLAssociativeIterator*)where value: (id)object
{
    return [tree insertEqualAt: where value: object];
}

- (void) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [tree insertEqualFrom: first to: last];
}

- (BOOL) isEqual: (id)object
{
    return [object isKindOfClass: [OLMultiSet class]] &&
           [tree isEqualNonUnique: ((OLMultiSet*)object)->tree];
}

@end
