//
// $Id: SetTest.m,v 1.12 2005/05/18 06:46:56 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 "SetTest.h"
#import <ObjectiveLib/Set.h>
#import <ObjectiveLib/Vector.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSData.h>
#import <Foundation/NSArchiver.h>
#if defined(HAVE_KEYED_ARCHIVES)
#import <Foundation/NSKeyedArchiver.h>
#endif
#if defined(__NEXT_RUNTIME__)
#import <objc/objc-class.h>
#endif

@implementation SetTest

- (void) testCoding
{
    NSMutableData* data;
    NSArchiver* archiver;
    NSData* archData;
    OLSet* s1;
    OLSet* s2;
    NSNumber* v;
    int i;


    s1 = [[OLSet alloc] init];
    for (i = 0; i< 5000; i++)
    {
        v = [[NSNumber alloc] initWithInt: i];
        [s1 insert: v];
        [v release];
    }
    data = [[NSMutableData alloc] initWithCapacity: 5000];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    [archiver encodeRootObject: s1];
    [archiver release];
    s2 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![s1 isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The hash sets should be equal"];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: s1];
    s2 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![s1 isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The hash sets should be equal"];
    }
#endif
    [s1 release];
}

- (void) testConvenienceAllocators
{
    OLSet* s;
    OLSet* s2;
    NSNumber* num;

    s = [OLSet set];
    if (![s isMemberOfClass: [OLSet class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLSet class]->name, [s class]->name];
    }
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    num = [[NSNumber alloc] initWithInt: 1];
    [s insert: num];
    [num release];
    num = [[NSNumber alloc] initWithInt: 2];
    [s insert: num];
    [num release];
    num = [[NSNumber alloc] initWithInt: 3];
    [s insert: num];
    [num release];
    s2 = [OLSet setFrom: [s begin] to: [s end]];
    if (![s isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The sets should be equal"];
    }
    num = [[NSNumber alloc] initWithInt: 4];
    [s insert: num];
    [num release];
    num = [[NSNumber alloc] initWithInt: 5];
    [s insert: num];
    [num release];
    s2 = [OLSet setWithOLSet: s];
    if (![s isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The sets should be equal"];
    }
    s = [OLSet setWithCompare: [OLFunctor functorOfType: OLFunctorType_Greater]];
    if (![s isMemberOfClass: [OLSet class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLSet class]->name, [s class]->name];
    }
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    if (![[s keyComp] isMemberOfClass: [OLGreater class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLGreater class]->name, [[s keyComp] class]->name];
    }
}

- (void) testErase
{
    NSString* strs[] = { @"five", @"four", @"three", @"two" };
    int i;
    OLSet* s;
    OLAssociativeIterator* itor;
    OLAssociativeIterator* end;
    unsigned count;

    s = [[OLSet alloc] init];
    for (i = 0; i < 4; i++)
        [s insert: strs[i]];
    [s insert: @"one"];
    itor = [[[s begin] advance] advance];
    [self logMessage: "Going to erase \"%s\"", [[itor dereference] cString]];
    [s erase: itor];
    if ([s size] != 4)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 4, but got %u", [s size]];
    }
    end = [s end];
    for (itor = [s begin], i = 0; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: strs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [strs[i] cString], [[itor dereference] cString]];
        }
    }
    [s erase: [s begin]];
    if ([s size] != 3)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 3, but got %u", [s size]];
    }
    end = [s end];
    for (itor = [s begin], i = 1; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: strs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [strs[i] cString], [[itor dereference] cString]];
        }
    }
    [s erase: [[s end] reverse]];
    if ([s size] != 2)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 2, but got %u", [s size]];
    }
    end = [s end];
    for (itor = [s begin], i = 1; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: strs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [strs[i] cString], [[itor dereference] cString]];
        }
    }
    count = [s eraseKey: @"three"];
    if (count != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %u", count];
    }
    if ([s size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %u", [s size]];
    }
    if (![[[s begin] dereference] isEqual: @"four"])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"four\", but got \"%s\"", [[s begin] dereference]];
    }
    [s eraseFrom: [s begin] to: [s end]];
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    for (i = 0; i < 4; i++)
        [s insert: strs[i]];
    [s clear];
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    [s release];
}

- (void) testFind
{
    NSString* strs[] = { @"one", @"two", @"three", @"four", @"five" };
    int i;
    OLSet* s;
    OLAssociativeIterator* itor;
    OLArrayIterator* ab;
    OLArrayIterator* ae;
    OLPair* p;

    s = [[OLSet alloc] init];
    ab = [[OLArrayIterator alloc] initWithPointer: strs];
    ae = [[OLArrayIterator alloc] initWithPointer: strs + 5];
    [s insertFrom: ab to: ae];
    [ab release];
    [ae release];
    for (i = 0; i < 5; i++)
    {
        itor = [s find: strs[i]];
        if (![[itor dereference] isEqual: strs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [strs[i] cString], [[itor dereference] cString]];
        }
    }
    itor = [s find: @"doggyboy"];
    if (![itor isEqual: [s end]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The item should not have been found"];
    }
    itor = [s lowerBound: strs[0]];
    if (![[itor dereference] isEqual: strs[0]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[0] cString], [[itor dereference] cString]];
    }
    itor = [[s upperBound: strs[4]] reverse];
    if (![[itor dereference] isEqual: strs[4]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[4] cString], [[itor dereference] cString]];
    }
    p = [s equalRange: strs[3]];
    if (![[[p first] dereference] isEqual: strs[3]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[3] cString], [[itor dereference] cString]];
    }
    if (![[[[p second] reverse] dereference] isEqual: strs[3]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[3] cString], [[itor dereference] cString]];
    }
    [s release];
}

- (void) testInsert
{
    OLSet* s;
    NSString* strs[] = { @"one", @"two", @"three", @"four", @"five" };
    NSString* orderedStrs[] = { @"five", @"four", @"one", @"three", @"two" };
    int i;
    OLPair* p;
    OLVector* v;
    OLAssociativeIterator* itor;
    OLAssociativeIterator* end;

    s = [[OLSet alloc] init];
    p = [s insert: strs[0]];
    if ([s size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %u", [s size]];
    }
    if (![[p second] boolValue])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    if (![[[p first] dereference] isEqual: strs[0]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[0] cString], [[[p first] dereference] cString]];
    }
    p = [s insert: strs[0]];
    if ([s size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %u", [s size]];
    }
    if ([[p second] boolValue])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    if (![[[p first] dereference] isEqual: strs[0]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[0] cString], [[[p first] dereference] cString]];
    }
    v = [[OLVector alloc] init];
    for (i = 1; i < 4; i++)
        [v pushBack: strs[i]];
    [s insertFrom: [v begin] to: [v end]];
    [v release];
    if ([s size] != 4)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 4, but got %u", [s size]];
    }
    end = [s end];
    for (itor = [s begin], i = 1; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: orderedStrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [orderedStrs[i] cString], [[itor dereference] cString]];
        }
    }
    itor = [s insertAt: [[s begin] advance] value: strs[4]];
    if ([s size] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [s size]];
    }
    if (![[itor dereference] isEqual: strs[4]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [strs[0] cString], [[itor dereference] cString]];
    }
    end = [s end];
    for (itor = [s begin], i = 0; ![itor isEqual: end]; [itor advance], i++)
    {
        [self logMessage: "[%i] %s", i, [[itor dereference] cString]];
        if (![[itor dereference] isEqual: orderedStrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [orderedStrs[i] cString], [[itor dereference] cString]];
        }
    }
    [s release];
}

- (void) testInitializers
{
    OLSet* s;
    OLGreater* greater;
    OLVector* v;
    NSString* strs[] = { @"one", @"two", @"three", @"four", @"five" };
    NSString* orderedStrs[] = { @"five", @"four", @"one", @"three", @"two" };
    int i;
    OLAssociativeIterator* b;
    OLAssociativeIterator* e;
    OLSet* s2;

    s = [[OLSet alloc] init];
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    [s release];
    greater = [[OLGreater alloc] init];
    s = [[OLSet alloc] initWithCompare: greater];
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set should be empty"];
    }
    if ([s keyComp] != greater)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The key comparison object is wrong"];
    }
    [s release];
    v = [[OLVector alloc] init];
    for (i = 0; i < 5; i++)
        [v pushBack: strs[i]];
    s = [[OLSet alloc] initFrom: [v begin] to: [v end]];
    if ([s size] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [s size]];
    }
    for (b = [s begin], e = [s end], i = 0; ![b isEqual: e]; [b advance], i++)
    {
        if (![[b dereference] isEqual: orderedStrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [orderedStrs[i] cString], [[b dereference] cString]];
        }
    }
    [s release];
    s = [[OLSet alloc] initFrom: [v begin] to: [v end] compare: greater];
    [v release];
    [greater release];
    if ([s size] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [s size]];
    }
    for (b = [s begin], e = [s end], i = 4; ![b isEqual: e]; [b advance], i--)
    {
        if (![[b dereference] isEqual: orderedStrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [orderedStrs[i] cString], [[b dereference] cString]];
        }
    }
    s2 = [[OLSet alloc] initWithOLSet: s];
    if ([s2 size] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5, but got %u", [s2 size]];
    }
    for (b = [s2 begin], e = [s2 end], i = 4; ![b isEqual: e]; [b advance], i--)
    {
        if (![[b dereference] isEqual: orderedStrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [orderedStrs[i] cString], [[b dereference] cString]];
        }
    }
    if (![s isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The sets must be equal"];
    }
    [s release];
    [s2 release];
}

- (void) testIterators
{
    OLSet* s;
    int nums[] = { 4, 3, 5, 1, 2 };
    int orderedNums[] = { 1, 2, 3, 4, 5 };
    int i;
    OLAssociativeIterator* itor;
    OLAssociativeIterator* end;
    OLReverseBidiIterator* rtor;
    OLReverseBidiIterator* rend;

    s = [[OLSet alloc] init];
    for (i = 0; i < 5; i++)
        [s insert: [NSNumber numberWithInt: nums[i]]];
    end = [s end];
    for (itor = [s begin], i = 0; ![itor isEqual: end]; [itor advance], i++)
    {
        if ([[itor dereference] intValue] != orderedNums[i])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %i, but got %i",
                orderedNums[i], [[itor dereference] intValue]];
        }
    }
    [itor reverse];
    for (i = 4; i >= 0; i--, [itor reverse])
    {
        if ([[itor dereference] intValue] != orderedNums[i])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %i, but got %i",
                orderedNums[i], [[itor dereference] intValue]];
        }
    }
    rend = [s rend];
    for (rtor = [s rbegin], i = 4; ![rtor isEqual: rend]; [rtor advance], i--)
    {
        if ([[rtor dereference] intValue] != orderedNums[i])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %i, but got %i",
                orderedNums[i], [[rtor dereference] intValue]];
        }
    }
    [rtor reverse];
    for (i = 0; i < 5; i++, [rtor reverse])
    {
        if ([[rtor dereference] intValue] != orderedNums[i])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected %i, but got %i",
                orderedNums[i], [[rtor dereference] intValue]];
        }
    }
    [s release];
}

- (void) testProperties
{
    NSString* strs[] = { @"one", @"two", @"three" };
    OLSet* s;
    OLSet* s2;
    int i;

    s = [[OLSet alloc] init];
    if (![s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set is empty"];
    }
    for (i = 0; i < 3; i++)
        [s insert: strs[i]];
    if ([s count: strs[0]] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %i", [s count: strs[0]]];
    }
    if ([s count: @"doggyboy"] != 0)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [s count: @"doggyboy"]];
    }
    if ([s empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The set is not empty"];
    }
    if (![[s allocator] isEqual: [OLAllocator defaultAllocator]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The allocator is incorrect"];
    }
    if ([s allocator] != [OLAllocator defaultAllocator])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The allocator is incorrect"];
    }
    s2 = [[OLSet alloc] init];
    if ([s isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The sets are not equal"];
    }
    if (![s2 size] == 0)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [s size]];
    }
    [s2 insertFrom: [s begin] to: [s end]];
    if (![s isEqual: s2])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The sets are equal"];
    }
    if ([s maxSize] != [s2 maxSize])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "All sets must have the same max size"];
    }
    [self logMessage: "The max size of a set is %u", [s maxSize]];
    if (![s size] == 3)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 3, but got %i", [s size]];
    }
    if ([s valueComp] != [s keyComp])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Value comp and key comp should point to the same object"];
    }
    [s release];
    [s2 release];
}

- (void) testSwap
{
    OLSet* s;
    OLSet* s2;
    NSString* strs[] = { @"one", @"two", @"three" };
    NSString* ostrs[] = { @"one", @"three", @"two" };
    NSString* strs2[] = { @"four", @"five", @"six" };
    NSString* ostrs2[] = { @"five", @"four", @"six" };
    int i;
    OLArrayIterator* ab;
    OLArrayIterator* ae;
    OLAssociativeIterator* itor;
    OLAssociativeIterator* end;

    ab = [[OLArrayIterator alloc] initWithPointer: strs];
    ae = [[OLArrayIterator alloc] initWithPointer: strs + 3];
    s = [[OLSet alloc] initFrom: ab to: ae];
    [ab release];
    [ae release];
    ab = [[OLArrayIterator alloc] initWithPointer: strs2];
    ae = [[OLArrayIterator alloc] initWithPointer: strs2 + 3];
    s2 = [[OLSet alloc] initFrom: ab to: ae];
    [ab release];
    [ae release];
    [s swap: s2];
    if (![s size] == 3)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 3, but got %i", [s size]];
    }
    if (![s2 size] == 3)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 3, but got %i", [s2 size]];
    }
    end = [s end];
    for (itor = [s begin], i = 0; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: ostrs2[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [ostrs2[i] cString], [[itor dereference] cString]];
        }
    }
    end = [s2 end];
    for (itor = [s2 begin], i = 0; ![itor isEqual: end]; [itor advance], i++)
    {
        if (![[itor dereference] isEqual: ostrs[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                [ostrs[i] cString], [[itor dereference] cString]];
        }
    }
    [s release];
    [s2 release];
}

@end
