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

@interface OLBinaryNegate (CheatingMethods)

- (id) function;

@end

@implementation OLBinaryNegate (CheatingMethods)

- (id) function
{
    return fn;
}

@end

@interface OLUnaryNegate (CheatingMethods)

- (id) function;

@end

@implementation OLUnaryNegate (CheatingMethods)

- (id) function
{
    return fn;
}

@end

@interface OLBinder1st (CheatingMethods)

- (id) argument;
- (id) function;

@end

@implementation OLBinder1st (CheatingMethods)

- (id) argument
{
    return lft;
}

- (id) function
{
    return fn;
}

@end

@interface OLBinder2nd (CheatingMethods)

- (id) argument;
- (id) function;

@end

@implementation OLBinder2nd (CheatingMethods)

- (id) argument
{
    return rght;
}

- (id) function
{
    return fn;
}

@end

@interface OLBoolBinder1st (CheatingMethods)

- (id) argument;
- (id) function;

@end

@implementation OLBoolBinder1st (CheatingMethods)

- (id) argument
{
    return lft;
}

- (id) function
{
    return fn;
}

@end

@interface OLBoolBinder2nd (CheatingMethods)

- (id) argument;
- (id) function;

@end

@implementation OLBoolBinder2nd (CheatingMethods)

- (id) argument
{
    return rght;
}

- (id) function
{
    return fn;
}

@end

@implementation FunctionalTest

- (void) testArithmetic
{
    id result;
    NSNumber* num1 = [NSNumber numberWithInt: 16];
    NSNumber* num2 = [NSNumber numberWithInt: 4];
    NSNumber* dbl1 = [NSNumber numberWithDouble: 9.9];
    NSNumber* dbl2 = [NSNumber numberWithDouble: 3.0];
    OLDivides* divider;
    OLMinus* minus;
    OLMultiplies* multiplier;
    OLPlus* plus;
    OLModulus* mod;

    divider = [[OLDivides alloc] init];
    result = [divider performBinaryFunctionWithArg: num1 andArg: num2];
    if ([result intValue] != 4)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 4, but got %i", [result intValue]];
    }
    result = [divider performBinaryFunctionWithArg: dbl1 andArg: dbl2];
    if ([result doubleValue] != 9.9 / 3.0)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %f, but got %f", 9.9 / 3.0, [result doubleValue]];
    }
    [divider release];
    minus = [[OLMinus alloc] init];
    result = [minus performBinaryFunctionWithArg: num1 andArg: num2];
    if ([result intValue] != 12)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 12, but got %i", [result intValue]];
    }
    result = [minus performBinaryFunctionWithArg: dbl1 andArg: dbl2];
    if ([result doubleValue] != 6.9)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 6.9, but got %f", [result doubleValue]];
    }
    [minus release];
    multiplier = [[OLMultiplies alloc] init];
    result = [multiplier performBinaryFunctionWithArg: num1 andArg: num2];
    if ([result intValue] != 64)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 64, but got %i", [result intValue]];
    }
    result = [multiplier performBinaryFunctionWithArg: dbl1 andArg: dbl2];
    if ([result doubleValue] != 9.9 * 3.0)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %f, but got %f", 9.9 * 3.0, [result doubleValue]];
    }
    [multiplier release];
    plus = [[OLPlus alloc] init];
    result = [plus performBinaryFunctionWithArg: num1 andArg: num2];
    if ([result intValue] != 20)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 20, but got %i", [result intValue]];
    }
    result = [plus performBinaryFunctionWithArg: dbl1 andArg: dbl2];
    if ([result doubleValue] != 12.9)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 12.9, but got %f", [result doubleValue]];
    }
    [plus release];
    mod = [[OLModulus alloc] init];
    result = [mod performBinaryFunctionWithArg: num1 andArg: num2];
    if ([result intValue] != 0)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [result intValue]];
    }
    result = [mod performBinaryFunctionWithArg: dbl1 andArg: dbl2];
    if ([result doubleValue] != remainder(9.9, 3.0))
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected %f, but got %f", remainder(9.9, 3.0), [result doubleValue]];
    }
    [mod release];
}

- (void) testBinder
{
    NSNumber* n3 = [NSNumber numberWithInt: 3];
    NSNumber* n5 = [NSNumber numberWithInt: 5];
    OLMinus* minus;
    OLBinder1st* b1st;
    OLBinder2nd* b2nd;
    int rc;

    minus = [[OLMinus alloc] init];
    b1st = [[OLBinder1st alloc] initWithFunction: minus andLeftArg: n3];
    rc = [[b1st performUnaryFunctionWithArg: n5] intValue];
    if (rc != -2)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected -2, but got %i", rc];
    }
    [b1st release];
    b2nd = [[OLBinder2nd alloc] initWithFunction: minus andRightArg: n3];
    rc = [[b2nd performUnaryFunctionWithArg: n5] intValue];
    if (rc != 2)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 2, but got %i", rc];
    }
    [b2nd release];
    [minus release];
}

- (void) testBinderCoding
{
    NSArchiver* archiver;
    OLDivides* divides;
    NSMutableData* data;
    NSData* archData;
    NSNumber* arg;
    OLBinder1st* b1;
    OLBinder2nd* b2;

    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    divides = [[OLDivides alloc] init];
    arg = [[NSNumber alloc] initWithInt: 5];
    b1 = [[OLBinder1st alloc] initWithFunction: divides andLeftArg: arg];
    [b1 setAutoreleasesResults: NO];
    [divides release];
    [arg release];
    [archiver encodeRootObject: b1];
    [archiver release];
    [b1 release];
    b1 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[b1 allocator] isEqual: [OLAllocator defaultAllocator]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected the default allocator"];
    }
    if ([b1 autoreleasesResults])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO but got YES"];
    }
    if (![[b1 function] isMemberOfClass: [OLDivides class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLDivides, but got %s", [[b1 function] class]->name];
    }
    if ([[b1 argument] intValue] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5 but got %i", [[b1 argument] intValue]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    [b1 setAutoreleasesResults: YES];
    archData = [NSKeyedArchiver archivedDataWithRootObject: b1];
    b1 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[b1 allocator] isEqual: [OLAllocator defaultAllocator]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected the default allocator"];
    }
    if (![b1 autoreleasesResults])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES but got NO"];
    }
    if (![[b1 function] isMemberOfClass: [OLDivides class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLDivides, but got %s", [[b1 function] class]->name];
    }
    if ([[b1 argument] intValue] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5 but got %i", [[b1 argument] intValue]];
    }
#endif
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    divides = [[OLDivides alloc] init];
    arg = [[NSNumber alloc] initWithInt: 7];
    b2 = [[OLBinder2nd alloc] initWithFunction: divides andRightArg: arg];
    [b2 setAutoreleasesResults: NO];
    [divides release];
    [arg release];
    [archiver encodeRootObject: b2];
    [archiver release];
    [b2 release];
    b2 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[b2 allocator] isEqual: [OLAllocator defaultAllocator]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected the default allocator"];
    }
    if ([b2 autoreleasesResults])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO but got YES"];
    }
    if (![[b2 function] isMemberOfClass: [OLDivides class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLDivides, but got %s", [[b2 function] class]->name];
    }
    if ([[b2 argument] intValue] != 7)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 7 but got %i", [[b2 argument] intValue]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    [b2 setAutoreleasesResults: YES];
    archData = [NSKeyedArchiver archivedDataWithRootObject: b2];
    b2 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[b2 allocator] isEqual: [OLAllocator defaultAllocator]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected the default allocator"];
    }
    if (![b2 autoreleasesResults])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES but got NO"];
    }
    if (![[b2 function] isMemberOfClass: [OLDivides class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLDivides, but got %s", [[b2 function] class]->name];
    }
    if ([[b2 argument] intValue] != 7)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 7 but got %i", [[b2 argument] intValue]];
    }
#endif
}

- (void) testBoolBinderCoding
{
    NSArchiver* archiver;
    OLLess* less;
    NSMutableData* data;
    NSData* archData;
    NSNumber* arg;
    OLBoolBinder1st* b1;
    OLBoolBinder2nd* b2;

    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    less = [[OLLess alloc] init];
    arg = [[NSNumber alloc] initWithInt: 5];
    b1 = [[OLBoolBinder1st alloc] initWithBoolFunction: less andLeftArg: arg];
    [less release];
    [arg release];
    [archiver encodeRootObject: b1];
    [archiver release];
    [b1 release];
    b1 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[b1 function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[b1 function] class]->name];
    }
    if ([[b1 argument] intValue] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5 but got %i", [[b1 argument] intValue]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: b1];
    b1 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[b1 function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[b1 function] class]->name];
    }
    if ([[b1 argument] intValue] != 5)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 5 but got %i", [[b1 argument] intValue]];
    }
#endif
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    less = [[OLLess alloc] init];
    arg = [[NSNumber alloc] initWithInt: 7];
    b2 = [[OLBoolBinder2nd alloc] initWithBoolFunction: less andRightArg: arg];
    [less release];
    [arg release];
    [archiver encodeRootObject: b2];
    [archiver release];
    [b2 release];
    b2 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[b2 function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[b2 function] class]->name];
    }
    if ([[b2 argument] intValue] != 7)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 7 but got %i", [[b2 argument] intValue]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: b2];
    b2 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[b2 function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[b2 function] class]->name];
    }
    if ([[b2 argument] intValue] != 7)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 7 but got %i", [[b2 argument] intValue]];
    }
#endif
}

- (void) testCoding
{
    NSArchiver* archiver;
    OLLess* less;
    OLGreater* greater;
    NSMutableData* data;
    NSData* archData;

    less = [[OLLess alloc] init];
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    [archiver encodeRootObject: less];
    [archiver release];
    [less release];
    less = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![less isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [less class]->name];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: less];
    less = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![less isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [less class]->name];
    }
#endif
    greater = [[OLGreater alloc] init];
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    [archiver encodeRootObject: greater];
    [archiver release];
    [greater release];
    greater = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![greater isMemberOfClass: [OLGreater class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLGreater, but got %s", [greater class]->name];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: greater];
    greater = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![greater isMemberOfClass: [OLGreater class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLGreater, but got %s", [greater class]->name];
    }
#endif
}

- (void) testComparison
{
    NSNumber* n0 = [NSNumber numberWithInt: 0];
    NSNumber* n02 = [NSNumber numberWithInt: 0];
    NSNumber* n1 = [NSNumber numberWithInt: 1];
    NSNumber* n12 = [NSNumber numberWithInt: 1];
    NSNumber* n2 = [NSNumber numberWithInt: 2];
    OLEqualTo* equal;
    OLGreater* greater;
    OLGreaterEqual* greaterEqual;
    OLLess* less;
    OLLessEqual* lessEqual;
    OLNotEqualTo* notEqual;
    BOOL rc;

    equal = [[OLEqualTo alloc] init];
    rc = [equal performBinaryFunctionWithArg: n0 andArg: n02];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [equal performBinaryFunctionWithArg: n0 andArg: n1];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [equal release];
    greater = [[OLGreater alloc] init];
    rc = [greater performBinaryFunctionWithArg: n1 andArg: n0];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [greater performBinaryFunctionWithArg: n1 andArg: n12];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    rc = [greater performBinaryFunctionWithArg: n1 andArg: n2];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [greater release];
    greaterEqual = [[OLGreaterEqual alloc] init];
    rc = [greaterEqual performBinaryFunctionWithArg: n1 andArg: n0];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [greaterEqual performBinaryFunctionWithArg: n1 andArg: n12];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [greaterEqual performBinaryFunctionWithArg: n1 andArg: n2];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [greaterEqual release];
    less = [[OLLess alloc] init];
    rc = [less performBinaryFunctionWithArg: n1 andArg: n2];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [less performBinaryFunctionWithArg: n1 andArg: n12];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    rc = [less performBinaryFunctionWithArg: n1 andArg: n0];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [less release];
    lessEqual = [[OLLessEqual alloc] init];
    rc = [lessEqual performBinaryFunctionWithArg: n1 andArg: n2];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [lessEqual performBinaryFunctionWithArg: n1 andArg: n12];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [lessEqual performBinaryFunctionWithArg: n1 andArg: n0];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [lessEqual release];
    notEqual = [[OLNotEqualTo alloc] init];
    rc = [notEqual performBinaryFunctionWithArg: n0 andArg: n02];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    rc = [notEqual performBinaryFunctionWithArg: n0 andArg: n1];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    [notEqual release];
}

- (void) testConvenienceAllocators
{
    OLFunctorType types[] =
    {
        OLFunctorType_Divides,
        OLFunctorType_EqualTo,
        OLFunctorType_Greater,
        OLFunctorType_GreaterEqual,
        OLFunctorType_Less,
        OLFunctorType_LessEqual,
        OLFunctorType_LogicalAnd,
        OLFunctorType_LogicalNot,
        OLFunctorType_LogicalOr,
        OLFunctorType_Minus,
        OLFunctorType_Modulus,
        OLFunctorType_Multiplies,
        OLFunctorType_Negate,
        OLFunctorType_NotEqualTo,
        OLFunctorType_Plus
    };
    Class classes[] =
    {
        [OLDivides class],
        [OLEqualTo class],
        [OLGreater class],
        [OLGreaterEqual class],
        [OLLess class],
        [OLLessEqual class],
        [OLLogicalAnd class],
        [OLLogicalNot class],
        [OLLogicalOr class],
        [OLMinus class],
        [OLModulus class],
        [OLMultiplies class],
        [OLNegate class],
        [OLNotEqualTo class],
        [OLPlus class]
    };
    id functor;
    int i;
    OLBinaryNegate* bneg;
    OLGreater* greater;
    NSNumber* left;
    NSNumber* right;
    OLBinder1st* b1;
    OLBinder2nd* b2;
    OLBoolBinder1st* bb1;
    OLBoolBinder2nd* bb2;
    OLPlus* plus;
    id result;
    OLVector* v;
    OLMemFun* memf;
    OLMemFun1* memf1;
    OLLogicalNot* not;
    OLUnaryNegate* neg;

    for (i = 0; i < 15; i++)
    {
        functor = [OLFunctor functorOfType: types[i]];
        if (![functor isMemberOfClass: classes[i]])
        {
            [self errInFile: __FILE__ line: __LINE__
                format: "Expected \"%s\", but got \"%s\"",
                classes[i]->name, [functor class]->name];
        }
    }
    greater = [[OLGreater alloc] init];
    bneg = [OLBinaryNegate functorWithBinaryFunction: greater];
    if (![bneg isMemberOfClass: [OLBinaryNegate class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLBinaryNegate class]->name, [bneg class]->name];
    }
    left = [[NSNumber alloc] initWithInt: 4];
    right = [[NSNumber alloc] initWithInt: 5];
    if (![bneg performBinaryFunctionWithArg: left andArg: right])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    plus = [[OLPlus alloc] init];
    b1 = [OLBinder1st functorWithFunction: plus andLeftArg: left];
    if (![b1 isMemberOfClass: [OLBinder1st class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLBinder1st class]->name, [b1 class]->name];
    }
    result = [b1 performUnaryFunctionWithArg: right];
    if ([result intValue] != 9)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 9, but got %i", [result intValue]];
    }
    b2 = [OLBinder2nd functorWithFunction: plus andRightArg: right];
    [plus release];
    if (![b2 isMemberOfClass: [OLBinder2nd class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLBinder2nd class]->name, [b2 class]->name];
    }
    result = [b2 performUnaryFunctionWithArg: left];
    if ([result intValue] != 9)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 9, but got %i", [result intValue]];
    }
    bb1 = [OLBoolBinder1st functorWithBoolFunction: greater andLeftArg: left];
    if (![bb1 isMemberOfClass: [OLBoolBinder1st class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLBoolBinder1st class]->name, [bb1 class]->name];
    }
    if ([bb1 performUnaryFunctionWithArg: right])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    bb2 = [OLBoolBinder2nd functorWithBoolFunction: greater andRightArg: right];
    if (![bb2 isMemberOfClass: [OLBoolBinder2nd class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLBoolBinder2nd class]->name, [bb2 class]->name];
    }
    if ([bb2 performUnaryFunctionWithArg: left])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    v = [[OLVector alloc] init];
    [v pushBack: greater];
    [v pushBack: left];
    [v pushBack: right];
    memf = [OLMemFun functorWithSelector: @selector(clear)];
    if (![memf isMemberOfClass: [OLMemFun class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLMemFun class]->name, [memf class]->name];
    }
    [memf performUnaryFunctionWithArg: v];
    if (![v empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "The vector should be empty"];
    }
    memf1 = [OLMemFun1 functorWithSelector: @selector(pushBack:)];
    if (![memf1 isMemberOfClass: [OLMemFun1 class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLMemFun1 class]->name, [memf1 class]->name];
    }
    [memf1 performBinaryFunctionWithArg: v andArg: greater];
    if ([v size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %u", [v size]];
    }
    [greater release];
    [left release];
    [right release];
    [v release];
    not = [[OLLogicalNot alloc] init];
    neg = [OLUnaryNegate functorWithUnaryFunction: not];
    [not release];
    if (![neg isMemberOfClass: [OLUnaryNegate class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"%s\", but got \"%s\"",
            [OLUnaryNegate class]->name, [neg class]->name];
    }
    left = [[NSNumber alloc] initWithBool: YES];
    if (![neg performUnaryFunctionWithArg: left])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    [left release];
}

- (void) testLogical
{
    NSNumber* b0 = [NSNumber numberWithBool: NO];
    NSNumber* b01 = [NSNumber numberWithBool: NO];
    NSNumber* b1 = [NSNumber numberWithBool: YES];
    NSNumber* b11 = [NSNumber numberWithBool: YES];
    OLLogicalAnd* and;
    OLLogicalNot* not;
    OLLogicalOr* or;
    BOOL rc;

    and = [[OLLogicalAnd alloc] init];
    rc = [and performBinaryFunctionWithArg: b1 andArg: b11];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [and performBinaryFunctionWithArg: b0 andArg: b1];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [and release];
    not = [[OLLogicalNot alloc] init];
    rc = [not performUnaryFunctionWithArg: b0];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [not performUnaryFunctionWithArg: b1];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [not release];
    or = [[OLLogicalOr alloc] init];
    rc = [or performBinaryFunctionWithArg: b0 andArg: b1];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [or performBinaryFunctionWithArg: b1 andArg: b11];
    if (!rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    rc = [or performBinaryFunctionWithArg: b0 andArg: b01];
    if (rc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    [or release];
}

- (void) testMemFun
{
    NSString* strs[] = { @"one", @"two", @"three" };
    OLVector* v;
    OLMemFun* memf;
    OLMemFun1* memf1;
    int i;

    v = [[OLVector alloc] init];
    for (i = 0; i < 3; i++)
        [v pushBack: strs[i]];
    memf = [[OLMemFun alloc] initWithSelector: @selector(clear)];
    [memf performUnaryFunctionWithArg: v];
    [memf release];
    if (![v empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [v size]];
    }
    memf1 = [[OLMemFun1 alloc] initWithSelector: @selector(pushBack:)];
    [memf1 performBinaryFunctionWithArg: v andArg: strs[2]];
    [memf1 release];
    if ([v size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %i", [v size]];
    }
    if (![[v front] isEqual: strs[2]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"three\", but got %s", [[v front] cString]];
    }
    [v release];
}

- (void) testMemFunCoding
{
    NSString* strs[] = { @"one", @"two", @"three" };
    NSArchiver* archiver;
    NSMutableData* data;
    NSData* archData;
    OLMemFun* memf;
    OLMemFun1* memf1;
    OLVector* v;
    int i;

    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    memf = [[OLMemFun alloc] initWithSelector: @selector(clear)];
    [archiver encodeRootObject: memf];
    [memf release];
    [archiver release];
    memf = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    v = [[OLVector alloc] init];
    for (i = 0; i < 3; i++)
        [v pushBack: strs[i]];
    [memf performUnaryFunctionWithArg: v];
    if (![v empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [v size]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: memf];
    memf = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    for (i = 0; i < 3; i++)
        [v pushBack: strs[i]];
    [memf performUnaryFunctionWithArg: v];
    if (![v empty])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 0, but got %i", [v size]];
    }
#endif
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    memf1 = [[OLMemFun1 alloc] initWithSelector: @selector(pushBack:)];
    [archiver encodeRootObject: memf1];
    [memf1 release];
    [archiver release];
    memf1 = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    [memf1 performBinaryFunctionWithArg: v andArg: strs[0]];
    if ([v size] != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %i", [v size]];
    }
    if (![[v front] isEqual: strs[0]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"one\", but got %s", [[v front] cString]];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: memf1];
    memf1 = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    [memf1 performBinaryFunctionWithArg: v andArg: strs[1]];
    if ([v size] != 2)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 2, but got %i", [v size]];
    }
    if (![[v front] isEqual: strs[0]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"one\", but got %s", [[v front] cString]];
    }
    if (![[v back] isEqual: strs[1]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected \"two\", but got %s", [[v back] cString]];
    }
#endif
    [v release];
}

- (void) testNegate
{
    NSNumber* nn1 = [NSNumber numberWithInt: -1];
    NSNumber* n1 = [NSNumber numberWithInt: 1];
    NSNumber* b0 = [NSNumber numberWithBool: NO];
    NSNumber* b01 = [NSNumber numberWithBool: NO];
    NSNumber* b1 = [NSNumber numberWithBool: YES];
    NSNumber* b11 = [NSNumber numberWithBool: YES];
    OLNegate* negate;
    OLLogicalNot* not;
    OLUnaryNegate* uneg;
    OLLogicalAnd* and;
    OLBinaryNegate* bneg;
    int rc;
    BOOL bc;

    negate = [[OLNegate alloc] init];
    rc = [[negate performUnaryFunctionWithArg: nn1] intValue];
    if (rc != 1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected 1, but got %i", rc];
    }
    rc = [[negate performUnaryFunctionWithArg: n1] intValue];
    if (rc != -1)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected -1, but got %i", rc];
    }
    [negate release];
    not = [[OLLogicalNot alloc] init];
    uneg = [[OLUnaryNegate alloc] initWithUnaryFunction: not];
    [not release];
    bc = [uneg performUnaryFunctionWithArg: b0];
    if (bc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    bc = [uneg performUnaryFunctionWithArg: b1];
    if (!bc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    [uneg release];
    and = [[OLLogicalAnd alloc] init];
    bneg = [[OLBinaryNegate alloc] initWithBinaryFunction: and];
    [and release];
    bc = [bneg performBinaryFunctionWithArg: b1 andArg: b11];
    if (bc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected NO, but got YES"];
    }
    bc = [bneg performBinaryFunctionWithArg: b0 andArg: b1];
    if (!bc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    bc = [bneg performBinaryFunctionWithArg: b0 andArg: b01];
    if (!bc)
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected YES, but got NO"];
    }
    [bneg release];
}

- (void) testNegateCoding
{
    NSArchiver* archiver;
    NSMutableData* data;
    NSData* archData;
    OLBinaryNegate* bn;
    OLUnaryNegate* un;
    OLLess* func;
    OLLogicalNot* ufunc;

    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    func = [[OLLess alloc] init];
    bn = [[OLBinaryNegate alloc] initWithBinaryFunction: func];
    [func release];
    [archiver encodeRootObject: bn];
    [archiver release];
    [bn release];
    bn = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[bn function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[bn function] class]->name];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: bn];
    bn = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[bn function] isMemberOfClass: [OLLess class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLess, but got %s", [[bn function] class]->name];
    }
#endif
    data = [[NSMutableData alloc] initWithCapacity: 50];
    archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
    ufunc = [[OLLogicalNot alloc] init];
    un = [[OLUnaryNegate alloc] initWithUnaryFunction: ufunc];
    [ufunc release];
    [archiver encodeRootObject: un];
    [archiver release];
    [un release];
    un = [NSUnarchiver unarchiveObjectWithData: data];
    [data release];
    if (![[un function] isMemberOfClass: [OLLogicalNot class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLogicalNot, but got %s", [[un function] class]->name];
    }
#if defined(HAVE_KEYED_ARCHIVES)
    archData = [NSKeyedArchiver archivedDataWithRootObject: un];
    un = [NSKeyedUnarchiver unarchiveObjectWithData: archData];
    if (![[un function] isMemberOfClass: [OLLogicalNot class]])
    {
        [self errInFile: __FILE__ line: __LINE__
            format: "Expected OLLogicalNot, but got %s", [[un function] class]->name];
    }
#endif
}

@end
